diff --git a/.asf.yaml b/.asf.yaml index b7043cec822..be818fda4d3 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -51,13 +51,14 @@ github: collaborators: - acs-robot - - kiranchavala - - rajujith - - GaOrtiga - - SadiJr - - winterhazel - gpordeus - hsato03 + - FelipeM525 + - lucas-a-martins + - nicoschmdt + - abh1sar + - rosi-shapeblue + - sudo87 protected_branches: ~ diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 00000000000..10f05a1adfd --- /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.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 00000000000..90de1169886 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -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/linters/.flake8 b/.github/linters/.flake8 index f250719ca19..3364ad14f29 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 00000000000..531c26ace44 --- /dev/null +++ b/.github/linters/.markdown-lint.yml @@ -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. + +# MD001/heading-increment Heading levels should only increment by one level at a time +MD001: false + +# MD003/heading-style Heading style +MD003: false + +# MD004/ul-style Unordered list style +MD004: false + +# 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/codespell.txt b/.github/linters/codespell.txt new file mode 100644 index 00000000000..37b3e6de1cb --- /dev/null +++ b/.github/linters/codespell.txt @@ -0,0 +1,526 @@ +accouns +acheived +acount +actuall +acuiring +acumulate +addreess +addtion +adminstrator +afer +afrer +afterall +againt +ags +aktive +algoritm +allo +alloacate +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 +cant +capabilites +capablity +capcity +carrefully +cavaet +chaing +checkd +childs +choosen +chould +clenup +cliente +clinet +cluser +cna +collison +comman +commited +comparision +comparisions +complient +concious +conectix +confg +configruation +configuable +conneciton +connexion +constrait +constraits +containg +contex +continuesly +contro +controler +controles +controll +convienient +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 +disale +disbale +discrepency +disover +dissapper +dissassociated +divice +doesn' +doesnot +doesnt +dont' +doubleclick +dows +eanbled +earch +ect +elemnt +eles +elments +emmited +enble +encryted +enebled +enmpty +entires +enviornment +environmnet +equivalant +erro +erronous +everthing +everytime +excetion +excption +excute +execept +execption +execut +executeable +exeeded +exisitng +exisits +existin +existsing +exitting +expcted +expection +explaination +explicitely +faield +faild +failes +falied +fasion +feild +filenname +fillled +findout +fisrt +fo +folowing +fowarding +frist +fro +frontent +fuctionality +genarate +generallly +gernerate +get's +gloabal +gorry +gracefull +gradiant +handeling +hanling +happend +hasing +hasnt +hda +hostanme +hould +hsould +hte +identifers +identifyer +identifyers +igoring +immediatley +implememented +implementor +implementors +implemnt +implict +implmeneted +implmentation +incase +includeing +incosistency +indecates +indien +infor +informations +informaton +infrastrcuture +ingore +inital +initalize +initator +initilization +inspite +instace +instal +instnace +intefaces +intepret +intereface +interfer +interpretted +intialize +intializes +intializing +invlaid +invokation +isnt +ist +klunky +lable +leve +lief +limite +linke +listner +lokal +lokales +maintainence +maintenace +maintenence +mamagement +mambers +manaully +manuel +maxium +mehtod +mergable +mesage +messge +metatdata +milisecond +minumum +mis +modifers +mor +mot +mulitply +multipl +multple +mutliple +nast +nd +neccessary +necesary +netowrk +nin +nodel +nome +noone +nowe +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 +readabilty +readd +reccuring +recevied +recieved +recursivelly +redunant +refference +releease +relese +remaning +remore +remvoing +renabling +repeatly +reponse +reqest +reqiured +requieres +requried +reserv +reserverd +reseted +reseting +resorce +responser +resposne +resturns +retreive +retreiving +retrive +retrived +retriving +retrun +retuned +returing +re-use +rever +rocessor +runing +runnign +sate +scalled +scipt +scirpt +scrip +seconadry +seconday +seesion +sepcified +sepcify +seprated +ser +servies +seting +settig +sevices +shoul +shoule +sie +signle +simplier +singature +skiping +snaphsot +snpashot +specied +specifed +specifiy +splitted +spped +standy +statics +stickyness +stil +stip +storeage +strat +streched +strutural +succesfull +successfull +suceessful +suces +sucessfully +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 +unknow +unkonw +unkown +unneccessary +unparseable +unrecoginized +unsupport +unxpected +updat +uptodate +usera +usign +usin +utlization +vaidate +valiate +valule +valus +varibles +verfy +verfying +verifing +virutal +visable +wakup +wil +wit +wll +wth diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1c6c90a6183..fd3c8f8ac67 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 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 c4196f06d4b..8c61ca8e4dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: 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", @@ -221,7 +231,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.10' architecture: 'x64' - name: Install Build Dependencies @@ -231,7 +241,7 @@ jobs: - name: Install Python dependencies run: | - python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycrypto mock flask netaddr pylint pycodestyle six astroid + python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycryptodome mock flask netaddr pylint pycodestyle six astroid pynose - name: Install jacoco dependencies run: | @@ -279,7 +289,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 & @@ -313,10 +323,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 f7b28fdeecc..c77783746ca 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -36,11 +36,11 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK11 + - name: Set up JDK 17 uses: actions/setup-java@v4 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/linter.yml b/.github/workflows/linter.yml index 784df0cf03c..b6c814a36f4 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -39,7 +39,7 @@ jobs: pip install pre-commit - name: Set PY run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pre-commit key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} diff --git a/.github/workflows/main-sonar-check.yml b/.github/workflows/main-sonar-check.yml index 66bb1093e04..dccd7174e54 100644 --- a/.github/workflows/main-sonar-check.yml +++ b/.github/workflows/main-sonar-check.yml @@ -36,25 +36,25 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK11 + - name: Set up JDK17 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'maven' - name: Cache SonarCloud packages - uses: actions/cache@v3 + uses: actions/cache@v4 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@v4 with: path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml', '*/pom.xml', '*/*/pom.xml', '*/*/*/pom.xml') }} restore-keys: | ${{ runner.os }}-m2 diff --git a/.github/workflows/rat.yml b/.github/workflows/rat.yml index b8f83de8194..52ce343841b 100644 --- a/.github/workflows/rat.yml +++ b/.github/workflows/rat.yml @@ -31,10 +31,10 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 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 2ebcf1fb2db..d31f55980a8 100644 --- a/.github/workflows/sonar-check.yml +++ b/.github/workflows/sonar-check.yml @@ -38,25 +38,25 @@ jobs: ref: "refs/pull/${{ github.event.number }}/merge" fetch-depth: 0 - - name: Set up JDK11 + - name: Set up JDK17 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'maven' - name: Cache SonarCloud packages - uses: actions/cache@v3 + uses: actions/cache@v4 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@v4 with: path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml', '*/pom.xml', '*/*/pom.xml', '*/*/*/pom.xml') }} restore-keys: | ${{ runner.os }}-m2 diff --git a/.github/workflows/ui.yml b/.github/workflows/ui.yml index 4d89977adf9..88d4a70e4c2 100644 --- a/.github/workflows/ui.yml +++ b/.github/workflows/ui.yml @@ -36,7 +36,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v3 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 e638a09ae21..abaef83e455 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/ -*.jar -*.war -*.mar *.iso +*.jar +*.log +*.log.* +*.mar +*.orig +*.patch +*.prefs +*.pyc +*.qcow2 +*.raw +*.swp *.tar.gz *.tgz *.vscode -*.css.map +*.war +.DS_Store +.checkstyle +.classpath +.idea +.lock-wscript +.metadata +.pmd +.pmdruleset.xml +.project +.pydevproject +.reviewboardrc +.settings.xml +.settings/ +.vscode +.waf-* +Gemfile.lock +build/build.number +build.number +build-indep-stamp +cloud.log.*.* +cloud-*.tar.bz2 +configure-stamp +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 +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 +systemvm/.pydevproject +target/ +target-eclipse +test/.pydevprojec +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 +waf-* # this ignores _all files starting with '.'. Don't do that! #.* -target-eclipse !.gitignore -.classpath -.settings.xml -.settings/ -db.properties.override -replace.properties.override -tools/marvin/marvin/cloudstackAPI/* -docs/tmp -docs/publish -docs/runbook/tmp -docs/runbook/publish -.project -Gemfile.lock -debian/tmp -debian/files -debian/cloudstack-*/* -debian/*.substvars -debian/*.debhelper -replace.properties.tmp -build-indep-stamp -configure-stamp -*_flymake.js -engine/storage/integration-test/test-output -tools/apidoc/log/ -plugins/network-elements/juniper-contrail/logs/ -scripts/vm/hypervisor/xenserver/vhd-util -*.orig -tools/appliance/box/ -.reviewboardrc -.checkstyle -.pmd -.pmdruleset.xml -.pydevproject -systemvm/.pydevproject -test/.pydevprojec -plugins/hypervisors/kvm/.pydevproject -scripts/.pydevproject -*.qcow2 -*.raw -venv -node_modules -.vscode diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0848a124794..32b70145e59 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,18 +15,24 @@ # specific language governing permissions and limitations # under the License. --- -default_stages: [commit, push] +default_stages: [pre-commit, pre-push] default_language_version: # force all unspecified Python hooks to run python3 python: python3 -minimum_pre_commit_version: "2.17.0" +minimum_pre_commit_version: "3.2.0" repos: - repo: meta hooks: - id: identity - id: check-hooks-apply + - repo: https://github.com/gitleaks/gitleaks + rev: v8.27.2 + hooks: + - id: gitleaks + name: run gitleaks + description: detect hardcoded secrets - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v5.0.0 hooks: #- id: check-added-large-files - id: check-case-conflict @@ -36,6 +42,8 @@ repos: - id: check-vcs-permalinks #- id: check-yaml - id: destroyed-symlinks + - id: detect-aws-credentials + args: [--allow-missing-credentials] - id: detect-private-key exclude: > (?x) @@ -52,12 +60,24 @@ repos: ^systemvm/agent/certs/realhostip\.key$ - id: end-of-file-fixer exclude: \.vhd$ - #- id: fix-byte-order-marker + - id: fix-byte-order-marker + - id: forbid-submodules - id: mixed-line-ending - exclude: \.(cs|xml)$ - # - id: trailing-whitespace + exclude: \.cs$ + - id: trailing-whitespace + files: \.(bat|cfg|cs|css|gitignore|header|in|install|java|md|properties|py|rb|sh|sql|txt|vue|xml|xsl|yaml|yml)$ + args: [--markdown-linebreak-ext=md] + exclude: ^services/console-proxy/rdpconsole/src/test/doc/freerdp-debug-log\.txt$ + - repo: https://github.com/codespell-project/codespell + rev: v2.2.6 + 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$ - repo: https://github.com/pycqa/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 args: [--config, .github/linters/.flake8] @@ -72,3 +92,12 @@ repos: ^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)$ diff --git a/.python-version b/.python-version index d70c8f8d89f..c8cfe395918 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.6 +3.10 diff --git a/CHANGES.md b/CHANGES.md index ef498f8edf0..8fea4f3a355 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 @@ -657,11 +655,11 @@ 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 cdfbfe77b7e..17ff4badc97 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,44 +3,44 @@ Contributing to Apache CloudStack (ACS) Summary ------- -This document covers how to contribute to the ACS project. ACS uses GitHub PRs to manage code contributions. -These instructions assume you have a GitHub.com account, so if you don't have one you will have to create one. Your proposed code changes will be published to your own fork of the ACS project and you will submit a Pull Request for your changes to be added. +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. -_Lets get started!!!_ +_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. +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 ----------------------- -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 +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,11 +51,9 @@ $ git fetch upstream $ git rebase upstream/main ``` - 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. It is best practice to create a new branch each time you want to contribute to the project and only track the changes for that pull request in this branch. @@ -70,7 +68,6 @@ $ 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` ------------------------------------------------------------ @@ -92,13 +89,12 @@ $ 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 ----------------------------------------------------- -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 +103,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,7 +114,6 @@ 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 ------------------------------------------- @@ -136,4 +131,4 @@ $ 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 +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 6586e4e57fc..efb42678b0a 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -18,25 +18,26 @@ Install tools and dependencies used for development: # yum -y install git java-17-openjdk java-17-openjdk-devel \ mysql mysql-server mkisofs git gcc python MySQL-python openssh-clients wget -Set up Maven (3.6.0): +Set up Maven (3.9.10): - # wget http://www.us.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz - # tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /usr/local + # wget https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz + # sudo tar -zxvf apache-maven-3.9.10-bin.tar.gz -C /usr/local # cd /usr/local - # ln -s apache-maven-3.6.3 maven + # sudo ln -s apache-maven-3.9.10 maven # echo export M2_HOME=/usr/local/maven >> ~/.bashrc # or .zshrc or .profile # echo export PATH=/usr/local/maven/bin:${PATH} >> ~/.bashrc # or .zshrc or .profile # source ~/.bashrc -Setup up NodeJS (LTS): +Setup up Node.js 16: - # curl -sL https://rpm.nodesource.com/setup_12.x | sudo bash - + # curl -sL https://rpm.nodesource.com/setup_16.x | sudo -E bash - # sudo yum install nodejs # sudo npm install -g @vue/cli npm-check-updates Start the MySQL service: $ service mysqld start + $ mysql_secure_installation ### Using jenv and/or pyenv for Version Management @@ -78,7 +79,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 +87,33 @@ Start the management server: If this works, you've successfully setup a single server Apache CloudStack installation. -Open the following URL on your browser to access the Management Server UI: - - http://localhost:8080/client/ +To access the Management Server UI, follow the following procedure: The default credentials are; user: admin, password: password and the domain field should be left blank which is defaulted to the ROOT domain. +## To bring up CloudStack UI + +Move to UI Directory + + $ cd /path/to/cloudstack/ui + +To install dependencies. + + $ npm install + +To build the project. + + $ npm run build + +For Development Mode. + + $ npm start + +Make sure to set `CS_URL=http://localhost:8080` on the `.env.local` file on UI. + +You should be able to run the management server on http://localhost:5050 + ## Building with non-redistributable plugins CloudStack supports several plugins that depend on libraries with distribution restrictions. diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 85c1ac9ec6a..4a48a4e7591 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 @@ -52,5 +51,4 @@ This PR... - diff --git a/README.md b/README.md index e193913612f..28d7d7396f4 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,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 +160,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 53c6c2f56aa..9927c22eebc 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,6 +20,19 @@ 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 diff --git a/agent/bindir/cloud-ssh.in b/agent/bindir/cloud-ssh.in index e4b3c141a97..a5ea975c2f3 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 e17944d8353..4cc6ed7a1d2 100755 --- a/agent/bindir/libvirtqemuhook.in +++ b/agent/bindir/libvirtqemuhook.in @@ -20,6 +20,19 @@ import sys import os import subprocess from threading import Timer + +# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- +# ---- We do this so cloud_utils can be looked up in the following order: +# ---- 1) Sources directory +# ---- 2) waf configured PYTHONDIR +# ---- 3) System Python path +for pythonpath in ( + "@PYTHONDIR@", + os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), + ): + if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) +# ---- End snippet of code ---- + from xml.dom.minidom import parse from cloudutils.configFileOps import configFileOps from cloudutils.networkConfig import networkConfig diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties index e600e8f8f20..cd31b0db56d 100644 --- a/agent/conf/agent.properties +++ b/agent/conf/agent.properties @@ -209,7 +209,7 @@ 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= @@ -286,6 +286,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. @@ -430,3 +431,22 @@ iscsi.session.cleanup.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 diff --git a/agent/conf/cloudstack-agent.logrotate.in b/agent/conf/cloudstack-agent.logrotate.in index 2b3dc87f253..9f22b4bab86 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/environment.properties.in b/agent/conf/environment.properties.in index 514161a13fc..b6cc5bbd987 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 44ebd1358af..84957edca03 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/pom.xml b/agent/pom.xml index 9caa6d992c8..76c37cd87be 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.21.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 56732dad993..a1834b6827b 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.net.UnknownHostException; 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,10 +58,10 @@ import org.apache.cloudstack.managed.context.ManagedContextTimerTask; import org.apache.cloudstack.utils.security.KeyStoreUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.ObjectUtils; -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; @@ -67,6 +70,9 @@ import com.cloud.agent.api.Command; 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; @@ -76,9 +82,12 @@ import com.cloud.agent.transport.Request; 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; @@ -90,7 +99,6 @@ 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 @@ -114,7 +122,7 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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; @@ -125,128 +133,162 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } } - 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 + private String keystoreSetupSetupPath; + private String keystoreCertImportScriptPath; + + private String hostname; + + 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(); + } + + protected String getAgentName() { + return (serverResource != null && serverResource.isAppendAgentNameToLogs() && + StringUtils.isNotBlank(serverResource.getName())) ? + serverResource.getName() : + "Agent"; + } + + 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")); + } + + /** + * 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) { - _shell = shell; - _link = null; - - _connection = new NioClient("Agent", _shell.getNextHost(), _shell.getPort(), _shell.getWorkers(), this); - - 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")); + 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 { - _shell = shell; - _resource = resource; - _link = null; - + 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 {}", ObjectUtils.defaultIfNull(_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 = {} : type = {} : zone = {} : pod = {} : workers = {} : host = {} : port = {}", ObjectUtils.defaultIfNull(_id, "new"), getResourceName(), - _shell.getZone(), _shell.getPod(), _shell.getWorkers(), host, _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(); } /** @@ -255,71 +297,64 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater * 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("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("Agent", host, _shell.getPort(), _shell.getWorkers(), this); - logger.info("Connecting to host:{}", host); + 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 = " + ObjectUtils.defaultIfNull(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()); @@ -332,108 +367,148 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } 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.debug("Set agent id {}", id); - _id = id; - _shell.setPersistentProperty(getResourceName(), "id", Long.toString(id)); + this.id = id; + shell.setPersistentProperty(getResourceName(), "id", Long.toString(id)); } - private synchronized void scheduleServicesRestartTask() { - if (certTimer != null) { - certTimer.cancel(); - certTimer.purge(); + public String getUuid() { + return _uuid; + } + + 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={}ms", checkInterval); - 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 lb algorithm '{}' and host.lb.interval={} ms", lbAlgorithm, 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) { - logger.debug("Adding task with request: {} to watch list", request.toString()); - - 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()); } } protected void cancelTasks() { - synchronized (_watchList) { - for (final WatchTask task : _watchList) { - task.cancel(); - } - logger.debug("Clearing {} tasks of watch list", _watchList.size()); - _watchList.clear(); + for (final ScheduledFuture task : watchList) { + task.cancel(true); } + logger.debug("Clearing watch list: {}", () -> watchList.size()); + watchList.clear(); } /** @@ -444,27 +519,52 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater * 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()); logger.debug("Sending Startup: {}", request.toString()); @@ -472,31 +572,37 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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()); @@ -504,6 +610,13 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater startup.setGuid(getResourceGuid()); startup.setResourceName(getResourceName()); startup.setVersion(getVersion()); + startup.setArch(getAgentArch()); + } + + protected String getAgentArch() { + final Script command = new Script("/usr/bin/arch", 500, logger); + final OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); + return command.execute(parser); } @Override @@ -512,92 +625,104 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } protected void reconnect(final Link link) { - if (!_reconnectAllowed) { + reconnect(link, null, false); + } + + 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; } - synchronized (this) { - if (_startup != null) { - _startup.cancel(); - _startup = null; - } - } - - if (link != null) { - link.close(); - link.terminated(); - } - + 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: {}. Attempting reconnection while we still have {} commands in progress.", _shell.getConnectedHost(), _inProgress.get()); - - _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); } - - 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()); + 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) { @@ -628,7 +753,7 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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; @@ -637,10 +762,17 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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()); + 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) { @@ -648,12 +780,10 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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; } } @@ -665,20 +795,25 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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()); @@ -732,13 +867,13 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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,8 +917,8 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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); @@ -801,30 +936,85 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater return new SetupCertificateAnswer(true); } - private void processManagementServerList(final List msList, final String lbAlgorithm, final Long lbCheckInterval) { + private void processManagementServerList(final List msList, final List avoidMsList, final String lbAlgorithm, final Long lbCheckInterval, final boolean triggerHostLB) { if (CollectionUtils.isNotEmpty(msList) && StringUtils.isNotEmpty(lbAlgorithm)) { try { final String newMSHosts = String.format("%s%s%s", com.cloud.utils.StringUtils.toCSVList(msList), IAgentShell.hostLbAlgorithmSeparator, lbAlgorithm); - _shell.setPersistentProperty(null, "host", newMSHosts); - _shell.setHosts(newMSHosts); - _shell.resetHostCounter(); + 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(); logger.debug("Received response: {}", response.toString()); @@ -832,17 +1022,24 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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) { @@ -853,35 +1050,49 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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 = {}", getId()); + logger.info("Ready command is processed for agent [id: {}, uuid: {}, name: {}]", getId(), getUuid(), getName()); + } + + 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 {}. Won't reconnect to mgt server, as connection is still alive", _pingInterval * _shell.getPingRetries()); + 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()); 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) { @@ -891,11 +1102,14 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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); @@ -912,35 +1126,29 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } } - 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); @@ -951,7 +1159,6 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater logger.warn("sendRequest is interrupted, exit waiting"); } } - return listener.getAnswer(); } finally { unregisterControlListener(listener); @@ -966,9 +1173,9 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } 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()); throw new AgentControlChannelException("Unable to post agent control request due to " + e.getMessage()); @@ -1020,26 +1227,26 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } } - 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() { + 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"); @@ -1047,35 +1254,32 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } } - 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) { - 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); } } } @@ -1106,9 +1310,10 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater @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 { @@ -1119,7 +1324,7 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } 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 "); @@ -1127,8 +1332,15 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater logger.error("Error parsing task", e); } } else if (task.getType() == Task.Type.DISCONNECT) { + try { + // an issue has been found if reconnect immediately after disconnecting. please refer to https://github.com/apache/cloudstack/issues/8517 + // wait 5 seconds before reconnecting + Thread.sleep(5000); + } catch (InterruptedException e) { + } + shell.setConnectionTransfer(false); + logger.debug("Executing disconnect task - {}", () -> getLinkLog(task.getLink())); reconnect(task.getLink()); - return; } else if (task.getType() == Task.Type.OTHER) { processOtherTask(task); } @@ -1151,26 +1363,26 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater 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 @@ -1178,7 +1390,6 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater return; } 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); @@ -1193,35 +1404,34 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater @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(); - logger.trace("Running preferred host checker task, connected host={}, preferred host={}", connectedHost, preferredHost); - - 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; - logger.trace("Host: {} is not reachable", preferredHost); - - } - if (isHostUp && _link != null && _inProgress.get() == 0) { - logger.debug("Preferred host {} is found to be reachable, trying to reconnect", preferredHost); - - _shell.resetHostCounter(); - reconnect(_link); + 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; + } + 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 0699e00250b..4862e7e001e 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.Map; 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 class AgentShell implements IAgentShell, Daemon { if (_hostCounter >= hosts.length) { _hostCounter = 0; } - hostToConnect = hosts[_hostCounter % hosts.length]; + String hostToConnect = hosts[_hostCounter % hosts.length]; _hostCounter++; return hostToConnect; } @@ -140,11 +143,10 @@ public class AgentShell implements IAgentShell, Daemon { } @Override - public void updateConnectedHost() { - connectedHost = hostToConnect; + public void updateConnectedHost(String connectedHost) { + this.connectedHost = connectedHost; } - @Override public void resetHostCounter() { _hostCounter = 0; @@ -163,6 +165,16 @@ public class AgentShell implements IAgentShell, Daemon { } } + @Override + public void setAvoidHosts(List avoidHosts) { + _avoidHosts = avoidHosts; + } + + @Override + public List getAvoidHosts() { + return _avoidHosts; + } + @Override public String getPrivateIp() { return _privateIp; @@ -215,6 +227,14 @@ public class AgentShell implements IAgentShell, Daemon { _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"); @@ -406,7 +426,9 @@ public class AgentShell implements IAgentShell, Daemon { 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 { @@ -455,6 +477,11 @@ public class AgentShell implements IAgentShell, Daemon { agent.start(); } + @Override + public Integer getSslHandshakeTimeout() { + return AgentPropertiesFileHandler.getPropertyValue(AgentProperties.SSL_HANDSHAKE_TIMEOUT); + } + public synchronized int getNextAgentId() { return _nextAgentId++; } diff --git a/agent/src/main/java/com/cloud/agent/IAgentShell.java b/agent/src/main/java/com/cloud/agent/IAgentShell.java index 2dd08fffd45..9eefa6d2eee 100644 --- a/agent/src/main/java/com/cloud/agent/IAgentShell.java +++ b/agent/src/main/java/com/cloud/agent/IAgentShell.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.agent; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -63,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/properties/AgentProperties.java b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java index 24a09ae2ac1..847d1bb2396 100644 --- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java +++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java @@ -155,6 +155,14 @@ public class AgentProperties{ */ public static final Property CMDS_TIMEOUT = new Property<>("cmds.timeout", 7200); + /** + * The timeout (in seconds) for the snapshot merge operation, mainly used for classic volume snapshots and disk-only VM snapshots on file-based storage.
+ * This configuration is only considered if libvirt.events.enabled is also true.
+ * Data type: Integer.
+ * Default value: 259200 + */ + public static final Property QCOW2_DELTA_MERGE_TIMEOUT = new Property<>("qcow2.delta.merge.timeout", 60 * 60 * 72); + /** * This parameter sets the VM migration speed (in mbps). The default value is -1,
* which means that the agent will try to guess the speed of the guest network and consume all possible bandwidth.
@@ -213,6 +221,15 @@ public class AgentProperties{ */ public static final Property AGENT_HOOKS_LIBVIRT_VM_XML_TRANSFORMER_SCRIPT = new Property<>("agent.hooks.libvirt_vm_xml_transformer.script", "libvirt-vm-xml-transformer.groovy"); + /** + * This property is used with the agent.hooks.basedir property to define the Libvirt VM XML transformer shell script.
+ * The shell script is used to execute the Libvirt VM XML transformer script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-xml-transformer.sh + */ + public static final Property AGENT_HOOKS_LIBVIRT_VM_XML_TRANSFORMER_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_xml_transformer.shell_script", "libvirt-vm-xml-transformer.sh"); + /** * This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_xml_transformer.script properties to define the Libvirt VM XML transformer method.
* Libvirt XML transformer hook does XML-to-XML transformation.
@@ -233,6 +250,15 @@ public class AgentProperties{ */ public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_START_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_start.script", "libvirt-vm-state-change.groovy"); + /** + * This property is used with the agent.hooks.basedir property to define the Libvirt VM on start shell script.
+ * The shell script is used to execute the Libvirt VM on start script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-state-change.sh + */ + public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_START_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_start.shell_script", "libvirt-vm-state-change.sh"); + /** * This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_on_start.script properties to define the Libvirt VM on start method.
* The hook is called right after Libvirt successfully launched the VM.
@@ -252,6 +278,15 @@ public class AgentProperties{ */ public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_STOP_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_stop.script", "libvirt-vm-state-change.groovy"); + /** + * This property is used with the agent.hooks.basedir property to define the Libvirt VM on stop shell script.
+ * The shell script is used to execute the Libvirt VM on stop script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-state-change.sh + */ + public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_STOP_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_stop.shell_script", "libvirt-vm-state-change.sh"); + /** * This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_on_stop.script properties to define the Libvirt VM on stop method.
* The hook is called right after libvirt successfully stopped the VM.
@@ -383,7 +418,7 @@ 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). */ @@ -516,6 +551,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 */ @@ -751,7 +787,7 @@ public class AgentProperties{ 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 */ @@ -803,12 +839,44 @@ public class AgentProperties{ */ 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/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java index ccd0d976e58..26f9d4b3d73 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 @@ -397,9 +397,8 @@ public class ConsoleProxyResource extends ServerResourceBase implements ServerRe } 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(); diff --git a/agent/src/test/java/com/cloud/agent/AgentShellTest.java b/agent/src/test/java/com/cloud/agent/AgentShellTest.java index f7151779f58..d8def24a603 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 class AgentShellTest { 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 00000000000..65dc030ebd7 --- /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 32897725e0c..ec68e24c7e5 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.21.0.0-SNAPSHOT diff --git a/api/src/main/java/com/cloud/agent/api/Command.java b/api/src/main/java/com/cloud/agent/api/Command.java index eb979c0060b..c4e99cb4170 100644 --- a/api/src/main/java/com/cloud/agent/api/Command.java +++ b/api/src/main/java/com/cloud/agent/api/Command.java @@ -19,9 +19,10 @@ package com.cloud.agent.api; import java.util.HashMap; import java.util.Map; -import com.cloud.agent.api.LogLevel.Log4jLevel; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.cloud.agent.api.LogLevel.Log4jLevel; /** * implemented by classes that extends the Command class. Command specifies @@ -35,6 +36,23 @@ public abstract class Command { Continue, Stop } + public enum State { + CREATED, // Command is created by management server + STARTED, // Command is started by agent + PROCESSING, // Processing by agent + PROCESSING_IN_BACKEND, // Processing in backend by agent + COMPLETED, // Operation succeeds by agent or management server + FAILED, // Operation fails by agent + RECONCILE_RETRY, // Ready for retry of reconciliation + RECONCILING, // Being reconciled by management server + RECONCILED, // Reconciled by management server + RECONCILE_SKIPPED, // Skip the reconciliation as the resource state is inconsistent with the command + RECONCILE_FAILED, // Fail to reconcile by management server + TIMED_OUT, // Timed out on management server or agent + INTERRUPTED, // Interrupted by management server or agent (for example agent is restarted), + DANGLED_IN_BACKEND // Backend process which cannot be processed normally (for example agent is restarted) + } + public static final String HYPERVISOR_TYPE = "hypervisorType"; // allow command to carry over hypervisor or other environment related context info @@ -42,6 +60,8 @@ public abstract class Command { protected Map contextMap = new HashMap(); private int wait; //in second private boolean bypassHostMaintenance = false; + private transient long requestSequence = 0L; + protected Map> externalDetails; protected Command() { this.wait = 0; @@ -82,6 +102,10 @@ public abstract class Command { return contextMap.get(name); } + public Map getContextMap() { + return contextMap; + } + public boolean allowCaching() { return true; } @@ -94,6 +118,26 @@ public abstract class Command { this.bypassHostMaintenance = bypassHostMaintenance; } + public boolean isReconcile() { + return false; + } + + public long getRequestSequence() { + return requestSequence; + } + + public void setRequestSequence(long requestSequence) { + this.requestSequence = requestSequence; + } + + public void setExternalDetails(Map> externalDetails) { + this.externalDetails = externalDetails; + } + + public Map> getExternalDetails() { + return externalDetails; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java b/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java index 85ffc189820..5515a9c48bc 100644 --- a/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java +++ b/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java @@ -15,10 +15,24 @@ // specific language governing permissions and limitations // under the License. package com.cloud.agent.api; + +import org.apache.cloudstack.gpu.GpuDevice; + public class VgpuTypesInfo { + private boolean passthroughEnabled = true; + private GpuDevice.DeviceType deviceType; + private String parentBusAddress; + private String busAddress; + private String numaNode; + private String pciRoot; + private String deviceId; + private String deviceName; + private String vendorId; + private String vendorName; private String modelName; private String groupName; + private String vmName; private Long maxHeads; private Long videoRam; private Long maxResolutionX; @@ -26,6 +40,7 @@ public class VgpuTypesInfo { private Long maxVgpuPerGpu; private Long remainingCapacity; private Long maxCapacity; + private boolean display = false; public String getModelName() { return modelName; @@ -39,22 +54,42 @@ public class VgpuTypesInfo { return videoRam; } + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + public Long getMaxHeads() { return maxHeads; } + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + public Long getMaxResolutionX() { return maxResolutionX; } + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + public Long getMaxResolutionY() { return maxResolutionY; } + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } + public Long getMaxVpuPerGpu() { return maxVgpuPerGpu; } + public void setMaxVgpuPerGpu(Long maxVgpuPerGpu) { + this.maxVgpuPerGpu = maxVgpuPerGpu; + } + public Long getRemainingCapacity() { return remainingCapacity; } @@ -71,8 +106,133 @@ public class VgpuTypesInfo { this.maxCapacity = maxCapacity; } - public VgpuTypesInfo(String groupName, String modelName, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY, Long maxVgpuPerGpu, - Long remainingCapacity, Long maxCapacity) { + public boolean isPassthroughEnabled() { + return passthroughEnabled; + } + + public void setPassthroughEnabled(boolean passthroughEnabled) { + this.passthroughEnabled = passthroughEnabled; + } + + public GpuDevice.DeviceType getDeviceType() { + return deviceType; + } + + public void setDeviceType(GpuDevice.DeviceType deviceType) { + this.deviceType = deviceType; + } + + public String getParentBusAddress() { + return parentBusAddress; + } + + public void setParentBusAddress(String parentBusAddress) { + this.parentBusAddress = parentBusAddress; + } + + public String getBusAddress() { + return busAddress; + } + + public void setBusAddress(String busAddress) { + this.busAddress = busAddress; + } + + public String getNumaNode() { + return numaNode; + } + + public void setNumaNode(String numaNode) { + this.numaNode = numaNode; + } + + public String getPciRoot() { + return pciRoot; + } + + public void setPciRoot(String pciRoot) { + this.pciRoot = pciRoot; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public String getVendorId() { + return vendorId; + } + + public void setVendorId(String vendorId) { + this.vendorId = vendorId; + } + + public String getVendorName() { + return vendorName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public boolean isDisplay() { + return display; + } + + public void setDisplay(boolean display) { + this.display = display; + } + + public VgpuTypesInfo(GpuDevice.DeviceType deviceType, String groupName, String modelName, String busAddress, + String vendorId, String vendorName, String deviceId, String deviceName, String numaNode, String pciRoot + ) { + this.deviceType = deviceType; + this.groupName = groupName; + this.modelName = modelName; + this.busAddress = busAddress; + this.deviceId = deviceId; + this.deviceName = deviceName; + this.vendorId = vendorId; + this.vendorName = vendorName; + this.numaNode = numaNode; + this.pciRoot = pciRoot; + } + + public VgpuTypesInfo(GpuDevice.DeviceType deviceType, String groupName, String modelName, String busAddress, + String vendorId, String vendorName, String deviceId, String deviceName + ) { + this.deviceType = deviceType; + this.groupName = groupName; + this.modelName = modelName; + this.busAddress = busAddress; + this.deviceId = deviceId; + this.deviceName = deviceName; + this.vendorId = vendorId; + this.vendorName = vendorName; + } + + public VgpuTypesInfo(String groupName, String modelName, Long videoRam, Long maxHeads, Long maxResolutionX, + Long maxResolutionY, Long maxVgpuPerGpu, Long remainingCapacity, Long maxCapacity + ) { this.groupName = groupName; this.modelName = modelName; this.videoRam = videoRam; diff --git a/api/src/main/java/com/cloud/agent/api/to/BucketTO.java b/api/src/main/java/com/cloud/agent/api/to/BucketTO.java new file mode 100644 index 00000000000..f7e4bfea80f --- /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 d22df2df172..5664de79091 100644 --- a/api/src/main/java/com/cloud/agent/api/to/DiskTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/DiskTO.java @@ -46,7 +46,7 @@ public class DiskTO { private Long diskSeq; private String path; private Volume.Type type; - private Map _details; + private Map details; public DiskTO() { @@ -92,10 +92,10 @@ public class DiskTO { } public void setDetails(Map details) { - _details = details; + this.details = details; } public Map getDetails() { - return _details; + return details; } } diff --git a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java index d08884d1cbe..69350815be3 100644 --- a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java @@ -47,7 +47,7 @@ public class FirewallRuleTO implements InternalIdentity { int[] srcPortRange; boolean revoked; boolean alreadyAdded; - private List sourceCidrList; + protected List sourceCidrList; private List destCidrList; FirewallRule.Purpose purpose; private Integer icmpType; @@ -155,9 +155,7 @@ public class FirewallRuleTO implements InternalIdentity { 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 4afe080477b..6e9cee06dd3 100644 --- a/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java @@ -16,7 +16,9 @@ // under the License. package com.cloud.agent.api.to; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import com.cloud.agent.api.VgpuTypesInfo; @@ -24,9 +26,23 @@ public class GPUDeviceTO { private String gpuGroup; private String vgpuType; + private int gpuCount; private HashMap> groupDetails = new HashMap>(); + private List gpuDevices = new ArrayList<>(); - public GPUDeviceTO( String gpuGroup, String vgpuType, HashMap> groupDetails) { + public GPUDeviceTO(String gpuGroup, String vgpuType, int gpuCount, + HashMap> groupDetails, + List gpuDevices) { + this.gpuGroup = gpuGroup; + this.vgpuType = vgpuType; + this.groupDetails = groupDetails; + this.gpuCount = gpuCount; + this.gpuDevices = gpuDevices; + + } + + public GPUDeviceTO(String gpuGroup, String vgpuType, + HashMap> groupDetails) { this.gpuGroup = gpuGroup; this.vgpuType = vgpuType; this.groupDetails = groupDetails; @@ -48,6 +64,14 @@ public class GPUDeviceTO { this.vgpuType = vgpuType; } + public int getGpuCount() { + return gpuCount; + } + + public void setGpuCount(int gpuCount) { + this.gpuCount = gpuCount; + } + public HashMap> getGroupDetails() { return groupDetails; } @@ -56,4 +80,11 @@ public class GPUDeviceTO { this.groupDetails = groupDetails; } + public List getGpuDevices() { + return gpuDevices; + } + + public void setGpuDevices(List gpuDevices) { + this.gpuDevices = gpuDevices; + } } diff --git a/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java b/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java index 966d24886fe..f395f26aeed 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 @@ -374,13 +374,15 @@ public class LoadBalancerTO { 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 +393,10 @@ public class LoadBalancerTO { return id; } + public String getUuid() { + return uuid; + } + public String getName() { return name; } @@ -411,12 +417,14 @@ public class LoadBalancerTO { 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 +434,10 @@ public class LoadBalancerTO { return id; } + public String getUuid() { + return uuid; + } + public long getThreshold() { return threshold; } @@ -442,6 +454,7 @@ public class LoadBalancerTO { 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 +462,9 @@ public class LoadBalancerTO { 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 +477,10 @@ public class LoadBalancerTO { 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 bd08ce81101..d65ec0e3daa 100644 --- a/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java @@ -36,7 +36,7 @@ public class NetworkTO { protected TrafficType type; protected URI broadcastUri; protected URI isolationUri; - protected boolean isSecurityGroupEnabled; + protected boolean securityGroupEnabled; protected String name; protected String ip6address; protected String ip6gateway; @@ -112,7 +112,7 @@ public class NetworkTO { } public void setSecurityGroupEnabled(boolean enabled) { - this.isSecurityGroupEnabled = enabled; + this.securityGroupEnabled = enabled; } /** @@ -221,7 +221,7 @@ public class NetworkTO { } public boolean isSecurityGroupEnabled() { - return this.isSecurityGroupEnabled; + return this.securityGroupEnabled; } public void setIp6Dns1(String ip6Dns1) { diff --git a/api/src/main/java/com/cloud/agent/api/to/NfsTO.java b/api/src/main/java/com/cloud/agent/api/to/NfsTO.java index 0f6511e8311..eeddbf649a7 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 class NfsTO implements DataStoreTO { } + @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 573363c04fb..ca95fcfd679 100644 --- a/api/src/main/java/com/cloud/agent/api/to/NicTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/NicTO.java @@ -86,6 +86,14 @@ public class NicTO extends NetworkTO { this.nicUuid = uuid; } + public String getNicUuid() { + return nicUuid; + } + + public void setNicUuid(String nicUuid) { + this.nicUuid = nicUuid; + } + @Override public String toString() { return new StringBuilder("[Nic:").append(type).append("-").append(ip).append("-").append(broadcastUri).append("]").toString(); diff --git a/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java index 76d6d952814..91f337c5f55 100644 --- a/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java @@ -19,6 +19,7 @@ package com.cloud.agent.api.to; 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 class PortForwardingRuleTO extends FirewallRuleTO { 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 class PortForwardingRuleTO extends FirewallRuleTO { 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 6e7aa8b21e2..18737c584b3 100644 --- a/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java @@ -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.hostName = hostName; this.instanceName = instanceName; + } + + public RemoteInstanceTO(String instanceName, String instancePath, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) { + this.hypervisorType = Hypervisor.HypervisorType.VMware; + this.instanceName = instanceName; + this.instancePath = instancePath; this.vcenterHost = vcenterHost; - this.datacenterName = datacenterName; - this.clusterName = clusterName; this.vcenterUsername = vcenterUsername; this.vcenterPassword = vcenterPassword; + this.datacenterName = datacenterName; } public Hypervisor.HypervisorType getHypervisorType() { @@ -62,8 +63,8 @@ public class RemoteInstanceTO implements Serializable { return this.instanceName; } - public String getHostName() { - return this.hostName; + public String getInstancePath() { + return this.instancePath; } public String getVcenterUsername() { @@ -81,8 +82,4 @@ public class RemoteInstanceTO implements Serializable { 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 233238cf793..936f8168b1e 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; 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 final class S3TO implements ClientOptions, DataStoreTO { } + @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 e361e7a141f..cbdb7922eb4 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 @@ package com.cloud.agent.api.to; 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 @@ public class 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 b89dfea40e0..14038566fbd 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 @@ package com.cloud.agent.api.to; 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 class SwiftTO implements DataStoreTO, SwiftUtil.SwiftClientCfg { 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/VirtualMachineTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java index b4f4619be9a..cffb9874080 100644 --- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java @@ -19,20 +19,22 @@ package com.cloud.agent.api.to; import java.util.List; import java.util.Map; import java.util.HashMap; +import java.util.stream.Collectors; import com.cloud.agent.api.LogLevel; import com.cloud.network.element.NetworkElement; import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VmDetailConstants; public class VirtualMachineTO { private long id; private String name; private BootloaderType bootloader; private VirtualMachine.State state; - Type type; - int cpus; + private Type type; + private int cpus; /** 'speed' is still here since 4.0.X/4.1.X management servers do not support @@ -43,47 +45,50 @@ public class VirtualMachineTO { So this is here for backwards compatibility with 4.0.X/4.1.X management servers and newer agents. */ - Integer speed; - Integer minSpeed; - Integer maxSpeed; + private Integer speed; + private Integer minSpeed; + private Integer maxSpeed; - long minRam; - long maxRam; - String hostName; - String arch; - String os; - String platformEmulator; - String bootArgs; - String[] bootupScripts; - boolean enableHA; - boolean limitCpuUse; - boolean enableDynamicallyScaleVm; + private long minRam; + private long maxRam; + private String hostName; + private String arch; + private String os; + private String platformEmulator; + private String bootArgs; + private String[] bootupScripts; + private boolean enableHA; + private boolean limitCpuUse; + private boolean enableDynamicallyScaleVm; @LogLevel(LogLevel.Log4jLevel.Off) - String vncPassword; - String vncAddr; - Map params; - String uuid; - String bootType; - String bootMode; - boolean enterHardwareSetup; + private String vncPassword; + private String vncAddr; + private Map details; + private Map params; + private String uuid; + private String bootType; + private String bootMode; + private boolean enterHardwareSetup; - DiskTO[] disks; - NicTO[] nics; - GPUDeviceTO gpuDevice; - Integer vcpuMaxLimit; - List vmData = null; + private DiskTO[] disks; + private NicTO[] nics; + private GPUDeviceTO gpuDevice; + private Integer vcpuMaxLimit; + private List vmData = null; - String configDriveLabel = null; - String configDriveIsoRootFolder = null; - String configDriveIsoFile = null; - NetworkElement.Location configDriveLocation = NetworkElement.Location.SECONDARY; + private String configDriveLabel = null; + private String configDriveIsoRootFolder = null; + private String configDriveIsoFile = null; + private NetworkElement.Location configDriveLocation = NetworkElement.Location.SECONDARY; - Double cpuQuotaPercentage = null; + private Double cpuQuotaPercentage = null; - Map guestOsDetails = new HashMap(); - Map extraConfig = new HashMap<>(); - Map networkIdToNetworkNameMap = new HashMap<>(); - DeployAsIsInfoTO deployAsIsInfo; + private Map guestOsDetails = new HashMap(); + private Map extraConfig = new HashMap<>(); + private Map networkIdToNetworkNameMap = new HashMap<>(); + private DeployAsIsInfoTO deployAsIsInfo; + private String metadataManufacturer; + private String metadataProductName; public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader, String os, boolean enableHA, boolean limitCpuUse, String vncPassword) { @@ -189,7 +194,11 @@ public class VirtualMachineTO { return maxSpeed; } - public boolean getLimitCpuUse() { + public boolean isEnableHA() { + return enableHA; + } + + public boolean isLimitCpuUse() { return limitCpuUse; } @@ -254,6 +263,10 @@ public class VirtualMachineTO { this.bootupScripts = bootupScripts; } + public void setEnableHA(boolean enableHA) { + this.enableHA = enableHA; + } + public DiskTO[] getDisks() { return disks; } @@ -287,11 +300,11 @@ public class VirtualMachineTO { } public Map getDetails() { - return params; + return details; } public void setDetails(Map params) { - this.params = params; + this.details = params; } public String getUuid() { @@ -429,8 +442,72 @@ public class VirtualMachineTO { this.deployAsIsInfo = deployAsIsInfo; } + public void setSpeed(Integer speed) { + this.speed = speed; + } + + public void setMinSpeed(Integer minSpeed) { + this.minSpeed = minSpeed; + } + + public void setMaxSpeed(Integer maxSpeed) { + this.maxSpeed = maxSpeed; + } + + public void setMinRam(long minRam) { + this.minRam = minRam; + } + + public void setMaxRam(long maxRam) { + this.maxRam = maxRam; + } + + public void setLimitCpuUse(boolean limitCpuUse) { + this.limitCpuUse = limitCpuUse; + } + + public Map getParams() { + return params; + } + + public void setParams(Map params) { + this.params = params; + } + + public void setExtraConfig(Map extraConfig) { + this.extraConfig = extraConfig; + } + + public String getMetadataManufacturer() { + return metadataManufacturer; + } + + public void setMetadataManufacturer(String metadataManufacturer) { + this.metadataManufacturer = metadataManufacturer; + } + + public String getMetadataProductName() { + return metadataProductName; + } + + public void setMetadataProductName(String metadataProductName) { + this.metadataProductName = metadataProductName; + } + @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/bgp/ASNumber.java b/api/src/main/java/com/cloud/bgp/ASNumber.java new file mode 100644 index 00000000000..b0e5394df75 --- /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 00000000000..ae877ee60db --- /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 00000000000..61d149f2847 --- /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 a4e2c2a7f05..4e584b18fee 100644 --- a/api/src/main/java/com/cloud/capacity/Capacity.java +++ b/api/src/main/java/com/cloud/capacity/Capacity.java @@ -34,13 +34,17 @@ public interface Capacity extends InternalIdentity, Identity { public static final short CAPACITY_TYPE_LOCAL_STORAGE = 9; public static final short CAPACITY_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET = 10; public static final short CAPACITY_TYPE_GPU = 19; + public static final short CAPACITY_TYPE_OBJECT_STORAGE = 20; + public static final short CAPACITY_TYPE_BACKUP_STORAGE = 21; public static final short CAPACITY_TYPE_CPU_CORE = 90; public static final List STORAGE_CAPACITY_TYPES = List.of(CAPACITY_TYPE_STORAGE, CAPACITY_TYPE_STORAGE_ALLOCATED, CAPACITY_TYPE_SECONDARY_STORAGE, - CAPACITY_TYPE_LOCAL_STORAGE); + CAPACITY_TYPE_LOCAL_STORAGE, + CAPACITY_TYPE_BACKUP_STORAGE, + CAPACITY_TYPE_OBJECT_STORAGE); public Long getHostOrPoolId(); diff --git a/api/src/main/java/com/cloud/configuration/ConfigurationService.java b/api/src/main/java/com/cloud/configuration/ConfigurationService.java index 97d4b42974b..32e31519ea7 100644 --- a/api/src/main/java/com/cloud/configuration/ConfigurationService.java +++ b/api/src/main/java/com/cloud/configuration/ConfigurationService.java @@ -17,7 +17,11 @@ package com.cloud.configuration; import java.util.List; +import java.util.Map; +import java.util.Objects; +import com.cloud.network.Network; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd; @@ -201,11 +205,12 @@ public interface ConfigurationService { * TODO * @param allocationState * TODO + * @param storageAccessGroups * @return the new pod if successful, null otherwise * @throws * @throws */ - Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState); + Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState, List storageAccessGroups); /** * Creates a mutual exclusive IP range in the pod with same gateway, netmask. @@ -372,4 +377,16 @@ public interface ConfigurationService { List listPortableIps(long id); Boolean isAccountAllowedToCreateOfferingsWithTags(IsAccountAllowedToCreateOfferingsWithTagsCmd cmd); + + public static final Map ProviderDetailKeyMap = Map.of( + Network.Provider.Nsx.getName(), ApiConstants.NSX_DETAIL_KEY, + Network.Provider.Netris.getName(), ApiConstants.NETRIS_DETAIL_KEY + ); + + public static boolean IsIpRangeForProvider(Network.Provider provider) { + if (Objects.isNull(provider)) { + return false; + } + return ProviderDetailKeyMap.containsKey(provider.getName()); + } } diff --git a/api/src/main/java/com/cloud/configuration/Resource.java b/api/src/main/java/com/cloud/configuration/Resource.java index bf8fca9d905..97be7f9d64c 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 @@ public interface Resource { 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 interface Resource { } 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 { 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 00000000000..3016e542db6 --- /dev/null +++ b/api/src/main/java/com/cloud/cpu/CPU.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 org.apache.commons.lang3.StringUtils; + +public class CPU { + public enum CPUArch { + x86("i686", 32), + amd64("x86_64", 64), + arm64("aarch64", 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 63188ca0b0e..23e6cc88a1e 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.Identity; 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 1cbab36f3bd..17c5b615d4b 100644 --- a/api/src/main/java/com/cloud/dc/Pod.java +++ b/api/src/main/java/com/cloud/dc/Pod.java @@ -43,4 +43,6 @@ public interface Pod extends InfrastructureEntity, Grouping, Identity, InternalI AllocationState getAllocationState(); boolean getExternalDhcp(); + + String getStorageAccessGroups(); } diff --git a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java index 2697311d2b9..d127e4bdd66 100644 --- a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java +++ b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java @@ -62,7 +62,7 @@ public interface DeploymentClusterPlanner extends DeploymentPlanner { "vm.allocation.algorithm", "Advanced", "random", - "Order in which hosts within a cluster will be considered for VM/volume allocation. The value can be 'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit', or 'firstfitleastconsumed'.", + "Order in which hosts within a cluster will be considered for VM allocation. The value can be 'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit', or 'firstfitleastconsumed'.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index d385fa9ed07..be21f13267b 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -28,10 +28,19 @@ 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.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.quota.QuotaTariff; +import org.apache.cloudstack.storage.sharedfs.SharedFS; import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.vm.schedule.VMSchedule; @@ -242,6 +251,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"; @@ -283,9 +294,12 @@ public class EventTypes { //registering userdata events public static final String EVENT_REGISTER_USER_DATA = "REGISTER.USER.DATA"; + public static final String EVENT_REGISTER_CNI_CONFIG = "REGISTER.CNI.CONFIG"; + public static final String EVENT_DELETE_CNI_CONFIG = "DELETE.CNI.CONFIG"; //register for user API and secret keys public static final String EVENT_REGISTER_FOR_SECRET_API_KEY = "REGISTER.USER.KEY"; + public static final String API_KEY_ACCESS_UPDATE = "API.KEY.ACCESS.UPDATE"; // Template Events public static final String EVENT_TEMPLATE_CREATE = "TEMPLATE.CREATE"; @@ -316,6 +330,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 @@ -331,6 +347,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"; @@ -364,6 +381,21 @@ public class EventTypes { public static final String EVENT_DISK_OFFERING_EDIT = "DISK.OFFERING.EDIT"; public static final String EVENT_DISK_OFFERING_DELETE = "DISK.OFFERING.DELETE"; + // GPU Cards + public static final String EVENT_GPU_CARD_CREATE = "GPU.CARD.CREATE"; + public static final String EVENT_GPU_CARD_EDIT = "GPU.CARD.EDIT"; + public static final String EVENT_GPU_CARD_DELETE = "GPU.CARD.DELETE"; + + // vGPU Profile + public static final String EVENT_VGPU_PROFILE_CREATE = "VGPU.PROFILE.CREATE"; + public static final String EVENT_VGPU_PROFILE_EDIT = "VGPU.PROFILE.EDIT"; + public static final String EVENT_VGPU_PROFILE_DELETE = "VGPU.PROFILE.DELETE"; + + // GPU Devices + public static final String EVENT_GPU_DEVICE_CREATE = "GPU.DEVICE.CREATE"; + public static final String EVENT_GPU_DEVICE_EDIT = "GPU.DEVICE.EDIT"; + public static final String EVENT_GPU_DEVICE_DELETE = "GPU.DEVICE.DELETE"; + // Network offerings public static final String EVENT_NETWORK_OFFERING_CREATE = "NETWORK.OFFERING.CREATE"; public static final String EVENT_NETWORK_OFFERING_ASSIGN = "NETWORK.OFFERING.ASSIGN"; @@ -388,6 +420,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"; @@ -446,9 +483,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"; @@ -479,6 +519,8 @@ public class EventTypes { public static final String EVENT_ZONE_VLAN_ASSIGN = "ZONE.VLAN.ASSIGN"; public static final String EVENT_ZONE_VLAN_RELEASE = "ZONE.VLAN.RELEASE"; + public static final String EVENT_ZONE_VXLAN_ASSIGN = "ZONE.VXLAN.ASSIGN"; + public static final String EVENT_ZONE_VXLAN_RELEASE = "ZONE.VXLAN.RELEASE"; // Projects public static final String EVENT_PROJECT_CREATE = "PROJECT.CREATE"; @@ -590,11 +632,13 @@ public class EventTypes { public static final String EVENT_VM_BACKUP_CREATE = "BACKUP.CREATE"; public static final String EVENT_VM_BACKUP_RESTORE = "BACKUP.RESTORE"; public static final String EVENT_VM_BACKUP_DELETE = "BACKUP.DELETE"; + public static final String EVENT_VM_BACKUP_OFFERING_REMOVED_AND_BACKUPS_DELETED = "BACKUP.OFFERING.BACKUPS.DEL"; public static final String EVENT_VM_BACKUP_RESTORE_VOLUME_TO_VM = "BACKUP.RESTORE.VOLUME.TO.VM"; public static final String EVENT_VM_BACKUP_SCHEDULE_CONFIGURE = "BACKUP.SCHEDULE.CONFIGURE"; public static final String EVENT_VM_BACKUP_SCHEDULE_DELETE = "BACKUP.SCHEDULE.DELETE"; public static final String EVENT_VM_BACKUP_USAGE_METRIC = "BACKUP.USAGE.METRIC"; public static final String EVENT_VM_BACKUP_EDIT = "BACKUP.OFFERING.EDIT"; + public static final String EVENT_VM_CREATE_FROM_BACKUP = "VM.CREATE.FROM.BACKUP"; // external network device events public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD"; @@ -670,6 +714,9 @@ public class EventTypes { public static final String EVENT_EXTERNAL_OPENDAYLIGHT_CONFIGURE_CONTROLLER = "PHYSICAL.ODLCONTROLLER.CONFIGURE"; //Guest OS related events + public static final String EVENT_GUEST_OS_CATEGORY_ADD = "GUEST.OS.CATEGORY.ADD"; + public static final String EVENT_GUEST_OS_CATEGORY_DELETE = "GUEST.OS.CATEGORY.DELETE"; + public static final String EVENT_GUEST_OS_CATEGORY_UPDATE = "GUEST.OS.CATEGORY.UPDATE"; public static final String EVENT_GUEST_OS_ADD = "GUEST.OS.ADD"; public static final String EVENT_GUEST_OS_REMOVE = "GUEST.OS.REMOVE"; public static final String EVENT_GUEST_OS_UPDATE = "GUEST.OS.UPDATE"; @@ -719,6 +766,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"; @@ -735,6 +791,67 @@ public class EventTypes { 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"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking @@ -836,6 +953,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); @@ -892,6 +1011,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); @@ -926,6 +1046,21 @@ public class EventTypes { entityEventDetails.put(EVENT_DISK_OFFERING_EDIT, DiskOffering.class); entityEventDetails.put(EVENT_DISK_OFFERING_DELETE, DiskOffering.class); + // GPU Cards + entityEventDetails.put(EVENT_GPU_CARD_CREATE, GpuCard.class); + entityEventDetails.put(EVENT_GPU_CARD_EDIT, GpuCard.class); + entityEventDetails.put(EVENT_GPU_CARD_DELETE, GpuCard.class); + + // vGPU Profiles + entityEventDetails.put(EVENT_VGPU_PROFILE_CREATE, VgpuProfile.class); + entityEventDetails.put(EVENT_VGPU_PROFILE_EDIT, VgpuProfile.class); + entityEventDetails.put(EVENT_VGPU_PROFILE_DELETE, VgpuProfile.class); + + // GPU Devices + entityEventDetails.put(EVENT_GPU_DEVICE_CREATE, GpuDevice.class); + entityEventDetails.put(EVENT_GPU_DEVICE_EDIT, GpuDevice.class); + entityEventDetails.put(EVENT_GPU_DEVICE_DELETE, GpuDevice.class); + // Network offerings entityEventDetails.put(EVENT_NETWORK_OFFERING_CREATE, NetworkOffering.class); entityEventDetails.put(EVENT_NETWORK_OFFERING_ASSIGN, NetworkOffering.class); @@ -996,8 +1131,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); @@ -1175,6 +1312,12 @@ public class EventTypes { entityEventDetails.put(EVENT_UPDATE_IMAGE_STORE_ACCESS_STATE, ImageStore.class); entityEventDetails.put(EVENT_LIVE_PATCH_SYSTEMVM, "SystemVMs"); + entityEventDetails.put(EVENT_MS_MAINTENANCE_PREPARE, "ManagementServer"); + entityEventDetails.put(EVENT_MS_MAINTENANCE_CANCEL, "ManagementServer"); + entityEventDetails.put(EVENT_MS_SHUTDOWN_PREPARE, "ManagementServer"); + entityEventDetails.put(EVENT_MS_SHUTDOWN_CANCEL, "ManagementServer"); + entityEventDetails.put(EVENT_MS_SHUTDOWN, "ManagementServer"); + //Object Store entityEventDetails.put(EVENT_OBJECT_STORE_CREATE, ObjectStore.class); entityEventDetails.put(EVENT_OBJECT_STORE_UPDATE, ObjectStore.class); @@ -1189,8 +1332,65 @@ public class EventTypes { 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); } + 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) { @@ -1219,4 +1419,12 @@ public class EventTypes { 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 fe27408eb4e..66b607100d9 100644 --- a/api/src/main/java/com/cloud/exception/OperationTimedoutException.java +++ b/api/src/main/java/com/cloud/exception/OperationTimedoutException.java @@ -40,7 +40,7 @@ public class OperationTimedoutException extends CloudException { boolean _isActive; public OperationTimedoutException(Command[] cmds, long agentId, long seqId, int time, boolean isActive) { - super("Commands " + seqId + " to Host " + agentId + " timed out after " + time); + super("Commands " + seqId + " to Host " + agentId + " timed out after " + time + " secs"); _agentId = agentId; _seqId = seqId; _time = time; diff --git a/api/src/main/java/com/cloud/exception/StorageAccessException.java b/api/src/main/java/com/cloud/exception/StorageAccessException.java index eefbcf5518a..d54d77d66f1 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 @@ import com.cloud.utils.SerialVersionUID; 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 7563bc3b742..07a0dfce041 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,12 @@ public interface Host extends StateObject, Identity, Partition, HAResour return strs; } } - public static final String HOST_UEFI_ENABLE = "host.uefi.enable"; - public static final String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; + + String HOST_UEFI_ENABLE = "host.uefi.enable"; + String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; + String HOST_INSTANCE_CONVERSION = "host.instance.conversion"; + String HOST_OVFTOOL_VERSION = "host.ovftool.version"; + String HOST_VIRTV2V_VERSION = "host.virtv2v.version"; /** * @return name of the machine. @@ -175,6 +180,8 @@ public interface Host extends StateObject, Identity, Partition, HAResour */ Long getManagementServerId(); + Long getLastManagementServerId(); + /* *@return removal date */ @@ -207,4 +214,8 @@ public interface Host extends StateObject, Identity, Partition, HAResour boolean isDisabled(); ResourceState getResourceState(); + + CPU.CPUArch getArch(); + + String getStorageAccessGroups(); } diff --git a/api/src/main/java/com/cloud/host/Status.java b/api/src/main/java/com/cloud/host/Status.java index 5dc82bbfaef..af6af82e973 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 enum Status { 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 e1108b34a83..5baac484772 100644 --- a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java +++ b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java @@ -20,37 +20,58 @@ import com.cloud.storage.Storage.ImageFormat; import org.apache.commons.lang3.StringUtils; 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; public class Hypervisor { public static class HypervisorType { + public enum Functionality { + DirectDownloadTemplate, + RootDiskSizeOverride, + VmStorageMigration + } + 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); - public static final HypervisorType KVM = new HypervisorType("KVM", ImageFormat.QCOW2); - public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA); + public static final HypervisorType XenServer = new HypervisorType("XenServer", ImageFormat.VHD, EnumSet.of(RootDiskSizeOverride, VmStorageMigration)); + public static final HypervisorType KVM = new HypervisorType("KVM", ImageFormat.QCOW2, EnumSet.of(DirectDownloadTemplate, RootDiskSizeOverride, VmStorageMigration)); + public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA, EnumSet.of(RootDiskSizeOverride, VmStorageMigration)); public static final HypervisorType 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"); + 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"); + 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); + this(name, null, EnumSet.noneOf(Functionality.class)); } public HypervisorType(String name, ImageFormat imageFormat) { + this(name, imageFormat, EnumSet.noneOf(Functionality.class)); + } + + 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 { @@ -81,6 +102,12 @@ public class Hypervisor { 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 @@ -102,6 +129,15 @@ public class Hypervisor { 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); diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java index 3c7dbac6442..0c821b4e36c 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.backup.Backup; 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 @@ public interface HypervisorGuru extends Adapter { * 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 @@ public interface HypervisorGuru extends Adapter { * @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 82% rename from plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java rename to api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java index 591da077aec..8b5551d6a8e 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java @@ -44,6 +44,8 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm AutoscaleRequested, ScaleUpRequested, ScaleDownRequested, + AddNodeRequested, + RemoveNodeRequested, UpgradeRequested, OperationSucceeded, OperationFailed, @@ -59,6 +61,8 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm 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"), 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"), @@ -96,6 +100,17 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm 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 +157,13 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm Long getMaxSize(); Long getSecurityGroupId(); ClusterType getClusterType(); + Long getControlNodeServiceOfferingId(); + Long getWorkerNodeServiceOfferingId(); + Long getEtcdNodeServiceOfferingId(); + Long getControlNodeTemplateId(); + Long getWorkerNodeTemplateId(); + Long getEtcdNodeTemplateId(); + Long getEtcdNodeCount(); + Long getCniConfigId(); + String getCniConfigDetails(); } diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java new file mode 100644 index 00000000000..37b8907b454 --- /dev/null +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.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.kubernetes.cluster; + +import org.apache.cloudstack.acl.ControlledEntity; + +import java.util.Map; + +import com.cloud.user.Account; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Adapter; + +public interface KubernetesServiceHelper extends Adapter { + + enum KubernetesClusterNodeType { + CONTROL, WORKER, ETCD, DEFAULT + } + + ControlledEntity findByUuid(String uuid); + ControlledEntity findByVmId(long vmId); + void checkVmCanBeDestroyed(UserVm userVm); + boolean isValidNodeType(String nodeType); + Map getServiceOfferingNodeTypeMap(Map> serviceOfferingNodeTypeMap); + Map getTemplateNodeTypeMap(Map> templateNodeTypeMap); + void cleanupForAccount(Account account); +} diff --git a/api/src/main/java/com/cloud/network/IpAddress.java b/api/src/main/java/com/cloud/network/IpAddress.java index ae1af450577..70d652b54e9 100644 --- a/api/src/main/java/com/cloud/network/IpAddress.java +++ b/api/src/main/java/com/cloud/network/IpAddress.java @@ -99,4 +99,5 @@ public interface IpAddress extends ControlledEntity, Identity, InternalIdentity, boolean isForSystemVms(); + boolean isForRouter(); } diff --git a/api/src/main/java/com/cloud/network/Ipv6Service.java b/api/src/main/java/com/cloud/network/Ipv6Service.java index 2b4dff01086..4ef5f98c38d 100644 --- a/api/src/main/java/com/cloud/network/Ipv6Service.java +++ b/api/src/main/java/com/cloud/network/Ipv6Service.java @@ -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 3b13ef7bd9c..dc94932e31f 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 @@ public interface Network extends ControlledEntity, StateObject, I 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, @@ -206,6 +206,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Provider Tungsten = new Provider("Tungsten", false); public static final Provider Nsx = new Provider("Nsx", false); + public static final Provider Netris = new Provider("Netris", false); private final String name; private final boolean isExternal; @@ -412,12 +413,16 @@ public interface Network extends ControlledEntity, StateObject, I 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 @@ -503,4 +508,6 @@ public interface Network extends ControlledEntity, StateObject, I 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 53ac735cf05..eb496ac4e0b 100644 --- a/api/src/main/java/com/cloud/network/NetworkModel.java +++ b/api/src/main/java/com/cloud/network/NetworkModel.java @@ -149,7 +149,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 +173,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 +305,8 @@ public interface NetworkModel { NicProfile getNicProfile(VirtualMachine vm, long networkId, String broadcastUri); + NicProfile getNicProfile(VirtualMachine vm, Nic nic, DataCenter dataCenter); + Set getAvailableIps(Network network, String requestedIp); String getDomainNetworkDomain(long domainId, long zoneId); @@ -317,6 +321,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 +360,8 @@ public interface NetworkModel { void verifyIp6DnsPair(final String ip6Dns1, final String ip6Dns2); + boolean isSecurityGroupSupportedForZone(Long zoneId); + + boolean checkSecurityGroupSupportForNetwork(Account account, DataCenter zone, List networkIds, + List securityGroupsIds); } diff --git a/api/src/main/java/com/cloud/network/NetworkProfile.java b/api/src/main/java/com/cloud/network/NetworkProfile.java index 1a5c80ea871..2e8efb48930 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 java.util.Date; 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 class NetworkProfile implements Network { isRedundant = network.isRedundant(); isRollingRestart = network.isRollingRestart(); externalId = network.getExternalId(); + networkCidrSize = network.getNetworkCidrSize(); } @Override @@ -210,11 +213,21 @@ public class NetworkProfile implements Network { 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 class NetworkProfile implements Network { 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 51799e25cda..196e1f9aab8 100644 --- a/api/src/main/java/com/cloud/network/NetworkService.java +++ b/api/src/main/java/com/cloud/network/NetworkService.java @@ -19,7 +19,7 @@ package com.cloud.network; import java.util.List; import java.util.Map; -import com.cloud.dc.DataCenter; +import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd; @@ -38,13 +38,16 @@ import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd; import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; +import com.cloud.agent.api.to.NicTO; +import com.cloud.dc.DataCenter; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.Network.IpAddresses; import com.cloud.network.Network.Service; import com.cloud.network.Networks.TrafficType; @@ -56,7 +59,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; -import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; /** * The NetworkService interface is the "public" api to entities that make requests to the orchestration engine @@ -102,6 +104,10 @@ public interface NetworkService { 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; + Pair, Integer> searchForNetworks(ListNetworksCmd cmd); boolean deleteNetwork(long networkId, boolean forced); @@ -263,4 +269,10 @@ public interface NetworkService { InternalLoadBalancerElementService getInternalLoadBalancerElementByNetworkServiceProviderId(long networkProviderId); InternalLoadBalancerElementService getInternalLoadBalancerElementById(long providerId); List getInternalLoadBalancerElements(); + + boolean handleCksIsoOnNetworkVirtualRouter(Long virtualRouterId, boolean mount) throws ResourceUnavailableException; + + IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress); + + String getNicVlanValueForExternalVm(NicTO nic); } diff --git a/api/src/main/java/com/cloud/network/Networks.java b/api/src/main/java/com/cloud/network/Networks.java index dfa0ddb84ca..9f06a044111 100644 --- a/api/src/main/java/com/cloud/network/Networks.java +++ b/api/src/main/java/com/cloud/network/Networks.java @@ -129,7 +129,8 @@ public class Networks { UnDecided(null, null), OpenDaylight("opendaylight", String.class), TUNGSTEN("tf", String.class), - NSX("nsx", String.class); + NSX("nsx", String.class), + Netris("netris", String.class); private final String scheme; private final Class type; diff --git a/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java new file mode 100644 index 00000000000..a22db4287dc --- /dev/null +++ b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java @@ -0,0 +1,358 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network; + +import java.util.List; + +public class SDNProviderNetworkRule { + + protected long domainId; + protected long accountId; + protected long zoneId; + protected Long networkResourceId; + protected String networkResourceName; + protected boolean isVpcResource; + protected long vmId; + protected long ruleId; + protected String publicIp; + protected String vmIp; + protected String publicPort; + protected String privatePort; + protected String protocol; + protected String algorithm; + protected List sourceCidrList; + protected List destinationCidrList; + protected Integer icmpCode; + + protected Integer icmpType; + protected String trafficType; + protected Network.Service service; + + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public Long getNetworkResourceId() { + return networkResourceId; + } + + public void setNetworkResourceId(Long networkResourceId) { + this.networkResourceId = networkResourceId; + } + + public String getNetworkResourceName() { + return networkResourceName; + } + + public void setNetworkResourceName(String networkResourceName) { + this.networkResourceName = networkResourceName; + } + + public boolean isVpcResource() { + return isVpcResource; + } + + public void setVpcResource(boolean vpcResource) { + isVpcResource = vpcResource; + } + + public long getVmId() { + return vmId; + } + + public void setVmId(long vmId) { + this.vmId = vmId; + } + + public long getRuleId() { + return ruleId; + } + + public void setRuleId(long ruleId) { + this.ruleId = ruleId; + } + + public String getPublicIp() { + return publicIp; + } + + public void setPublicIp(String publicIp) { + this.publicIp = publicIp; + } + + public String getVmIp() { + return vmIp; + } + + public void setVmIp(String vmIp) { + this.vmIp = vmIp; + } + + public String getPublicPort() { + return publicPort; + } + + public void setPublicPort(String publicPort) { + this.publicPort = publicPort; + } + + public String getPrivatePort() { + return privatePort; + } + + public void setPrivatePort(String privatePort) { + this.privatePort = privatePort; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getAlgorithm() { + return algorithm; + } + + public Network.Service getService() { + return service; + } + + public void setService(Network.Service service) { + this.service = service; + } + + public Integer getIcmpCode() { + return icmpCode; + } + + public void setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + } + + public Integer getIcmpType() { + return icmpType; + } + + public void setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + } + + public List getSourceCidrList() { + return sourceCidrList; + } + + public void setSourceCidrList(List sourceCidrList) { + this.sourceCidrList = sourceCidrList; + } + + public List getDestinationCidrList() { + return destinationCidrList; + } + + public void setDestinationCidrList(List destinationCidrList) { + this.destinationCidrList = destinationCidrList; + } + + public String getTrafficType() { + return trafficType; + } + + public void setTrafficType(String trafficType) { + this.trafficType = trafficType; + } + + public static class Builder { + public long domainId; + public long accountId; + public long zoneId; + public Long networkResourceId; + public String networkResourceName; + public boolean isVpcResource; + public long vmId; + + public long ruleId; + public String publicIp; + public String vmIp; + public String publicPort; + public String privatePort; + public String protocol; + public String algorithm; + public List sourceCidrList; + public List destinationCidrList; + public String trafficType; + public Integer icmpType; + public Integer icmpCode; + public Network.Service service; + + public Builder() { + // Default constructor + } + + public Builder setDomainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder setAccountId(long accountId) { + this.accountId = accountId; + return this; + } + + public Builder setZoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder setNetworkResourceId(Long networkResourceId) { + this.networkResourceId = networkResourceId; + return this; + } + + public Builder setNetworkResourceName(String networkResourceName) { + this.networkResourceName = networkResourceName; + return this; + } + + public Builder setVpcResource(boolean isVpcResource) { + this.isVpcResource = isVpcResource; + return this; + } + + + public Builder setVmId(long vmId) { + this.vmId = vmId; + return this; + } + + public Builder setRuleId(long ruleId) { + this.ruleId = ruleId; + return this; + } + + public Builder setPublicIp(String publicIp) { + this.publicIp = publicIp; + return this; + } + + public Builder setVmIp(String vmIp) { + this.vmIp = vmIp; + return this; + } + + public Builder setPublicPort(String publicPort) { + this.publicPort = publicPort; + return this; + } + + public Builder setPrivatePort(String privatePort) { + this.privatePort = privatePort; + return this; + } + + public Builder setProtocol(String protocol) { + this.protocol = protocol; + return this; + } + + public Builder setAlgorithm(String algorithm) { + this.algorithm = algorithm; + return this; + } + + public Builder setTrafficType(String trafficType) { + this.trafficType = trafficType; + return this; + } + + public Builder setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + return this; + } + + public Builder setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + return this; + } + + public Builder setSourceCidrList(List sourceCidrList) { + this.sourceCidrList = sourceCidrList; + return this; + } + + public Builder setDestinationCidrList(List destinationCidrList) { + this.destinationCidrList = destinationCidrList; + return this; + } + + public Builder setService(Network.Service service) { + this.service = service; + return this; + } + + public SDNProviderNetworkRule build() { + SDNProviderNetworkRule rule = new SDNProviderNetworkRule(); + rule.setDomainId(this.domainId); + rule.setAccountId(this.accountId); + rule.setZoneId(this.zoneId); + rule.setNetworkResourceId(this.networkResourceId); + rule.setNetworkResourceName(this.networkResourceName); + rule.setVpcResource(this.isVpcResource); + rule.setVmId(this.vmId); + rule.setVmIp(this.vmIp); + rule.setPublicIp(this.publicIp); + rule.setPublicPort(this.publicPort); + rule.setPrivatePort(this.privatePort); + rule.setProtocol(this.protocol); + rule.setRuleId(this.ruleId); + rule.setAlgorithm(this.algorithm); + rule.setIcmpType(this.icmpType); + rule.setIcmpCode(this.icmpCode); + rule.setSourceCidrList(this.sourceCidrList); + rule.setDestinationCidrList(this.destinationCidrList); + rule.setTrafficType(this.trafficType); + rule.setService(service); + return rule; + } + } +} diff --git a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java index 994df875f7d..51036abe060 100644 --- a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java +++ b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java @@ -24,7 +24,7 @@ import org.apache.cloudstack.api.InternalIdentity; public interface Site2SiteVpnConnection extends ControlledEntity, InternalIdentity, Displayable { enum State { - Pending, Connecting, Connected, Disconnected, Error, + Pending, Connecting, Connected, Disconnected, Error, Removed } @Override diff --git a/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java b/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java index c47500c7849..cb92739d283 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); diff --git a/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java b/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java index 5c3ee3f1032..cd04db802ca 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 @@ public interface VpcVirtualNetworkApplianceService extends VirtualNetworkApplian /** * @param router * @param network - * @param isRedundant * @return * @throws ConcurrentOperationException * @throws ResourceUnavailableException */ boolean removeVpcRouterFromGuestNetwork(VirtualRouter router, Network network) throws ConcurrentOperationException, ResourceUnavailableException; + + /** + * @param router + * @param network + * @return + * @throws ConcurrentOperationException + * @throws ResourceUnavailableException + */ + boolean stopKeepAlivedOnRouter(VirtualRouter router, Network network) throws ConcurrentOperationException, ResourceUnavailableException; + + + /** + * @param router + * @param network + * @return + * @throws ConcurrentOperationException + * @throws ResourceUnavailableException + */ + boolean startKeepAlivedOnRouter(VirtualRouter router, Network network) throws ConcurrentOperationException, ResourceUnavailableException; + } 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 00000000000..ee919cb1af7 --- /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 1bb37be970d..dc0f60f4519 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/NetworkElement.java b/api/src/main/java/com/cloud/network/element/NetworkElement.java index fa67575edd3..cb0fc2fca98 100644 --- a/api/src/main/java/com/cloud/network/element/NetworkElement.java +++ b/api/src/main/java/com/cloud/network/element/NetworkElement.java @@ -23,6 +23,7 @@ import com.cloud.deploy.DeployDestination; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.IpAddress; import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; @@ -87,6 +88,14 @@ public interface NetworkElement extends Adapter { boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException; + /** + * Release IP from the network provider if reserved + * @param ipAddress + */ + default boolean releaseIp(IpAddress ipAddress) { + return true; + } + /** * The network is being shutdown. * @param network diff --git a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java index e99bc2fd416..8dcc8b6d0a4 100644 --- a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java +++ b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java @@ -17,12 +17,40 @@ package com.cloud.network.element; import java.util.List; +import java.util.Objects; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.vpc.NetworkACLItem; public interface PortForwardingServiceProvider extends NetworkElement, IpDeployingRequester { + + static String getPublicPortRange(PortForwardingRule rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + + static String getPrivatePFPortRange(PortForwardingRule rule) { + return rule.getDestinationPortStart() == rule.getDestinationPortEnd() ? + String.valueOf(rule.getDestinationPortStart()) : + String.valueOf(rule.getDestinationPortStart()).concat("-").concat(String.valueOf(rule.getDestinationPortEnd())); + } + + static String getPrivatePortRange(FirewallRule rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + + static String getPrivatePortRangeForACLRule(NetworkACLItem rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + /** * Apply rules * @param network diff --git a/api/src/main/java/com/cloud/network/element/VpcProvider.java b/api/src/main/java/com/cloud/network/element/VpcProvider.java index 6debd1fbc2d..fe8c8f8612f 100644 --- a/api/src/main/java/com/cloud/network/element/VpcProvider.java +++ b/api/src/main/java/com/cloud/network/element/VpcProvider.java @@ -55,4 +55,8 @@ public interface VpcProvider extends NetworkElement { boolean applyACLItemsToPrivateGw(PrivateGateway gateway, List rules) throws ResourceUnavailableException; boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); + + default boolean updateVpc(Vpc vpc, String previousVpcName) { + return true; + } } diff --git a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java index cbadbb18a8f..ced664e54a9 100644 --- a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java +++ b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java @@ -212,4 +212,11 @@ public interface NetworkGuru extends Adapter { 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 64b2aeedf12..e4cf4ec526f 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 class LoadBalancingRule { return lb.getId(); } + public LoadBalancer getLb() { + return lb; + } + public String getName() { return lb.getName(); } 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 00000000000..afc21f7f511 --- /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 00000000000..211517ead49 --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java @@ -0,0 +1,108 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.netris; + +import com.cloud.network.SDNProviderNetworkRule; + + +import java.util.List; + +public class NetrisNetworkRule { + public enum NetrisRuleAction { + PERMIT, DENY + } + + private SDNProviderNetworkRule baseRule; + private NetrisRuleAction aclAction; + private List lbBackends; + private String lbRuleName; + private String lbCidrList; + private String reason; + + public NetrisNetworkRule(Builder builder) { + this.baseRule = builder.baseRule; + this.aclAction = builder.aclAction; + this.lbBackends = builder.lbBackends; + this.reason = builder.reason; + this.lbCidrList = builder.lbCidrList; + this.lbRuleName = builder.lbRuleName; + } + + public NetrisRuleAction getAclAction() { + return aclAction; + } + + public List getLbBackends() { + return lbBackends; + } + + public String getReason() { + return reason; + } + + public String getLbCidrList() {return lbCidrList; } + + public String getLbRuleName() { return lbRuleName; } + + public SDNProviderNetworkRule getBaseRule() { + return baseRule; + } + + // Builder class extending the parent builder + public static class Builder { + private SDNProviderNetworkRule baseRule; + private NetrisRuleAction aclAction; + private List lbBackends; + private String reason; + private String lbCidrList; + private String lbRuleName; + + public Builder baseRule(SDNProviderNetworkRule baseRule) { + this.baseRule = baseRule; + return this; + } + + public Builder aclAction(NetrisRuleAction aclAction) { + this.aclAction = aclAction; + return this; + } + + public Builder lbBackends(List lbBackends) { + this.lbBackends = lbBackends; + return this; + } + + public Builder reason(String reason) { + this.reason = reason; + return this; + } + + public Builder lbCidrList(String lbCidrList) { + this.lbCidrList = lbCidrList; + return this; + } + + public Builder lbRuleName(String lbRuleName) { + this.lbRuleName = lbRuleName; + return this; + } + + public NetrisNetworkRule build() { + return new NetrisNetworkRule(this); + } + } +} diff --git a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java new file mode 100644 index 00000000000..fccf2930e97 --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.netris; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface NetrisProvider extends InternalIdentity, Identity { + long getZoneId(); + String getName(); + String getUrl(); + String getUsername(); + String getSiteName(); + String getTenantName(); + String getNetrisTag(); +} diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java new file mode 100644 index 00000000000..110e9f07105 --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java @@ -0,0 +1,310 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.netris; + +import com.cloud.network.IpAddress; +import com.cloud.network.Network; +import com.cloud.network.SDNProviderNetworkRule; +import com.cloud.network.vpc.StaticRoute; +import com.cloud.network.vpc.Vpc; + +import java.util.List; + +/** + * Interface for Netris Services that provides methods to manage VPCs, networks, + * NAT rules, network rules, and static routes in an SDN (Software Defined Networking) environment. + */ + +public interface NetrisService { + + /** + * Creates IPAM (IP Address Management) allocations for zone-level public ranges. + * + * @param zoneId the ID of the zone + * @return true if the operation is successful, false otherwise + */ + boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId); + + /** + * Creates a VPC (Virtual Private Cloud) resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcId the ID of the VPC + * @param vpcName the name of the VPC + * @param sourceNatEnabled true if source NAT is enabled + * @param cidr the CIDR of the VPC + * @param isVpcNetwork true if it is a VPC network + * @return true if the operation is successful, false otherwise + */ + boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork); + + /** + * Updates an existing VPC resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcId the ID of the VPC + * @param vpcName the new name of the VPC + * @param previousVpcName the previous name of the VPC + * @return true if the operation is successful, false otherwise + */ + boolean updateVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, String previousVpcName); + + /** + * Deletes a VPC resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpc the VPC to delete + * @return true if the operation is successful, false otherwise + */ + boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc); + + /** + * Creates a virtual network (vNet) resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param cidr the CIDR of the network + * @param globalRouting true if global routing is enabled + * @return true if the operation is successful, false otherwise + */ + boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr, Boolean globalRouting); + + /** + * Updates an existing vNet resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the new name of the network + * @param networkId the ID of the network + * @param prevNetworkName the previous name of the network + * @return true if the operation is successful, false otherwise + */ + boolean updateVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String prevNetworkName); + + /** + * Deletes an existing vNet resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param cidr the CIDR of the network + * @return true if the operation is successful, false otherwise + */ + boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr); + + /** + * Creates a source NAT rule for a VPC or network. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param sourceNatIp the source NAT IP + * @return true if the operation is successful, false otherwise + */ + boolean createSnatRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, long networkId, boolean isForVpc, String vpcCidr, String sourceNatIp); + + /** + * Creates a port forwarding rule for a VPC or network. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param networkRule the network rule to forward + * @return true if the operation is successful, false otherwise + */ + boolean createPortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule); + + /** + * Deletes a port forwarding rule for a VPC or network. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param networkRule the network rule to remove + * @return true if the operation is successful, false otherwise + */ + boolean deletePortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule); + + /** + * Updates the source NAT IP for a specified VPC. + * + * @param vpc the VPC to updates + * @param address the new source NAT IP address + * @return true if the operation is successful, false otherwise + */ + boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); + + /** + * Creates a static NAT rule for a specific VM. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param staticNatIp the static NAT IP + * @param vmIp the VM's IP address + * @param vmId the ID of the VM + * @return true if the operation is successful, false otherwise + */ + boolean createStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String vpcCidr, String staticNatIp, String vmIp, long vmId); + + /** + * Deletes a static NAT rule for a specific VM. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if the rule applies to a VPC + * @param staticNatIp the static NAT IP + * @param vmId the ID of the VM + * @return true if the operation is successful, false otherwise + */ + boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp, long vmId); + + /** + * Adds firewall rules to a specific network. + * + * @param network the target network + * @param firewallRules the list of firewall rules to add + * @return true if the operation is successful, false otherwise + */ + boolean addFirewallRules(Network network, List firewallRules); + + /** + * Deletes firewall rules from a specific network. + * + * @param network the target network + * @param firewallRules the list of firewall rules to delete + * @return true if the operation is successful, false otherwise + */ + boolean deleteFirewallRules(Network network, List firewallRules); + + /** + * Adds or updates a static route for a specific network or VPC. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if it is for a VPC + * @param prefix the IP prefix of the route + * @param nextHop the next hop address + * @param routeId the ID of the route + * @param updateRoute true if the route should be updated + * @return true if the operation is successful, false otherwise + */ + boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute); + + /** + * Deletes a specific static route for a network or VPC. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if it is for a VPC + * @param prefix the IP prefix of the route + * @param nextHop the next hop address + * @param routeId the ID of the route + * @return true if the operation is successful, false otherwise + */ + boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId); + + /** + * Lists static routes for a specific network or VPC. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if it is for a VPC + * @param prefix the IP prefix of the route + * @param nextHop the next hop address + * @param routeId the ID of the route + * @return a list of static routes + */ + List listStaticRoutes(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId); + + /** + * Releases a NAT IP address. + * + * @param zoneId the ID of the zone + * @param publicIp the public NAT IP to release + * @return true if the operation is successful, false otherwise + */ + boolean releaseNatIp(long zoneId, String publicIp); + + /** + * Creates or updates a load balancer (LB) rule. + * + * @param rule the network rule for the load balancer + * @return true if the operation is successful, false otherwise + */ + boolean createOrUpdateLbRule(NetrisNetworkRule rule); + + /** + * Deletes a load balancer (LB) rule. + * + * @param rule the network rule to delete + * @return true if the operation is successful, false otherwise + */ + boolean deleteLbRule(NetrisNetworkRule rule); +} diff --git a/api/src/main/java/com/cloud/network/nsx/NsxService.java b/api/src/main/java/com/cloud/network/nsx/NsxService.java index 79ad9547c73..1adb7461cc0 100644 --- a/api/src/main/java/com/cloud/network/nsx/NsxService.java +++ b/api/src/main/java/com/cloud/network/nsx/NsxService.java @@ -16,11 +16,23 @@ // 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/RulesService.java b/api/src/main/java/com/cloud/network/rules/RulesService.java index 0b4afeef945..547d4ab51e0 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.exception.ResourceUnavailableException; 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 @@ public interface RulesService { 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/StaticRoute.java b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java index 5707ca14024..739fca328b8 100644 --- a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java +++ b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java @@ -25,6 +25,7 @@ public interface StaticRoute extends ControlledEntity, Identity, InternalIdentit Staged, // route been created but has never got through network rule conflict detection. Routes in this state can not be sent to VPC virtual router. Add, // Add means the route has been created and has gone through network rule conflict detection. Active, // Route has been sent to the VPC router and reported to be active. + Update, Revoke, // Revoke means this route has been revoked. If this route has been sent to the VPC router, the route will be deleted from database. Deleting // rule has been revoked and is scheduled for deletion } @@ -32,7 +33,9 @@ public interface StaticRoute extends ControlledEntity, Identity, InternalIdentit /** * @return */ - long getVpcGatewayId(); + Long getVpcGatewayId(); + + String getNextHop(); /** * @return diff --git a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java index cb4849f1f7b..c8fc073911f 100644 --- a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java +++ b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java @@ -23,7 +23,8 @@ public class StaticRouteProfile implements StaticRoute { private String targetCidr; private long accountId; private long domainId; - private long gatewayId; + private Long gatewayId; + private String nextHop; private StaticRoute.State state; private long vpcId; String vlanTag; @@ -46,6 +47,18 @@ public class StaticRouteProfile implements StaticRoute { ipAddress = gateway.getIp4Address(); } + public StaticRouteProfile(StaticRoute staticRoute) { + id = staticRoute.getId(); + uuid = staticRoute.getUuid(); + targetCidr = staticRoute.getCidr(); + accountId = staticRoute.getAccountId(); + domainId = staticRoute.getDomainId(); + gatewayId = staticRoute.getVpcGatewayId(); + state = staticRoute.getState(); + vpcId = staticRoute.getVpcId(); + gateway = staticRoute.getNextHop(); + } + @Override public long getAccountId() { return accountId; @@ -57,10 +70,15 @@ public class StaticRouteProfile implements StaticRoute { } @Override - public long getVpcGatewayId() { + public Long getVpcGatewayId() { return gatewayId; } + @Override + public String getNextHop() { + return nextHop; + } + @Override public String getCidr() { return targetCidr; diff --git a/api/src/main/java/com/cloud/network/vpc/Vpc.java b/api/src/main/java/com/cloud/network/vpc/Vpc.java index e9a831c9d83..b94089d2d43 100644 --- a/api/src/main/java/com/cloud/network/vpc/Vpc.java +++ b/api/src/main/java/com/cloud/network/vpc/Vpc.java @@ -105,4 +105,6 @@ public interface Vpc extends ControlledEntity, Identity, InternalIdentity { String getIp6Dns1(); String getIp6Dns2(); + + boolean useRouterIpAsResolver(); } diff --git a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java index 3aab57d5d3d..17f49bb3652 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 @@ package com.cloud.network.vpc; import java.util.Date; +import com.cloud.offering.NetworkOffering; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -31,6 +32,8 @@ public interface VpcOffering extends InternalIdentity, Identity { public static final String redundantVPCOfferingName = "Redundant VPC offering"; public static final String DEFAULT_VPC_NAT_NSX_OFFERING_NAME = "VPC offering with NSX - NAT Mode"; public static final String DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME = "VPC offering with NSX - Route Mode"; + public static final String DEFAULT_VPC_ROUTE_NETRIS_OFFERING_NAME = "VPC offering with Netris - Route Mode"; + public static final String DEFAULT_VPC_NAT_NETRIS_OFFERING_NAME = "VPC offering with Netris - NAT Mode"; /** * @@ -55,9 +58,7 @@ public interface VpcOffering extends InternalIdentity, Identity { */ boolean isDefault(); - boolean isForNsx(); - - String getNsxMode(); + NetworkOffering.NetworkMode getNetworkMode(); /** * @return service offering id used by VPC virtual router @@ -79,4 +80,8 @@ public interface VpcOffering extends InternalIdentity, Identity { 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 1ce3cf8ab0e..97b95339ecf 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.CreateVPCOfferingCmd; 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,8 +37,10 @@ public interface VpcProvisioningService { VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders, Map serviceCapabilitystList, NetUtils.InternetProtocol internetProtocol, - Long serviceOfferingId, Boolean forNsx, String mode, - 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 2cdc034a16e..c1546609d2b 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java @@ -48,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 @@ public interface VpcService { */ 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 @@ public interface VpcService { * @param cidr * @return */ - StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException; + StaticRoute createStaticRoute(Long gatewayId, Long vpcId, String nextHop, String cidr) throws NetworkRuleConflictException; /** * Lists static routes based on parameters passed to the call diff --git a/api/src/main/java/com/cloud/network/vpn/RemoteAccessVpnService.java b/api/src/main/java/com/cloud/network/vpn/RemoteAccessVpnService.java index bbb9771d27a..ffa8af4576d 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/DiskOfferingInfo.java b/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java index d83039e15c2..12dcf423e34 100644 --- a/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java +++ b/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java @@ -31,6 +31,13 @@ public class DiskOfferingInfo { _diskOffering = diskOffering; } + public DiskOfferingInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops) { + _diskOffering = diskOffering; + _size = size; + _minIops = minIops; + _maxIops = maxIops; + } + public void setDiskOffering(DiskOffering diskOffering) { _diskOffering = diskOffering; } diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java index cf01fbf30e2..5000a4f8c62 100644 --- a/api/src/main/java/com/cloud/offering/NetworkOffering.java +++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java @@ -43,11 +43,15 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RelatedNetworkOffering, domainid, zoneid, pvlanType, internetProtocol } - public enum NsxMode { + 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"; @@ -60,6 +64,8 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC = "DefaultNATNSXNetworkOfferingForVpc"; public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC_WITH_ILB = "DefaultNATNSXNetworkOfferingForVpcWithInternalLB"; public static final String DEFAULT_ROUTED_NSX_OFFERING_FOR_VPC = "DefaultRoutedNSXNetworkOfferingForVpc"; + public static final String DEFAULT_ROUTED_NETRIS_OFFERING_FOR_VPC = "DefaultRoutedNetrisNetworkOfferingForVpc"; + public static final String DEFAULT_NAT_NETRIS_OFFERING_FOR_VPC = "DefaultNATNetrisNetworkOfferingForVpc"; public static final String DEFAULT_NAT_NSX_OFFERING = "DefaultNATNSXNetworkOffering"; public static final String DEFAULT_ROUTED_NSX_OFFERING = "DefaultRoutedNSXNetworkOffering"; public final static String QuickCloudNoServices = "QuickCloudNoServices"; @@ -98,11 +104,7 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, boolean isForVpc(); - boolean isForTungsten(); - - boolean isForNsx(); - - String getNsxMode(); + NetworkMode getNetworkMode(); TrafficType getTrafficType(); @@ -165,4 +167,8 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, 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 58c7b0dbaf9..532123e4373 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, } @@ -139,4 +142,8 @@ public interface ServiceOffering extends InfrastructureEntity, InternalIdentity, Boolean getDiskOfferingStrictness(); void setDiskOfferingStrictness(boolean diskOfferingStrictness); + + Long getVgpuProfileId(); + + Integer getGpuCount(); } diff --git a/api/src/main/java/com/cloud/org/Cluster.java b/api/src/main/java/com/cloud/org/Cluster.java index 4079c88dfde..b0aa6bb04cf 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 interface Cluster extends Grouping, Partition { 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 ab6e6fb6c5a..3b61367e3b4 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 @@ package com.cloud.region.ha; 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 2757c918ed6..3cdf8fc64e9 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.DeleteClusterCmd; 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 70738c7921b..e91cf820b08 100644 --- a/api/src/main/java/com/cloud/resource/ResourceState.java +++ b/api/src/main/java/com/cloud/resource/ResourceState.java @@ -76,6 +76,10 @@ public enum ResourceState { } } + public static List s_maintenanceStates = List.of(ResourceState.Maintenance, + ResourceState.ErrorInMaintenance, ResourceState.PrepareForMaintenance, + ResourceState.ErrorInPrepareForMaintenance); + public ResourceState getNextState(Event a) { return s_fsm.getNextState(this, a); } @@ -98,8 +102,7 @@ public enum ResourceState { } public static boolean isMaintenanceState(ResourceState state) { - return Arrays.asList(ResourceState.Maintenance, ResourceState.ErrorInMaintenance, - ResourceState.PrepareForMaintenance, ResourceState.ErrorInPrepareForMaintenance).contains(state); + return s_maintenanceStates.contains(state); } public static boolean canAttemptMaintenance(ResourceState state) { diff --git a/api/src/main/java/com/cloud/server/ManagementServerHostStats.java b/api/src/main/java/com/cloud/server/ManagementServerHostStats.java index 1f201d7689f..6eb275031e8 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 18f3e901cd9..f7b2456ce5c 100644 --- a/api/src/main/java/com/cloud/server/ManagementService.java +++ b/api/src/main/java/com/cloud/server/ManagementService.java @@ -25,16 +25,20 @@ import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd; import org.apache.cloudstack.api.command.admin.config.ListCfgGroupsByCmd; import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd; import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd; +import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCategoryCmd; import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.DeleteGuestOsCategoryCmd; import org.apache.cloudstack.api.command.admin.guest.GetHypervisorGuestOsNamesCmd; import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd; import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCategoryCmd; import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsMappingCmd; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; +import org.apache.cloudstack.api.command.admin.management.RemoveManagementServerCmd; import org.apache.cloudstack.api.command.admin.pod.ListPodsByCmd; import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd; @@ -59,8 +63,10 @@ import org.apache.cloudstack.api.command.user.ssh.CreateSSHKeyPairCmd; import org.apache.cloudstack.api.command.user.ssh.DeleteSSHKeyPairCmd; import org.apache.cloudstack.api.command.user.ssh.ListSSHKeyPairsCmd; import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd; +import org.apache.cloudstack.api.command.user.userdata.DeleteCniConfigurationCmd; import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd; import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd; +import org.apache.cloudstack.api.command.user.userdata.RegisterCniConfigurationCmd; import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd; import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd; import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd; @@ -168,6 +174,12 @@ public interface ManagementService { */ Pair, Integer> listGuestOSCategoriesByCriteria(ListGuestOsCategoriesCmd cmd); + GuestOsCategory addGuestOsCategory(AddGuestOsCategoryCmd cmd); + + GuestOsCategory updateGuestOsCategory(UpdateGuestOsCategoryCmd cmd); + + boolean deleteGuestOsCategory(DeleteGuestOsCategoryCmd cmd); + /** * Obtains a list of all guest OS mappings * @@ -360,17 +372,23 @@ public interface ManagementService { * The api command class. * @return The list of userdatas found. */ - Pair, Integer> listUserDatas(ListUserDataCmd cmd); + Pair, Integer> listUserDatas(ListUserDataCmd cmd, boolean forCks); + + /** + * Registers a cni configuration. + * + * @param cmd The api command class. + * @return A VO with the registered user data. + */ + UserData registerCniConfiguration(RegisterCniConfigurationCmd cmd); /** * Registers a userdata. * - * @param cmd - * The api command class. + * @param cmd The api command class. * @return A VO with the registered userdata. */ UserData registerUserData(RegisterUserDataCmd cmd); - /** * Deletes a userdata. * @@ -380,6 +398,14 @@ public interface ManagementService { */ boolean deleteUserData(DeleteUserDataCmd cmd); + /** + * Deletes user data. + * + * @param cmd + * The api command class. + * @return True on success. False otherwise. + */ + boolean deleteCniConfiguration(DeleteCniConfigurationCmd cmd); /** * Search registered key pairs for the logged in user. * @@ -481,4 +507,6 @@ public interface ManagementService { Pair patchSystemVM(PatchSystemVMCmd cmd); + boolean removeManagementServer(RemoveManagementServerCmd cmd); + } diff --git a/api/src/main/java/com/cloud/server/ResourceIconManager.java b/api/src/main/java/com/cloud/server/ResourceIconManager.java index e5111d9160b..d10b3eb0cd5 100644 --- a/api/src/main/java/com/cloud/server/ResourceIconManager.java +++ b/api/src/main/java/com/cloud/server/ResourceIconManager.java @@ -16,7 +16,9 @@ // under the License. package com.cloud.server; +import java.util.Collection; import java.util.List; +import java.util.Map; public interface ResourceIconManager { @@ -25,4 +27,8 @@ public interface ResourceIconManager { boolean deleteResourceIcon(List resourceIds, ResourceTag.ResourceObjectType resourceType); ResourceIcon getByResourceTypeAndUuid(ResourceTag.ResourceObjectType type, String resourceId); + + Map getByResourceTypeAndIds(ResourceTag.ResourceObjectType type, Collection resourceIds); + + Map getByResourceTypeAndUuids(ResourceTag.ResourceObjectType type, Collection resourceUuids); } diff --git a/api/src/main/java/com/cloud/server/ResourceTag.java b/api/src/main/java/com/cloud/server/ResourceTag.java index 9bbb5d43eae..b3026deceff 100644 --- a/api/src/main/java/com/cloud/server/ResourceTag.java +++ b/api/src/main/java/com/cloud/server/ResourceTag.java @@ -66,6 +66,7 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit LBStickinessPolicy(false, true), LBHealthCheckPolicy(false, true), SnapshotPolicy(true, true), + GuestOsCategory(false, false, true), GuestOs(false, true), NetworkOffering(false, true), VpcOffering(true, false), diff --git a/api/src/main/java/com/cloud/storage/GuestOsCategory.java b/api/src/main/java/com/cloud/storage/GuestOsCategory.java index b46418d5c8f..e1ee4489158 100644 --- a/api/src/main/java/com/cloud/storage/GuestOsCategory.java +++ b/api/src/main/java/com/cloud/storage/GuestOsCategory.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.storage; +import java.util.Date; + import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -27,4 +29,7 @@ public interface GuestOsCategory extends Identity, InternalIdentity { void setName(String name); + boolean isFeatured(); + + Date getCreated(); } diff --git a/api/src/main/java/com/cloud/storage/MigrationOptions.java b/api/src/main/java/com/cloud/storage/MigrationOptions.java index a39a2a7c827..8b642d09e29 100644 --- a/api/src/main/java/com/cloud/storage/MigrationOptions.java +++ b/api/src/main/java/com/cloud/storage/MigrationOptions.java @@ -24,6 +24,7 @@ public class MigrationOptions implements Serializable { private String srcPoolUuid; private Storage.StoragePoolType srcPoolType; + private Long srcPoolClusterId; private Type type; private ScopeType scopeType; private String srcBackingFilePath; @@ -38,21 +39,23 @@ public class MigrationOptions implements Serializable { public MigrationOptions() { } - public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate, ScopeType scopeType) { + public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate, ScopeType scopeType, Long srcPoolClusterId) { this.srcPoolUuid = srcPoolUuid; this.srcPoolType = srcPoolType; this.type = Type.LinkedClone; this.scopeType = scopeType; this.srcBackingFilePath = srcBackingFilePath; this.copySrcTemplate = copySrcTemplate; + this.srcPoolClusterId = srcPoolClusterId; } - public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid, ScopeType scopeType) { + public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid, ScopeType scopeType, Long srcPoolClusterId) { this.srcPoolUuid = srcPoolUuid; this.srcPoolType = srcPoolType; this.type = Type.FullClone; this.scopeType = scopeType; this.srcVolumeUuid = srcVolumeUuid; + this.srcPoolClusterId = srcPoolClusterId; } public String getSrcPoolUuid() { @@ -63,6 +66,10 @@ public class MigrationOptions implements Serializable { return srcPoolType; } + public Long getSrcPoolClusterId() { + return srcPoolClusterId; + } + public ScopeType getScopeType() { return scopeType; } public String getSrcBackingFilePath() { diff --git a/api/src/main/java/com/cloud/storage/Snapshot.java b/api/src/main/java/com/cloud/storage/Snapshot.java index fc919e442b2..c0a7b812ed9 100644 --- a/api/src/main/java/com/cloud/storage/Snapshot.java +++ b/api/src/main/java/com/cloud/storage/Snapshot.java @@ -48,7 +48,7 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, } public enum State { - Allocated, Creating, CreatedOnPrimary, BackingUp, BackedUp, Copying, Destroying, Destroyed, + Allocated, Creating, CreatedOnPrimary, BackingUp, BackedUp, Copying, Destroying, Destroyed, Hidden, //it's a state, user can't see the snapshot from ui, while the snapshot may still exist on the storage Error; diff --git a/api/src/main/java/com/cloud/storage/Storage.java b/api/src/main/java/com/cloud/storage/Storage.java index c997f5e1dbf..1ad3731b9ea 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 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; + +import java.util.ArrayList; +import java.util.List; public class Storage { public static enum ImageFormat { @@ -34,6 +30,7 @@ public class Storage { OVA(true, true, true, "ova"), VHDX(true, true, true, "vhdx"), BAREMETAL(false, false, false, "BAREMETAL"), + EXTERNAL(false, false, false, "EXTERNAL"), VMDK(true, true, false, "vmdk"), VDI(true, true, false, "vdi"), TAR(false, false, false, "tar"), @@ -139,6 +136,21 @@ public class Storage { 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 class Storage { * 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 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-) - 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; 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,50 +204,12 @@ public class Storage { } public boolean supportsEncryption() { + return encryption == EncryptionSupport.Hypervisor || encryption == EncryptionSupport.Storage; + } + + public EncryptionSupport encryptionSupportMode() { 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; - } - - @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 static List getNonSharedStoragePoolTypes() { diff --git a/api/src/main/java/com/cloud/storage/StorageService.java b/api/src/main/java/com/cloud/storage/StorageService.java index c3609cfd8ee..a29c8f6aece 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.net.UnknownHostException; 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.DeletePoolCmd; 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 a474b23489c..502e2aaae40 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 59d203ac73a..4e696e877cc 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 interface Upload extends InternalIdentity, Identity { } 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 f43d5331222..db702a61f2b 100644 --- a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java +++ b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java @@ -17,6 +17,7 @@ package com.cloud.storage; import java.util.Date; +import java.util.List; import org.apache.cloudstack.api.InternalIdentity; @@ -25,6 +26,8 @@ public interface VMTemplateStorageResourceAssoc extends InternalIdentity { UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED } + List PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS); + String getInstallPath(); long getTemplateId(); diff --git a/api/src/main/java/com/cloud/storage/Volume.java b/api/src/main/java/com/cloud/storage/Volume.java index 40c5660b2df..c7fbdb0a544 100644 --- a/api/src/main/java/com/cloud/storage/Volume.java +++ b/api/src/main/java/com/cloud/storage/Volume.java @@ -271,11 +271,13 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba void setExternalUuid(String externalUuid); - public Long getPassphraseId(); + Long getPassphraseId(); - public void setPassphraseId(Long id); + void setPassphraseId(Long id); - public String getEncryptFormat(); + String getEncryptFormat(); - public void setEncryptFormat(String encryptFormat); + 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 4f09702b7db..dd7341da1b5 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -22,7 +22,12 @@ import java.net.MalformedURLException; import java.util.List; import java.util.Map; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.offering.DiskOffering; +import com.cloud.user.Account; import com.cloud.utils.Pair; +import com.cloud.utils.fsm.NoTransitionException; + import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd; @@ -37,13 +42,9 @@ import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; import org.apache.cloudstack.api.response.GetUploadParamsResponse; import org.apache.cloudstack.framework.config.ConfigKey; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.user.Account; -import com.cloud.utils.fsm.NoTransitionException; - public interface VolumeApiService { - ConfigKey ConcurrentMigrationsThresholdPerDatastore = new ConfigKey("Advanced" + ConfigKey ConcurrentMigrationsThresholdPerDatastore = new ConfigKey<>("Advanced" , Long.class , "concurrent.migrations.per.target.datastore" , "0" @@ -51,7 +52,7 @@ public interface VolumeApiService { , true // not sure if this is to be dynamic , ConfigKey.Scope.Global); - ConfigKey UseHttpsToUpload = new ConfigKey("Advanced", + ConfigKey UseHttpsToUpload = new ConfigKey<>("Advanced", Boolean.class, "use.https.to.upload", "true", @@ -85,7 +86,7 @@ public interface VolumeApiService { * @param cmd * the API command wrapping the criteria * @return the volume object - * @throws ResourceAllocationException + * @throws ResourceAllocationException no capacity to allocate the new volume size */ Volume resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationException; @@ -102,8 +103,12 @@ 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); @@ -113,7 +118,9 @@ public interface VolumeApiService { Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List zoneIds) 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. @@ -130,16 +137,16 @@ public interface VolumeApiService { void updateDisplay(Volume volume, Boolean displayVolume); - Snapshot allocSnapshotForVm(Long vmId, Long volumeId, String snapshotName) throws ResourceAllocationException; + Snapshot allocSnapshotForVm(Long vmId, Long volumeId, String snapshotName, Long vmSnapshotId) throws ResourceAllocationException; /** - * Checks if the target storage supports the disk offering. + * Checks if the storage pool supports the disk offering tags. * This validation is consistent with the mechanism used to select a storage pool to deploy a volume when a virtual machine is deployed or when a data disk is allocated. * * The scenarios when this method returns true or false is presented in the following table. * * - * + * * * * @@ -163,7 +170,15 @@ public interface VolumeApiService { * *
#Disk offering tagsStorage tagsDoes the storage support the disk offering?#Disk offering diskOfferingTagsStorage diskOfferingTagsDoes the storage support the disk offering?
*/ - boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String diskOfferingTags); + boolean doesStoragePoolSupportDiskOffering(StoragePool destPool, DiskOffering diskOffering); + + /** + * Checks if the storage pool supports the required disk offering tags + * destPool the storage pool to check the disk offering tags + * diskOfferingTags the tags that should be supported + * return whether the tags are supported in the storage pool + */ + boolean doesStoragePoolSupportDiskOfferingTags(StoragePool destPool, String diskOfferingTags); Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge); @@ -184,4 +199,6 @@ public interface VolumeApiService { 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 0893f337ce2..67afd6aa4e2 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 java.util.List; 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; @@ -106,6 +107,16 @@ public interface SnapshotApiService { */ 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/template/TemplateApiService.java b/api/src/main/java/com/cloud/template/TemplateApiService.java index 5b494c308c3..6138f24c92b 100644 --- a/api/src/main/java/com/cloud/template/TemplateApiService.java +++ b/api/src/main/java/com/cloud/template/TemplateApiService.java @@ -58,10 +58,23 @@ public interface TemplateApiService { VirtualMachineTemplate prepareTemplate(long templateId, long zoneId, Long storageId); + /** + * Detach ISO from VM + * @param vmId id of the VM + * @param isoId id of the ISO (when passed). If it is not passed, it will get it from user_vm table + * @param extraParams forced, isVirtualRouter + * @return true when operation succeeds, false if not + */ + boolean detachIso(long vmId, Long isoId, Boolean... extraParams); - boolean detachIso(long vmId, boolean forced); - - boolean attachIso(long isoId, long vmId, boolean forced); + /** + * Attach ISO to a VM + * @param isoId id of the ISO to attach + * @param vmId id of the VM to attach the ISO to + * @param extraParams: forced, isVirtualRouter + * @return true when operation succeeds, false if not + */ + boolean attachIso(long isoId, long vmId, Boolean... extraParams); /** * Deletes a template diff --git a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java index 1f8cef0365b..b8c646048b9 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 @@ package com.cloud.template; 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 interface VirtualMachineTemplate extends ControlledEntity, Identity, Inte boolean isDeployAsIs(); + boolean isForCks(); + Long getUserDataId(); UserData.UserDataOverridePolicy getUserDataOverridePolicy(); + CPU.CPUArch getArch(); + + Long getExtensionId(); + } diff --git a/api/src/main/java/com/cloud/user/Account.java b/api/src/main/java/com/cloud/user/Account.java index bb9838f137a..1cc5eae64a6 100644 --- a/api/src/main/java/com/cloud/user/Account.java +++ b/api/src/main/java/com/cloud/user/Account.java @@ -71,6 +71,7 @@ public interface Account extends ControlledEntity, InternalIdentity, Identity { } public static final long ACCOUNT_ID_SYSTEM = 1; + public static final long ACCOUNT_ID_ADMIN = 2; public String getAccountName(); @@ -93,4 +94,8 @@ public interface Account extends ControlledEntity, InternalIdentity, Identity { 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 63f5455cfd0..c0ebcf09f59 100644 --- a/api/src/main/java/com/cloud/user/AccountService.java +++ b/api/src/main/java/com/cloud/user/AccountService.java @@ -19,6 +19,7 @@ package com.cloud.user; import java.util.List; import java.util.Map; +import com.cloud.utils.Pair; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -86,6 +87,8 @@ public interface AccountService { boolean isDomainAdmin(Long accountId); + boolean isResourceDomainAdmin(Long accountId); + boolean isNormalUser(long accountId); User getActiveUserByRegistrationToken(String registrationToken); @@ -116,6 +119,8 @@ public interface AccountService { void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, ControlledEntity... entities) throws PermissionDeniedException; + void validateAccountHasAccessToResource(Account account, AccessType accessType, Object resource); + Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly); /** @@ -125,9 +130,9 @@ public interface AccountService { */ UserAccount getUserAccountById(Long userId); - public Map getKeys(GetUserKeysCmd cmd); + public Pair> getKeys(GetUserKeysCmd cmd); - public Map getKeys(Long userId); + public Pair> getKeys(Long userId); /** * Lists user two-factor authentication provider plugins diff --git a/api/src/main/java/com/cloud/user/ResourceLimitService.java b/api/src/main/java/com/cloud/user/ResourceLimitService.java index 04560df428f..49b20fe2fef 100644 --- a/api/src/main/java/com/cloud/user/ResourceLimitService.java +++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java @@ -38,13 +38,26 @@ public interface ResourceLimitService { 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", true); + "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); + 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); /** @@ -239,13 +252,37 @@ public interface ResourceLimitService { 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); @@ -253,4 +290,8 @@ public interface ResourceLimitService { void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); + void checkVmGpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu) throws ResourceAllocationException; + void incrementVmGpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu); + void decrementVmGpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu); + } diff --git a/api/src/main/java/com/cloud/user/User.java b/api/src/main/java/com/cloud/user/User.java index 422e264f10b..041b39ad272 100644 --- a/api/src/main/java/com/cloud/user/User.java +++ b/api/src/main/java/com/cloud/user/User.java @@ -94,4 +94,9 @@ public interface User extends OwnedBy, InternalIdentity { public boolean isUser2faEnabled(); public String getKeyFor2fa(); + + public void setApiKeyAccess(Boolean apiKeyAccess); + + public Boolean getApiKeyAccess(); + } diff --git a/api/src/main/java/com/cloud/user/UserData.java b/api/src/main/java/com/cloud/user/UserData.java index fa0c50473c0..13a3c74f367 100644 --- a/api/src/main/java/com/cloud/user/UserData.java +++ b/api/src/main/java/com/cloud/user/UserData.java @@ -29,4 +29,5 @@ public interface UserData extends ControlledEntity, InternalIdentity, Identity { String getUserData(); String getParams(); + boolean isForCks(); } diff --git a/api/src/main/java/com/cloud/uservm/UserVm.java b/api/src/main/java/com/cloud/uservm/UserVm.java index e30f5e03054..9035d2903c9 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 d3c1daa1f5d..a0c80ceb1bf 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 class NicProfile implements InternalIdentity, Serializable { this.mtu = mtu; } + public boolean getIpv4AllocationRaceCheck() { + return this.ipv4AllocationRaceCheck; + } + + public void setIpv4AllocationRaceCheck(boolean ipv4AllocationRaceCheck) { + this.ipv4AllocationRaceCheck = ipv4AllocationRaceCheck; + } // // OTHER METHODS @@ -442,6 +450,9 @@ public class NicProfile implements InternalIdentity, Serializable { @Override public String toString() { - return String.format("NicProfile %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "vmId", "deviceId", "broadcastUri", "reservationId", "iPv4Address")); + 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 787ed7bde37..6f1aba4613d 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -16,14 +16,18 @@ // under the License. package com.cloud.vm; +import com.cloud.storage.Snapshot; +import com.cloud.storage.Volume; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.cloud.deploy.DeploymentPlan; import org.apache.cloudstack.api.BaseCmd.HTTPMethod; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd; +import org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; import org.apache.cloudstack.api.command.user.vm.RebootVMCmd; @@ -42,9 +46,11 @@ import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; 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; @@ -66,10 +72,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 +113,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 +157,6 @@ public interface UserVmService { * 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 +221,17 @@ public interface UserVmService { * available. */ UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List securityGroupIdList, - Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, + Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List sshKeyPairs, Map requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameter, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId) throws InsufficientCapacityException, + Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** * 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 +298,15 @@ public interface UserVmService { * available. */ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, - List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, + List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List sshKeyPairs, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, - Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; + Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** * Creates a User VM in Advanced Zone (Security Group feature is disabled) * 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 +370,10 @@ public interface UserVmService { * available. */ UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, Account owner, - String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, + String hostName, String displayName, Long diskOfferingId, Long diskSize, List dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List sshKeyPairs, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId) + Map templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; @@ -483,14 +473,14 @@ public interface UserVmService { 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, Long rootDiskOfferingId, boolean expunge, Map details) throws InsufficientCapacityException, ResourceUnavailableException; @@ -527,4 +517,8 @@ public interface UserVmService { * @return true if the VM is successfully unmanaged, false if not. */ boolean unmanageUserVM(Long vmId); + + UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws InsufficientCapacityException, ResourceAllocationException, ResourceUnavailableException; + + UserVm restoreVMFromBackup(CreateVMFromBackupCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException; } diff --git a/api/src/main/java/com/cloud/vm/VirtualMachine.java b/api/src/main/java/com/cloud/vm/VirtualMachine.java index e7c5efb773b..d244de7115e 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachine.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachine.java @@ -128,7 +128,6 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Partition, s_fsm.addTransition(new Transition(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging, null)); s_fsm.addTransition(new Transition(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging, null)); s_fsm.addTransition(new Transition(State.Stopped, Event.RestoringRequested, State.Restoring, null)); - s_fsm.addTransition(new Transition(State.Expunging, Event.RestoringRequested, State.Restoring, null)); s_fsm.addTransition(new Transition(State.Destroyed, Event.RestoringRequested, State.Restoring, null)); s_fsm.addTransition(new Transition(State.Restoring, Event.RestoringSuccess, State.Stopped, null)); s_fsm.addTransition(new Transition(State.Restoring, Event.RestoringFailed, State.Stopped, null)); @@ -333,6 +332,8 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Partition, */ 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 f2ff3da8449..c67ee4eabc2 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java @@ -192,6 +192,10 @@ public interface VirtualMachineProfile { 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 9338cc11cd4..ea5d209a5d4 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 @@ package com.cloud.vm; 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"; @@ -73,6 +74,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 +89,9 @@ public interface VmDetailConstants { String DEPLOY_AS_IS_CONFIGURATION = "configurationId"; String KEY_PAIR_NAMES = "keypairnames"; String CKS_CONTROL_NODE_LOGIN_USER = "controlNodeLoginUser"; + String CKS_NODE_TYPE = "node"; + String OFFERING = "offering"; + String TEMPLATE = "template"; // VMware to KVM VM migrations specific String VMWARE_TO_KVM_PREFIX = "vmware-to-kvm"; @@ -99,4 +104,25 @@ public interface VmDetailConstants { String VMWARE_HOST_NAME = String.format("%s-host", VMWARE_TO_KVM_PREFIX); String VMWARE_DISK = String.format("%s-disk", VMWARE_TO_KVM_PREFIX); String VMWARE_MAC_ADDRESSES = String.format("%s-mac-addresses", VMWARE_TO_KVM_PREFIX); + + // TPM + String VIRTUAL_TPM_ENABLED = "virtual.tpm.enabled"; + String VIRTUAL_TPM_MODEL = "virtual.tpm.model"; + String VIRTUAL_TPM_VERSION = "virtual.tpm.version"; + + // CPU mode and model, ADMIN only + String GUEST_CPU_MODE = "guest.cpu.mode"; + String GUEST_CPU_MODEL = "guest.cpu.model"; + + // Lease related + String INSTANCE_LEASE_EXPIRY_DATE = "leaseexpirydate"; + String INSTANCE_LEASE_EXPIRY_ACTION = "leaseexpiryaction"; + String INSTANCE_LEASE_EXECUTION = "leaseactionexecution"; + + // External orchestrator related + String MAC_ADDRESS = "mac_address"; + String EXPUNGE_EXTERNAL_VM = "expunge.external.vm"; + String EXTERNAL_DETAIL_PREFIX = "External:"; + String CLOUDSTACK_VM_DETAILS = "cloudstack.vm.details"; + String CLOUDSTACK_VLAN = "cloudstack.vlan"; } diff --git a/api/src/main/java/com/cloud/vm/VmDiskInfo.java b/api/src/main/java/com/cloud/vm/VmDiskInfo.java new file mode 100644 index 00000000000..b8779a8d77c --- /dev/null +++ b/api/src/main/java/com/cloud/vm/VmDiskInfo.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.vm; + +import com.cloud.offering.DiskOffering; +import com.cloud.offering.DiskOfferingInfo; + +public class VmDiskInfo extends DiskOfferingInfo { + private Long _deviceId; + + public VmDiskInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops) { + super(diskOffering, size, minIops, maxIops); + } + + public VmDiskInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops, Long deviceId) { + super(diskOffering, size, minIops, maxIops); + _deviceId = deviceId; + } + + public Long getDeviceId() { + return _deviceId; + } +} diff --git a/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java b/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java index 3897df2d5e6..24e93af1562 100644 --- a/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java +++ b/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java @@ -31,7 +31,8 @@ public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity enum State { Allocated("The VM snapshot is allocated but has not been created yet."), Creating("The VM snapshot is being created."), Ready( "The VM snapshot is ready to be used."), Reverting("The VM snapshot is being used to revert"), Expunging("The volume is being expunging"), Removed( - "The volume is destroyed, and can't be recovered."), Error("The volume is in error state, and can't be recovered"); + "The volume is destroyed, and can't be recovered."), Error("The volume is in error state, and can't be recovered"), + Hidden("The VM snapshot is hidden from the user and cannot be recovered."); String _description; @@ -60,6 +61,8 @@ public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity s_fsm.addTransition(Expunging, Event.ExpungeRequested, Expunging); s_fsm.addTransition(Expunging, Event.OperationSucceeded, Removed); s_fsm.addTransition(Expunging, Event.OperationFailed, Error); + s_fsm.addTransition(Expunging, Event.Hide, Hidden); + s_fsm.addTransition(Hidden, Event.ExpungeRequested, Expunging); } } @@ -68,7 +71,7 @@ public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity } enum Event { - CreateRequested, OperationFailed, OperationSucceeded, RevertRequested, ExpungeRequested, + CreateRequested, OperationFailed, OperationSucceeded, RevertRequested, ExpungeRequested, Hide, } @Override diff --git a/api/src/main/java/org/apache/cloudstack/acl/Role.java b/api/src/main/java/org/apache/cloudstack/acl/Role.java index 5e5ffd583d8..ce4166ef4c8 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.Identity; 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/RoleService.java b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java index 07f62a7e7f8..f041c8342ae 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(); /** @@ -54,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); @@ -76,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. @@ -84,14 +93,14 @@ 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); diff --git a/api/src/main/java/org/apache/cloudstack/acl/RoleType.java b/api/src/main/java/org/apache/cloudstack/acl/RoleType.java index 005d47c85bc..46e4f1bc510 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/RoleType.java +++ b/api/src/main/java/org/apache/cloudstack/acl/RoleType.java @@ -23,8 +23,11 @@ import com.google.common.base.Enums; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import java.util.Collection; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; +import java.util.Set; // Enum for default roles in CloudStack public enum RoleType { @@ -100,6 +103,30 @@ public enum RoleType { return roleId; } + public static int toCombinedMask(Collection roles) { + int combinedMask = 0; + if (roles != null) { + for (RoleType role : roles) { + combinedMask |= role.getMask(); + } + } + return combinedMask; + } + + public static Set fromCombinedMask(int combinedMask) { + Set roles = EnumSet.noneOf(RoleType.class); + for (RoleType roleType : RoleType.values()) { + if ((combinedMask & roleType.getMask()) != 0) { + roles.add(roleType); + } + } + if (roles.isEmpty()) { + roles.add(Unknown); + } + return roles; + } + + /** * This method returns the role account type if the role isn't null, else it returns the default account type. * */ diff --git a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java index 22842b834fe..69f391a5656 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.ApiConstants; 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; @@ -56,6 +57,10 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledVie @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") private String projectId; @@ -72,6 +77,10 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledVie @Param(description = "virtual machine 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 class AffinityGroupResponse extends BaseResponse implements ControlledVie 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 class AffinityGroupResponse extends BaseResponse implements ControlledVie 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/alert/AlertService.java b/api/src/main/java/org/apache/cloudstack/alert/AlertService.java index 1250284b5c2..d8e471756a0 100644 --- a/api/src/main/java/org/apache/cloudstack/alert/AlertService.java +++ b/api/src/main/java/org/apache/cloudstack/alert/AlertService.java @@ -73,6 +73,9 @@ public interface AlertService { public static final AlertType ALERT_TYPE_VM_SNAPSHOT = new AlertType((short)32, "ALERT.VM.SNAPSHOT", true); public static final AlertType ALERT_TYPE_VR_PUBLIC_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PUBLIC.IFACE.MTU", true); public static final AlertType ALERT_TYPE_VR_PRIVATE_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PRIVATE.IFACE.MTU", true); + public static final AlertType ALERT_TYPE_EXTENSION_PATH_NOT_READY = new AlertType((short)33, "ALERT.TYPE.EXTENSION.PATH.NOT.READY", true); + public static final AlertType ALERT_TYPE_BACKUP_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_BACKUP_STORAGE, "ALERT.STORAGE.BACKUP", true); + public static final AlertType ALERT_TYPE_OBJECT_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_OBJECT_STORAGE, "ALERT.STORAGE.OBJECT", true); public short getType() { return type; diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiArgValidator.java b/api/src/main/java/org/apache/cloudstack/api/ApiArgValidator.java index 3e06fc0e44e..38047235273 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 aafc039b36b..4d33ba859a5 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), @@ -81,15 +84,25 @@ public enum ApiCommandResourceType { ManagementServer(org.apache.cloudstack.management.ManagementServerHost.class), ObjectStore(org.apache.cloudstack.storage.object.ObjectStore.class), Bucket(org.apache.cloudstack.storage.object.Bucket.class), - QuotaTariff(org.apache.cloudstack.quota.QuotaTariff.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; } @@ -119,4 +132,8 @@ public enum ApiCommandResourceType { } 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 f10769d7319..3b36ce9100e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -29,10 +29,21 @@ public class ApiConstants { 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 ASYNC_BACKUP = "asyncbackup"; public static final String AUTO_SELECT = "autoselect"; public static final String USER_API_KEY = "userapikey"; @@ -42,14 +53,25 @@ 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 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"; @@ -72,9 +94,11 @@ 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 COMBINED_CAPACITY_ORDERING = "COMBINED"; public static final String CONTROLLER = "controller"; public static final String CONTROLLER_UNIT = "controllerunit"; 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 +111,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,7 +125,12 @@ public class ApiConstants { public static final String CN = "cn"; public static final String COMMAND = "command"; public static final String CMD_EVENT_TYPE = "cmdeventtype"; + public static final String CNI_CONFIG = "cniconfig"; + public static final String CNI_CONFIG_ID = "cniconfigurationid"; + public static final String CNI_CONFIG_DETAILS = "cniconfigdetails"; + public static final String CNI_CONFIG_NAME = "cniconfigname"; public static final String COMPONENT = "component"; + public static final String CPU = "CPU"; public static final String CPU_CORE_PER_SOCKET = "cpucorepersocket"; public static final String CPU_NUMBER = "cpunumber"; public static final String CPU_SPEED = "cpuspeed"; @@ -113,27 +144,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,11 +208,15 @@ 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 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_DHCP_OPTION = "extradhcpoption"; @@ -182,15 +225,26 @@ public class ApiConstants { public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue"; public static final String EXTERNAL = "external"; public static final String EXTERNAL_UUID = "externaluuid"; + public static final String EXTERNAL_DETAILS = "externaldetails"; + public static final String PARAMETERS = "parameters"; + public static final String EXTENSION = "extension"; + public static final String EXTENSION_ID = "extensionid"; + public static final String EXTENSION_NAME = "extensionname"; + public static final String EXTENSIONS_PATH = "extensionspath"; public static final String FENCE = "fence"; public static final String FETCH_LATEST = "fetchlatest"; + public static final String FILESYSTEM = "filesystem"; 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_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 +261,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"; @@ -222,6 +277,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 +288,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 +309,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 +323,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,32 +343,45 @@ 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 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 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"; @@ -311,12 +389,15 @@ public class ApiConstants { public static final String NETMASK = "netmask"; public static final String NEW_NAME = "newname"; public static final String NIC = "nic"; + public static final String NICS = "nics"; public static final String NIC_NETWORK_LIST = "nicnetworklist"; public static final String NIC_IP_ADDRESS_LIST = "nicipaddresslist"; public static final String NIC_MULTIQUEUE_NUMBER = "nicmultiqueuenumber"; public static final String NIC_PACKED_VIRTQUEUES_ENABLED = "nicpackedvirtqueuesenabled"; public static final String NEW_START_IP = "newstartip"; public static final String NEW_END_IP = "newendip"; + public static final String KUBERNETES_NODE_VERSION = "kubernetesnodeversion"; + public static final String NUMA_NODE = "numanode"; public static final String NUM_RETRIES = "numretries"; public static final String OFFER_HA = "offerha"; public static final String OS_DISTRIBUTION = "osdistribution"; @@ -333,6 +414,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"; @@ -344,14 +432,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"; @@ -364,6 +465,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"; @@ -379,11 +482,15 @@ 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 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"; @@ -395,13 +502,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 SEARCH_BASE = "searchbase"; + public static final String SECONDARY_IP = "secondaryip"; + public static final String SECRET_KEY = "secretkey"; public static final String SECURITY_GROUP_IDS = "securitygroupids"; public static final String SECURITY_GROUP_NAMES = "securitygroupnames"; public static final String SECURITY_GROUP_NAME = "securitygroupname"; @@ -411,6 +518,7 @@ 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"; @@ -419,15 +527,19 @@ public class ApiConstants { public static final String SHOW_UNIQUE = "showunique"; 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 SOURCE_CIDR_LIST = "sourcecidrlist"; public static final String SOURCE_ZONE_ID = "sourcezoneid"; - public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine"; - public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot"; + 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"; @@ -440,21 +552,33 @@ 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 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"; @@ -462,7 +586,9 @@ public class ApiConstants { public static final String TRUST_STORE_PASSWORD = "truststorepass"; public static final String URL = "url"; public static final String USAGE_INTERFACE = "usageinterface"; + public static final String USED = "used"; public static final String USED_SUBNETS = "usedsubnets"; + public static final String USED_IOPS = "usediops"; public static final String USER_DATA = "userdata"; public static final String USER_DATA_NAME = "userdataname"; @@ -478,15 +604,25 @@ public class ApiConstants { public static final String USERNAME = "username"; public static final String USER_CONFIGURABLE = "userconfigurable"; public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist"; + public static final String USER_SECRET_KEY = "usersecretkey"; public static final String USE_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"; @@ -494,16 +630,25 @@ 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"; @@ -527,6 +672,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"; @@ -557,6 +703,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"; @@ -568,13 +716,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"; @@ -665,6 +814,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"; @@ -677,8 +828,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"; @@ -811,6 +966,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"; @@ -834,6 +991,7 @@ public class ApiConstants { public static final String SPLIT_CONNECTIONS = "splitconnections"; public static final String FOR_VPC = "forvpc"; public static final String FOR_NSX = "fornsx"; + public static final String FOR_CKS = "forcks"; public static final String NSX_SUPPORT_LB = "nsxsupportlb"; public static final String NSX_SUPPORTS_INTERNAL_LB = "nsxsupportsinternallb"; public static final String FOR_TUNGSTEN = "fortungsten"; @@ -938,7 +1096,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"; @@ -951,6 +1111,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"; @@ -986,6 +1147,7 @@ public class ApiConstants { public static final String OVM3_CLUSTER = "ovm3cluster"; public static final String OVM3_VIP = "ovm3vip"; public static final String CLEAN_UP_DETAILS = "cleanupdetails"; + public static final String CLEAN_UP_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"; @@ -1040,6 +1202,10 @@ public class ApiConstants { public static final String MASTER_NODES = "masternodes"; public static final String NODE_IDS = "nodeids"; public static final String CONTROL_NODES = "controlnodes"; + public static final String ETCD_NODES = "etcdnodes"; + public static final String EXTERNAL_NODES = "externalnodes"; + public static final String IS_EXTERNAL_NODE = "isexternalnode"; + public static final String IS_ETCD_NODE = "isetcdnode"; public static final String MIN_SEMANTIC_VERSION = "minimumsemanticversion"; public static final String MIN_KUBERNETES_VERSION_ID = "minimumkubernetesversionid"; public static final String NODE_ROOT_DISK_SIZE = "noderootdisksize"; @@ -1048,6 +1214,8 @@ public class ApiConstants { public static final String AUTOSCALING_ENABLED = "autoscalingenabled"; public static final String MIN_SIZE = "minsize"; public static final String MAX_SIZE = "maxsize"; + public static final String NODE_TYPE_OFFERING_MAP = "nodeofferings"; + public static final String NODE_TYPE_TEMPLATE_MAP = "nodetemplates"; public static final String BOOT_TYPE = "boottype"; public static final String BOOT_MODE = "bootmode"; @@ -1074,16 +1242,19 @@ public class ApiConstants { 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"; @@ -1094,10 +1265,12 @@ public class ApiConstants { public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid"; public static final String HAS_RULES = "hasrules"; public static final String NSX_DETAIL_KEY = "forNsx"; + public static final String NETRIS_DETAIL_KEY = "forNetris"; + public static final String NETRIS_TAG = "netristag"; + public static final String NETRIS_VXLAN_ID = "netrisvxlanid"; public static final String 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"; @@ -1116,6 +1289,60 @@ 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"; + /** * 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). @@ -1167,4 +1394,34 @@ public class ApiConstants { 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 d4fdeddc9a9..616c37484d8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java @@ -22,6 +22,7 @@ package org.apache.cloudstack.api; */ public enum ApiErrorCode { + BAD_REQUEST(400), UNAUTHORIZED(401), UNAUTHORIZED2FA(511), METHOD_NOT_ALLOWED(405), @@ -30,6 +31,7 @@ public enum ApiErrorCode { UNSUPPORTED_ACTION_ERROR(432), API_LIMIT_EXCEED(429), + SERVICE_UNAVAILABLE(503), INTERNAL_ERROR(530), ACCOUNT_ERROR(531), ACCOUNT_RESOURCE_LIMIT_ERROR(532), diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java b/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java index 54fda7e36b8..cb75939d6bc 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 java.util.Map; 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,10 @@ public interface ApiServerService { 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); + + boolean isPostRequestsAndTimestampsEnforced(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java index b206cd011c1..8f47d51b19d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java @@ -31,6 +31,7 @@ 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; @@ -38,6 +39,8 @@ import org.apache.cloudstack.affinity.AffinityGroupService; import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gpu.GpuService; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService; import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService; import org.apache.cloudstack.query.QueryService; @@ -92,6 +95,7 @@ import com.cloud.utils.ReflectUtil; import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.UUIDManager; import com.cloud.vm.UserVmService; +import com.cloud.vm.VmDetailConstants; import com.cloud.vm.snapshot.VMSnapshotService; public abstract class BaseCmd { @@ -128,6 +132,8 @@ public abstract class BaseCmd { @Inject public UserVmService _userVmService; @Inject + public GpuService gpuService; + @Inject public ManagementService _mgr; @Inject public StorageService _storageService; @@ -217,7 +223,11 @@ public abstract class BaseCmd { 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; @@ -478,4 +488,14 @@ public abstract class BaseCmd { } return detailsMap; } + + public Map convertExternalDetailsToMap(Map externalDetails) { + Map customparameterMap = convertDetailsToMap(externalDetails); + Map details = new HashMap<>(); + for (String key : customparameterMap.keySet()) { + String value = customparameterMap.get(key); + details.put(VmDetailConstants.EXTERNAL_DETAIL_PREFIX + key, value); + } + return details; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java index e3aead6881b..8489bf05ec7 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; @@ -49,6 +51,10 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd { description = "the ID of the OS type that best represents the OS of this image.") private Long osTypeId; + @Parameter(name = ApiConstants.FORCE_UPDATE_OS_TYPE, type = CommandType.BOOLEAN, since = "4.21", description = "Force OS type update. Warning: Updating OS type will " + + "update the guest OS configuration for all the existing Instances deployed with this template/iso, which may affect their behavior.") + private Boolean forceUpdateOsType; + @Parameter(name = ApiConstants.FORMAT, type = CommandType.STRING, description = "the format for the image") private String format; @@ -77,6 +83,11 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd { 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", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -105,6 +116,10 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd { return osTypeId; } + public Boolean getForceUpdateOsType() { + return forceUpdateOsType; + } + public Boolean getPasswordEnabled() { return passwordEnabled; } @@ -141,4 +156,11 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd { public boolean isCleanupDetails(){ return cleanupDetails == null ? false : cleanupDetails.booleanValue(); } + + 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/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index ef759aaf9c3..51918570078 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,14 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.cloudstack.storage.object.Bucket; 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,7 +37,7 @@ import org.apache.cloudstack.api.response.AutoScalePolicyResponse; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; import org.apache.cloudstack.api.response.BackupOfferingResponse; -import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.BackupRepositoryResponse; import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.BucketResponse; import org.apache.cloudstack.api.response.CapacityResponse; @@ -57,20 +58,22 @@ import org.apache.cloudstack.api.response.ExtractResponse; import org.apache.cloudstack.api.response.FirewallResponse; import org.apache.cloudstack.api.response.FirewallRuleResponse; import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.GuestOsMappingResponse; import org.apache.cloudstack.api.response.GuestVlanRangeResponse; import org.apache.cloudstack.api.response.GuestVlanResponse; +import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.HostForMigrationResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; import org.apache.cloudstack.api.response.HypervisorGuestOsNamesResponse; import org.apache.cloudstack.api.response.IPAddressResponse; -import org.apache.cloudstack.api.response.IpQuarantineResponse; import org.apache.cloudstack.api.response.ImageStoreResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse; import org.apache.cloudstack.api.response.IpForwardingRuleResponse; +import org.apache.cloudstack.api.response.IpQuarantineResponse; import org.apache.cloudstack.api.response.IsolationMethodResponse; import org.apache.cloudstack.api.response.LBHealthCheckResponse; import org.apache.cloudstack.api.response.LBStickinessResponse; @@ -108,6 +111,7 @@ import org.apache.cloudstack.api.response.SecondaryStorageHeuristicsResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.ServiceResponse; +import org.apache.cloudstack.api.response.SharedFSResponse; import org.apache.cloudstack.api.response.Site2SiteCustomerGatewayResponse; import org.apache.cloudstack.api.response.Site2SiteVpnConnectionResponse; import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse; @@ -137,23 +141,29 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.backup.BackupOffering; +import org.apache.cloudstack.backup.BackupRepository; import org.apache.cloudstack.backup.BackupSchedule; 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 +224,11 @@ import com.cloud.projects.ProjectAccount; import com.cloud.projects.ProjectInvitation; import com.cloud.region.ha.GlobalLoadBalancerRule; import com.cloud.resource.RollingMaintenanceManager; -import com.cloud.server.ResourceTag; import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSHypervisor; +import com.cloud.storage.GuestOsCategory; import com.cloud.storage.ImageStore; import com.cloud.storage.Snapshot; import com.cloud.storage.StoragePool; @@ -231,14 +242,13 @@ import com.cloud.user.User; import com.cloud.user.UserAccount; import com.cloud.user.UserData; import com.cloud.uservm.UserVm; -import com.cloud.utils.net.Ip; import com.cloud.utils.Pair; +import com.cloud.utils.net.Ip; import com.cloud.vm.InstanceGroup; import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.VirtualMachine; import com.cloud.vm.snapshot.VMSnapshot; -import org.apache.cloudstack.vm.UnmanagedInstanceTO; public interface ResponseGenerator { UserResponse createUserResponse(UserAccount user); @@ -301,6 +311,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 +327,8 @@ public interface ResponseGenerator { ClusterResponse createClusterResponse(Cluster cluster, Boolean showCapacities); + ClusterResponse createMinimalClusterResponse(Cluster cluster); + FirewallRuleResponse createPortForwardingRuleResponse(PortForwardingRule fwRule); IpForwardingRuleResponse createIpForwardingRuleResponse(StaticNatRule fwRule); @@ -345,9 +359,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 createExtractResponse(Long id, Long zoneId, Long accountId, String mode, String url); + ExtractResponse createVolumeExtractResponse(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 +486,10 @@ public interface ResponseGenerator { AutoScaleVmGroupResponse createAutoScaleVmGroupResponse(AutoScaleVmGroup vmGroup); + GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory); + + GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory, boolean showIcon); + GuestOSResponse createGuestOSResponse(GuestOS os); GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor osHypervisor); @@ -512,8 +532,6 @@ public interface ResponseGenerator { UserDataResponse createUserDataResponse(UserData userData); - BackupResponse createBackupResponse(Backup backup); - BackupScheduleResponse createBackupScheduleResponse(BackupSchedule backup); BackupOfferingResponse createBackupOfferingResponse(BackupOffering policy); @@ -549,4 +567,16 @@ public interface ResponseGenerator { 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); } 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 5ba9d182daa..1f78708f7e5 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/DeleteAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/DeleteAccountCmd.java index 36e22acff91..a90fc4aebe9 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 @@ -89,12 +89,11 @@ public class DeleteAccountCmd extends BaseAsyncCmd { 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/UpdateAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/UpdateAccountCmd.java index 91cbb90e4da..3347a0d09f3 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 java.util.Map; 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; @@ -40,8 +42,8 @@ import org.apache.cloudstack.region.RegionService; 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 { + responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) +public class UpdateAccountCmd extends BaseCmd implements UserCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -70,6 +72,9 @@ public class UpdateAccountCmd extends BaseCmd { @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 class UpdateAccountCmd extends BaseCmd { return params; } + public String getApiKeyAccess() { + return apiKeyAccess; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -131,7 +140,7 @@ public class UpdateAccountCmd extends BaseCmd { 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 { 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 00000000000..80cb92c8362 --- /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.getId()); + 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 00000000000..c4a6505d52f --- /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.getId()); + 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 fef2b27eaa5..d82cc852e4f 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.ArrayList; 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 class ListRolesCmd extends BaseListCmd { 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 @@ public class ListRolesCmd extends BaseListCmd { 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 @@ public class ListRolesCmd extends BaseListCmd { @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 4c317d06b13..b3d816adc3f 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 @@ public abstract class RoleCmd extends BaseCmd { 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/bgp/CreateASNRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmd.java new file mode 100644 index 00000000000..beacba850c3 --- /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 00000000000..33e139315bf --- /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 00000000000..82e54581102 --- /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 00000000000..687f60dc6da --- /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/cluster/AddClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java index 184a443d9db..3aef11b92e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java @@ -19,20 +19,22 @@ package org.apache.cloudstack.api.command.admin.cluster; 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,7 +44,6 @@ import com.cloud.user.Account; requestHasSensitiveInfo = true, responseHasSensitiveInfo = false) public class AddClusterCmd extends BaseCmd { - @Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, required = true, description = "the cluster name") private String clusterName; @@ -64,9 +65,14 @@ public class AddClusterCmd extends BaseCmd { @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, - description = "hypervisor type of the cluster: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3") + description = "hypervisor type of the cluster: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3,External") private String hypervisor; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the cluster. Valid options are: x86_64, aarch64", + 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; @@ -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 class AddClusterCmd extends BaseCmd { return hypervisor; } + public Long getExtensionId() { + return extensionId; + } + public String getClusterType() { return clusterType; } @@ -186,6 +216,10 @@ public class AddClusterCmd extends BaseCmd { this.clusterType = type; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + @Override public long getEntityOwnerId() { return Account.ACCOUNT_ID_SYSTEM; @@ -204,6 +238,14 @@ public class AddClusterCmd extends BaseCmd { 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/ListClustersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmd.java index 67d0678410c..b38278e3946 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmd.java @@ -17,8 +17,11 @@ package org.apache.cloudstack.api.command.admin.cluster; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import javax.inject.Inject; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -28,7 +31,13 @@ import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionHelper; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; +import com.cloud.hypervisor.Hypervisor; import com.cloud.org.Cluster; import com.cloud.utils.Pair; @@ -36,6 +45,8 @@ import com.cloud.utils.Pair; requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListClustersCmd extends BaseListCmd { + @Inject + ExtensionHelper extensionHelper; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -68,6 +79,16 @@ public class ListClustersCmd extends BaseListCmd { @Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "flag to display the capacity of the clusters") private Boolean showCapacities; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the clusters", + since = "4.20.1") + private String arch; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -112,25 +133,64 @@ public class ListClustersCmd extends BaseListCmd { return showCapacities; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListClustersCmd() { + + } + + public ListClustersCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// + protected void updateClustersExtensions(final List clusterResponses) { + if (CollectionUtils.isEmpty(clusterResponses)) { + return; + } + Map idExtensionMap = new HashMap<>(); + for (ClusterResponse response : clusterResponses) { + if (!Hypervisor.HypervisorType.External.getHypervisorDisplayName().equals(response.getHypervisorType())) { + continue; + } + Long extensionId = extensionHelper.getExtensionIdForCluster(response.getInternalId()); + if (extensionId == null) { + continue; + } + Extension extension = idExtensionMap.computeIfAbsent(extensionId, id -> extensionHelper.getExtension(id)); + if (extension == null) { + continue; + } + response.setExtensionId(extension.getUuid()); + response.setExtensionName(extension.getName()); + } + } + protected Pair, Integer> getClusterResponses() { Pair, Integer> result = _mgr.searchForClusters(this); - List clusterResponses = new ArrayList(); + List clusterResponses = new ArrayList<>(); for (Cluster cluster : result.first()) { ClusterResponse clusterResponse = _responseGenerator.createClusterResponse(cluster, showCapacities); clusterResponse.setObjectName("cluster"); clusterResponses.add(clusterResponse); } - return new Pair, Integer>(clusterResponses, result.second()); + updateClustersExtensions(clusterResponses); + return new Pair<>(clusterResponses, result.second()); } @Override public void execute() { Pair, Integer> clusterResponses = getClusterResponses(); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); response.setResponses(clusterResponses.first(), clusterResponses.second()); response.setResponseName(getCommandName()); this.setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java index 77bb97fd39d..c160cfd2e03 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java @@ -16,6 +16,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,6 +32,7 @@ import org.apache.cloudstack.api.response.ClusterResponse; 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) @@ -53,6 +57,17 @@ public class UpdateClusterCmd extends BaseCmd { @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", + 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 class UpdateClusterCmd extends BaseCmd { 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 class UpdateClusterCmd extends BaseCmd { } Cluster result = _resourceService.updateCluster(this); if (result != null) { - ClusterResponse clusterResponse = _responseGenerator.createClusterResponse(cluster, false); + ClusterResponse clusterResponse = _responseGenerator.createClusterResponse(result, false); clusterResponse.setResponseName(getCommandName()); this.setResponseObject(clusterResponse); } else { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java index 50984188bf5..01f7af10841 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,6 +43,12 @@ public class UpdateHypervisorCapabilitiesCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HypervisorCapabilitiesResponse.class, description = "ID of the hypervisor capability") private Long id; + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor for which the hypervisor capabilities are to be updated", since = "4.19.1") + 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; @@ -73,6 +79,14 @@ public class UpdateHypervisorCapabilitiesCmd extends BaseCmd { 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/domain/ListDomainsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmd.java index b91e56dcaef..895e9328992 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 @@ -100,7 +100,7 @@ public class ListDomainsCmd extends BaseListCmd implements UserCmd { dv = EnumSet.of(DomainDetails.all); } else { try { - ArrayList dc = new ArrayList(); + ArrayList dc = new ArrayList<>(); for (String detail : viewDetails) { dc.add(DomainDetails.valueOf(detail)); } @@ -142,7 +142,10 @@ public class ListDomainsCmd extends BaseListCmd implements UserCmd { if (CollectionUtils.isEmpty(response)) { return; } - _resourceLimitService.updateTaggedResourceLimitsAndCountsForDomains(response, getTag()); + EnumSet details = getDetails(); + if (details.contains(DomainDetails.all) || details.contains(DomainDetails.resource)) { + _resourceLimitService.updateTaggedResourceLimitsAndCountsForDomains(response, getTag()); + } if (!getShowIcon()) { return; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmd.java new file mode 100644 index 00000000000..2faad89bf67 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmd.java @@ -0,0 +1,122 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.gpu.GpuCard; + + +@APICommand(name = "createGpuCard", description = "Creates a GPU card definition in the system", + responseObject = GpuCardResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class CreateGpuCardCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.STRING, required = true, + description = "the device ID of the GPU card") + private String deviceId; + + @Parameter(name = ApiConstants.DEVICE_NAME, type = CommandType.STRING, required = true, + description = "the device name of the GPU card") + private String deviceName; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, + description = "the display name of the GPU card") + private String name; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, required = true, + description = "the vendor name of the GPU card") + private String vendorName; + + @Parameter(name = ApiConstants.VENDOR_ID, type = CommandType.STRING, required = true, + description = "the vendor ID of the GPU card") + private String vendorId; + + // Optional parameters for the passthrough vGPU profile display properties + @Parameter(name = ApiConstants.VIDEORAM, type = CommandType.LONG, + description = "the video RAM size in MB for the passthrough vGPU profile") + private Long videoRam; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public String getDeviceId() { + return deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public String getName() { + return name; + } + + public String getVendorName() { + return vendorName; + } + + public String getVendorId() { + return vendorId; + } + + public Long getVideoRam() { + return videoRam; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + GpuCard gpuCard = gpuService.createGpuCard(this); + if (gpuCard != null) { + GpuCardResponse response = new GpuCardResponse(gpuCard); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create GPU card"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create GPU card: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmd.java new file mode 100644 index 00000000000..e6386082a44 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmd.java @@ -0,0 +1,123 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + + +@APICommand(name = "createGpuDevice", description = "Creates a GPU device manually on a host", + responseObject = GpuDeviceResponse.class, since = "4.21.0", requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class CreateGpuDeviceCmd extends BaseCmd { + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, + description = "ID of the host where the GPU device is located") + private Long hostId; + + @Parameter(name = ApiConstants.BUS_ADDRESS, type = CommandType.STRING, required = true, + description = "PCI bus address of the GPU device (e.g., 0000:01:00.0) or UUID for MDEV devices.") + private String busAddress; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + required = true, description = "ID of the GPU card type") + private Long gpuCardId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + required = true, description = "ID of the vGPU profile") + private Long vgpuProfileId; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, + description = "Type of GPU device (PCI, MDEV, VGPUOnly). Defaults to PCI.") + private String type; + + @Parameter(name = ApiConstants.PARENT_GPU_DEVICE_ID, type = CommandType.UUID, entityType = GpuDeviceResponse.class, + description = "ID of the parent GPU device (for virtual GPU devices)") + private Long parentGpuDeviceId; + + @Parameter(name = ApiConstants.NUMA_NODE, type = CommandType.STRING, + description = "NUMA node of the GPU device (e.g., 0, 1, etc.). This is optional and can be used to " + + "specify the NUMA node for the GPU device which is used during allocation. Defaults to -1") + private String numaNode; + + public Long getHostId() { + return hostId; + } + + public String getBusAddress() { + return busAddress; + } + + public Long getGpuCardId() { + return gpuCardId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public GpuDevice.DeviceType getType() { + GpuDevice.DeviceType deviceType = GpuDevice.DeviceType.PCI; + if (StringUtils.isNotBlank(type)) { + deviceType = EnumUtils.getEnumIgnoreCase(GpuDevice.DeviceType.class, type); + if (deviceType == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid GPU device type: " + type); + } + } + return deviceType; + } + + public Long getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public String getNumaNode() { + if (StringUtils.isBlank(numaNode)) { + return "-1"; // Default value for NUMA node + } + return numaNode; + } + + @Override + public void execute() { + try { + GpuDeviceResponse response = gpuService.createGpuDevice(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmd.java new file mode 100644 index 00000000000..3210773b6f4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmd.java @@ -0,0 +1,131 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "createVgpuProfile", description = "Creates a vGPU profile in the system", + responseObject = VgpuProfileResponse.class, requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, since = "4.21.0", authorized = {RoleType.Admin}) +public class CreateVgpuProfileCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, + description = "the name of the vGPU profile") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, + description = "the description of the vGPU profile") + private String description; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + required = true, description = "the GPU card ID associated with this GPU device") + private Long cardId; + + @Parameter(name = ApiConstants.MAX_VGPU_PER_PHYSICAL_GPU, type = CommandType.LONG, + description = "Max vGPU per physical GPU. This is used to calculate capacity.") + private Long maxVgpuPerPgpu; + + @Parameter(name = ApiConstants.VIDEORAM, type = CommandType.LONG, + description = "the video RAM size in MB") + private Long videoRam; + + @Parameter(name = ApiConstants.MAXHEADS, type = CommandType.LONG, + description = "the maximum number of display heads") + private Long maxHeads; + + @Parameter(name = ApiConstants.MAXRESOLUTIONX, type = CommandType.LONG, + description = "the maximum X resolution") + private Long maxResolutionX; + + @Parameter(name = ApiConstants.MAXRESOLUTIONY, type = CommandType.LONG, + description = "the maximum Y resolution") + private Long maxResolutionY; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Long getCardId() { + return cardId; + } + + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public Long getVideoRam() { + return videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + @Override + public void execute() { + try { + VgpuProfileResponse response = gpuService.createVgpuProfile(this); + if (response != null) { + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to create vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmd.java new file mode 100644 index 00000000000..9a510ecdead --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmd.java @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + + +@APICommand(name = "deleteGpuCard", description = "Deletes a GPU card definition from the system", + responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DeleteGpuCardCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuCardResponse.class, required = true, + description = "the ID of the GPU card") + private Long id; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + @Override + public void execute() { + try { + boolean success = gpuService.deleteGpuCard(this); + if (success) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete GPU card"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete GPU card: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmd.java new file mode 100644 index 00000000000..9224afc66ec --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmd.java @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import java.util.List; + +@APICommand(name = "deleteGpuDevice", description = "Deletes a vGPU profile from the system", + responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DeleteGpuDeviceCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GpuDeviceResponse.class, required = true, + description = "comma separated list of IDs of the GPU device") + private List ids; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + @Override + public void execute() { + try { + boolean success = gpuService.deleteGpuDevices(this); + if (success) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to delete vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmd.java new file mode 100644 index 00000000000..a09a04199f5 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmd.java @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "deleteVgpuProfile", description = "Deletes a vGPU profile from the system", + responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DeleteVgpuProfileCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, required = true, + description = "the ID of the vGPU profile") + private Long id; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + @Override + public void execute() { + try { + boolean success = gpuService.deleteVgpuProfile(this); + if (success) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to delete vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmd.java new file mode 100644 index 00000000000..2ac07a9fb3a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmd.java @@ -0,0 +1,63 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; + + +@APICommand(name = "discoverGpuDevices", description = "Discovers available GPU devices on a host", + responseObject = GpuDeviceResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DiscoverGpuDevicesCmd extends BaseListCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, + description = "ID of the host") + private Long id; + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation ////////////////// + /// ////////////////////////////////////////////////// + + @Override + public void execute() { + CallContext.current().setEventDetails("Discovering GPU Devices on host id: " + getId()); + ListResponse response = gpuService.discoverGpuDevices(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdmin.java new file mode 100644 index 00000000000..b3c57713fcd --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdmin.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.command.user.gpu.ListGpuDevicesCmd; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "listGpuDevices", description = "Lists all available GPU devices", + responseView = ResponseObject.ResponseView.Full, + responseObject = GpuDeviceResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class ListGpuDevicesCmdByAdmin extends ListGpuDevicesCmd implements AdminCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuDeviceResponse.class, + description = "ID of the GPU device") + private Long id; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, + description = "the host ID where the GPU device is attached") + private Long hostId; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "the GPU card ID associated with the GPU device") + private Long gpuCardId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + description = "the vGPU profile ID assigned to the GPU device") + private Long vgpuProfileId; + + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getHostId() { + return hostId; + } + + public Long getGpuCardId() { + return gpuCardId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmd.java new file mode 100644 index 00000000000..5dfe6c3deee --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmd.java @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import java.util.List; + +@APICommand(name = "manageGpuDevice", description = "Manages a GPU device", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin}) +public class ManageGpuDeviceCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GpuDeviceResponse.class, required = true, + description = "comma separated list of IDs of the GPU device") + private List ids; + + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + @Override + public void execute() { + try { + if (gpuService.enableGpuDevice(this)) { + SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable GPU device"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable GPU device: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmd.java new file mode 100644 index 00000000000..46de23ec44b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmd.java @@ -0,0 +1,79 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import java.util.List; + +@APICommand(name = "unmanageGpuDevice", description = "Unmanage a GPU device", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin}) +public class UnmanageGpuDeviceCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GpuDeviceResponse.class, required = true, + description = "comma separated list of IDs of the GPU device") + private List ids; + + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + @Override + public void execute() { + try { + if (gpuService.disableGpuDevice(this)) { + SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to disable GPU device"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to disable GPU device: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmd.java new file mode 100644 index 00000000000..0061149a985 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmd.java @@ -0,0 +1,99 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.gpu.GpuCard; + + +@APICommand(name = "updateGpuCard", description = "Updates a GPU card definition in the system", + responseObject = GpuCardResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class UpdateGpuCardCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuCardResponse.class, required = true, + description = "the ID of the GPU card") + private Long id; + + @Parameter(name = ApiConstants.DEVICE_NAME, type = CommandType.STRING, + description = "the device name of the GPU card") + private String deviceName; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the display name of the GPU card") + private String name; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, + description = "the vendor name of the GPU card") + private String vendorName; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getDeviceName() { + return deviceName; + } + + public String getName() { + return name; + } + + public String getVendorName() { + return vendorName; + } + + @Override + public void execute() { + try { + GpuCard gpuCard = gpuService.updateGpuCard(this); + if (gpuCard != null) { + GpuCardResponse response = new GpuCardResponse(gpuCard); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update GPU card"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update GPU card: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmd.java new file mode 100644 index 00000000000..5ad6e6e1a6f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmd.java @@ -0,0 +1,109 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + + +@APICommand(name = "updateGpuDevice", description = "Updates an existing GPU device", + responseObject = GpuDeviceResponse.class, since = "4.21.0", requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class UpdateGpuDeviceCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuDeviceResponse.class, required = true, + description = "ID of the GPU device to update") + private Long id; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "New GPU card ID") + private Long gpuCardId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + description = "New vGPU profile ID") + private Long vgpuProfileId; + + @Parameter(name = "type", type = CommandType.STRING, description = "New type of GPU device (PCI, MDEV, VGPUOnly)") + private String type; + + @Parameter(name = "parentgpudeviceid", type = CommandType.UUID, entityType = GpuDeviceResponse.class, + description = "New parent GPU device ID (for virtual GPU devices)") + private Long parentGpuDeviceId; + + @Parameter(name = ApiConstants.NUMA_NODE, type = CommandType.STRING, + description = "New NUMA node of the GPU device") + private String numaNode; + + public Long getId() { + return id; + } + + public Long getGpuCardId() { + return gpuCardId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public GpuDevice.DeviceType getType() { + GpuDevice.DeviceType deviceType = null; + if (StringUtils.isNotBlank(type)) { + deviceType = EnumUtils.getEnumIgnoreCase(GpuDevice.DeviceType.class, type); + if (deviceType == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid GPU device type: " + type); + } + } + return deviceType; + } + + public Long getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public String getNumaNode() { + return numaNode; + } + + @Override + public void execute() { + try { + GpuDeviceResponse response = gpuService.updateGpuDevice(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmd.java new file mode 100644 index 00000000000..c8d60739bd4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmd.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "updateVgpuProfile", description = "Updates a vGPU profile in the system", + responseObject = VgpuProfileResponse.class, requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, since = "4.21.0", authorized = {RoleType.Admin}) +public class UpdateVgpuProfileCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, required = true, + description = "the ID of the vGPU profile") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the vGPU profile") + private String profileName; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, + description = "the description of the vGPU profile") + private String description; + + @Parameter(name = ApiConstants.MAX_VGPU_PER_PHYSICAL_GPU, type = CommandType.LONG, + description = "the maximum number of vGPUs per physical GPU") + private Long maxVgpuPerPgpu; + + @Parameter(name = ApiConstants.VIDEORAM, type = CommandType.LONG, + description = "the video RAM size in MB") + private Long videoRam; + + @Parameter(name = ApiConstants.MAXHEADS, type = CommandType.LONG, + description = "the maximum number of display heads") + private Long maxHeads; + + @Parameter(name = ApiConstants.MAXRESOLUTIONX, type = CommandType.LONG, + description = "the maximum X resolution") + private Long maxResolutionX; + + @Parameter(name = ApiConstants.MAXRESOLUTIONY, type = CommandType.LONG, + description = "the maximum Y resolution") + private Long maxResolutionY; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getProfileName() { + return profileName; + } + + public String getDescription() { + return description; + } + + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public Long getVideoRam() { + return videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + @Override + public void execute() { + try { + VgpuProfileResponse response = gpuService.updateVgpuProfile(this); + if (response != null) { + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to update vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmd.java new file mode 100644 index 00000000000..f099de43f5d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmd.java @@ -0,0 +1,93 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.guest; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; + +import com.cloud.storage.GuestOsCategory; +import com.cloud.user.Account; + +@APICommand(name = "addOsCategory", + description = "Adds a new OS category", + responseObject = GuestOSCategoryResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.21.0", + authorized = {RoleType.Admin}) +public class AddGuestOsCategoryCmd extends BaseCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name of the OS category", + required = true) + private String name; + + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, + description = "Whether the category is featured or not") + private Boolean featured; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public boolean isFeatured() { + return Boolean.TRUE.equals(featured); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + GuestOsCategory guestOs = _mgr.addGuestOsCategory(this); + if (guestOs != null) { + GuestOSCategoryResponse response = _responseGenerator.createGuestOSCategoryResponse(guestOs); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add new OS category"); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.GuestOsCategory; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/DeleteGuestOsCategoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/DeleteGuestOsCategoryCmd.java new file mode 100644 index 00000000000..8cf62ee2aab --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/DeleteGuestOsCategoryCmd.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.guest; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.user.Account; + + +@APICommand(name = "deleteOsCategory", + description = "Deletes an OS category", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.21.0", + authorized = {RoleType.Admin}) +public class DeleteGuestOsCategoryCmd extends BaseCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + required = true, description = "ID of the OS category") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + boolean result = _mgr.deleteGuestOsCategory(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove OS category"); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.GuestOsCategory; + } + + @Override + public Long getApiResourceId() { + return getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmd.java new file mode 100644 index 00000000000..4041967abe8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmd.java @@ -0,0 +1,113 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.guest; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; + +import com.cloud.storage.GuestOsCategory; +import com.cloud.user.Account; + +@APICommand(name = "updateOsCategory", + description = "Updates an OS category", + responseObject = GuestOSCategoryResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.21.0", + authorized = {RoleType.Admin}) +public class UpdateGuestOsCategoryCmd extends BaseCmd { + + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + required = true, description = "ID of the OS category") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name for the OS category") + private String name; + + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, + description = "Whether the category is featured or not") + private Boolean featured; + + @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, + description = "sort key of the OS category for listing") + private Integer sortKey; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Boolean isFeatured() { + return featured; + } + + public Integer getSortKey() { + return sortKey; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + GuestOsCategory guestOs = _mgr.updateGuestOsCategory(this); + if (guestOs != null) { + GuestOSCategoryResponse response = _responseGenerator.createGuestOSCategoryResponse(guestOs); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update OS category"); + } + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.GuestOsCategory; + } + + @Override + public Long getApiResourceId() { + return getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java index c98cd149ef3..59909e09854 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java @@ -16,8 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.admin.guest; -import org.apache.commons.collections.MapUtils; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -25,18 +29,14 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.GuestOSResponse; -import org.apache.cloudstack.acl.RoleType; +import org.apache.commons.collections.MapUtils; import com.cloud.event.EventTypes; import com.cloud.storage.GuestOS; import com.cloud.user.Account; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - @APICommand(name = "updateGuestOs", description = "Updates the information about Guest OS", responseObject = GuestOSResponse.class, since = "4.4.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateGuestOsCmd extends BaseAsyncCmd { @@ -50,7 +50,7 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = true, description = "UUID of the Guest OS") private Long id; - @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = true, description = "Unique display name for Guest OS") + @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, description = "Unique display name for Guest OS") private String osDisplayName; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs)") @@ -59,6 +59,12 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { @Parameter(name="forDisplay", type=CommandType.BOOLEAN, description="whether this guest OS is available for end users", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + description = "the ID of the OS category", since = "4.21.0") + private Long osCategoryId; + + + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -71,9 +77,9 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { return osDisplayName; } - public Map getDetails() { - Map detailsMap = new HashMap<>();; - if (MapUtils.isNotEmpty(detailsMap)) { + public Map getDetails() { + Map detailsMap = new HashMap<>(); + if (MapUtils.isNotEmpty(details)) { Collection servicesCollection = details.values(); Iterator iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -90,6 +96,10 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { return display; } + public Long getOsCategoryId() { + return osCategoryId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java index ca27837aa88..cc124ab8106 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java @@ -18,7 +18,7 @@ package org.apache.cloudstack.api.command.admin.host; import java.util.ArrayList; import java.util.List; - +import java.util.Map; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -75,6 +75,18 @@ public class AddHostCmd extends BaseCmd { @Parameter(name = ApiConstants.HOST_TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "list of tags to be added to the host") private List hostTags; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the host", + since = "4.21.0") + private List storageAccessGroups; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", + since = "4.21.0") + protected Map externalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -115,10 +127,18 @@ public class AddHostCmd extends BaseCmd { return hostTags; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + public String getAllocationState() { return allocationState; } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelMaintenanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostMaintenanceCmd.java similarity index 98% rename from api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelMaintenanceCmd.java rename to api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostMaintenanceCmd.java index a514a61b8a4..55fe8ec23ce 100644 --- 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/CancelHostMaintenanceCmd.java @@ -33,7 +33,7 @@ import com.cloud.user.Account; @APICommand(name = "cancelHostMaintenance", description = "Cancels host maintenance.", responseObject = HostResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class CancelMaintenanceCmd extends BaseAsyncCmd { +public class CancelHostMaintenanceCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// 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 af87bbf33bb..a71150a69e2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java @@ -21,7 +21,6 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -31,10 +30,13 @@ import org.apache.cloudstack.api.Parameter; 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; @@ -105,6 +107,17 @@ public class ListHostsCmd extends BaseListCmd { @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; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -189,6 +202,26 @@ public class ListHostsCmd extends BaseListCmd { 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; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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/PrepareForHostMaintenanceCmd.java similarity index 98% rename from api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForMaintenanceCmd.java rename to api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForHostMaintenanceCmd.java index 2641c54364e..5c2b50c8723 100644 --- 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/PrepareForHostMaintenanceCmd.java @@ -33,7 +33,7 @@ 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 { +public class PrepareForHostMaintenanceCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// 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 88eeadb9b13..9baea58b7b5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java @@ -16,8 +16,9 @@ // under the License. package org.apache.cloudstack.api.command.admin.host; -import com.cloud.host.Host; -import com.cloud.user.Account; +import java.util.List; +import java.util.Map; + import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -28,7 +29,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.HostResponse; -import java.util.List; +import com.cloud.host.Host; +import com.cloud.user.Account; @APICommand(name = "updateHost", description = "Updates a host.", responseObject = HostResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -67,6 +69,9 @@ public class UpdateHostCmd extends BaseCmd { @Parameter(name = ApiConstants.ANNOTATION, type = CommandType.STRING, description = "Add an annotation to this host", since = "4.11", authorized = {RoleType.Admin}) private String annotation; + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", since = "4.21.0") + protected Map externalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -103,6 +108,10 @@ public class UpdateHostCmd extends BaseCmd { return annotation; } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -125,8 +134,9 @@ public class UpdateHostCmd extends BaseCmd { 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.debug("Failed to update host: {} with id {}", host, getId(), 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/management/ListMgmtsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/ListMgmtsCmd.java index a68ed62857a..7b7eae1d0e9 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.BaseListCmd; 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) @@ -39,6 +40,11 @@ public class ListMgmtsCmd extends BaseListCmd { @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; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -51,6 +57,10 @@ public class ListMgmtsCmd extends BaseListCmd { return hostName; } + public Boolean getPeers() { + return BooleanUtils.toBooleanDefaultIfNull(peers, false); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/management/RemoveManagementServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/RemoveManagementServerCmd.java new file mode 100644 index 00000000000..803b95bfa9e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/RemoveManagementServerCmd.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.management; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.response.ManagementServerResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "removeManagementServer", description = "Removes a Management Server.", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = RoleType.Admin) +public class RemoveManagementServerCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, required = true, description = "the ID of the Management Server") + private Long id; + + public Long getId() { + return id; + } + + @Override + public void execute() { + boolean result = _mgr.removeManagementServer(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove Management Server."); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + public String getEventType() { + return EventTypes.EVENT_MANAGEMENT_SERVER_REMOVE; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java new file mode 100644 index 00000000000..a482cb1d4f2 --- /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=" + getParentId(); + } + + @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 00000000000..5f48cf9c632 --- /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=" + getZoneId(); + } + + @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/CreateNetworkCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java index cd9770877ed..d8b57f79528 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,10 +24,13 @@ 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.network.CreateNetworkCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; import org.apache.cloudstack.api.response.NetworkResponse; import com.cloud.network.Network; +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 { @@ -49,6 +52,14 @@ public class CreateNetworkCmdByAdmin extends CreateNetworkCmd implements AdminCm 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 class CreateNetworkCmdByAdmin extends CreateNetworkCmd implements AdminCm 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 9117bcfc193..51ba6ec02ea 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; @@ -140,17 +141,18 @@ public class CreateNetworkOfferingCmd extends BaseCmd { description = "true if network offering is meant to be used for VPC, false otherwise.") private Boolean forVpc; + @Deprecated @Parameter(name = ApiConstants.FOR_NSX, type = CommandType.BOOLEAN, description = "true if network offering is meant to be used for NSX, false otherwise.", since = "4.20.0") private Boolean forNsx; - @Parameter(name = ApiConstants.NSX_MODE, + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, - description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", - since = "4.20.0") - private String nsxMode; + description = "Name of the provider providing the service", + since = "4.21.0") + private String provider; @Parameter(name = ApiConstants.NSX_SUPPORT_LB, type = CommandType.BOOLEAN, @@ -164,6 +166,12 @@ public class CreateNetworkOfferingCmd extends BaseCmd { 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.") @@ -211,6 +219,16 @@ public class CreateNetworkOfferingCmd extends BaseCmd { 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 /////////////////////// ///////////////////////////////////////////////////// @@ -247,18 +265,38 @@ public class CreateNetworkOfferingCmd extends BaseCmd { return serviceOfferingId; } + public boolean isExternalNetworkProvider() { + return Arrays.asList("NSX", "Netris").stream() + .anyMatch(s -> provider != null && s.equalsIgnoreCase(provider)); + } + + public boolean isForNsx() { + return provider != null && provider.equalsIgnoreCase("NSX"); + } + + public boolean isForNetris() { + return provider != null && provider.equalsIgnoreCase("Netris"); + } + + public String getProvider() { + return provider; + } + public List getSupportedServices() { - if (!isForNsx()) { + if (!isExternalNetworkProvider()) { return supportedServices == null ? new ArrayList() : supportedServices; } else { List services = new ArrayList<>(List.of( Dhcp.getName(), Dns.getName(), - StaticNat.getName(), - SourceNat.getName(), - PortForwarding.getName(), UserData.getName() )); + if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) { + services.addAll(Arrays.asList( + StaticNat.getName(), + SourceNat.getName(), + PortForwarding.getName())); + } if (getNsxSupportsLbService()) { services.add(Lb.getName()); } @@ -298,12 +336,8 @@ public class CreateNetworkOfferingCmd extends BaseCmd { return forVpc; } - public boolean isForNsx() { - return BooleanUtils.isTrue(forNsx); - } - - public String getNsxMode() { - return nsxMode; + public String getNetworkMode() { + return networkMode; } public boolean getNsxSupportsLbService() { @@ -335,7 +369,7 @@ public class CreateNetworkOfferingCmd extends BaseCmd { public Map> getServiceProviders() { Map> serviceProviderMap = new HashMap<>(); - if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isForNsx()) { + if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) { Collection servicesCollection = serviceProviderList.values(); Iterator iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -351,17 +385,16 @@ public class CreateNetworkOfferingCmd extends BaseCmd { providerList.add(provider); serviceProviderMap.put(service, providerList); } - } else if (Boolean.TRUE.equals(forNsx)) { - getServiceProviderMapForNsx(serviceProviderMap); + } else if (isExternalNetworkProvider()) { + getServiceProviderMapForExternalProvider(serviceProviderMap, Network.Provider.getProvider(provider).getName()); } return serviceProviderMap; } - private void getServiceProviderMapForNsx(Map> serviceProviderMap) { + private void getServiceProviderMapForExternalProvider(Map> serviceProviderMap, String provider) { String routerProvider = Boolean.TRUE.equals(getForVpc()) ? VirtualRouterProvider.Type.VPCVirtualRouter.name() : VirtualRouterProvider.Type.VirtualRouter.name(); - List unsupportedServices = new ArrayList<>(List.of("Vpn", "SecurityGroup", "Connectivity", - "Gateway", "BaremetalPxeService")); + List unsupportedServices = new ArrayList<>(List.of("Vpn", "Gateway", "SecurityGroup", "Connectivity", "BaremetalPxeService")); List routerSupported = List.of("Dhcp", "Dns", "UserData"); List allServices = Service.listAllServices().stream().map(Service::getName).collect(Collectors.toList()); if (routerProvider.equals(VirtualRouterProvider.Type.VPCVirtualRouter.name())) { @@ -374,8 +407,9 @@ public class CreateNetworkOfferingCmd extends BaseCmd { continue; if (routerSupported.contains(service)) serviceProviderMap.put(service, List.of(routerProvider)); - else - serviceProviderMap.put(service, List.of(Network.Provider.Nsx.getName())); + else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) || NetworkACL.getName().equalsIgnoreCase(service)) { + serviceProviderMap.put(service, List.of(provider)); + } if (!getNsxSupportsLbService()) { serviceProviderMap.remove(Lb.getName()); } @@ -462,6 +496,14 @@ public class CreateNetworkOfferingCmd extends BaseCmd { 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/DedicateIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmd.java new file mode 100644 index 00000000000..2df032c559c --- /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 IPv4 subnet " + getId(); + } + + @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/DeleteIpv4SubnetForGuestNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmd.java new file mode 100644 index 00000000000..28a646f9d03 --- /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 " + getId(); + } + + @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 00000000000..222bc1bad98 --- /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 " + getId(); + } + + @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/ListIpv4SubnetsForGuestNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmd.java new file mode 100644 index 00000000000..9761f6e89eb --- /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 00000000000..2c2182250ed --- /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/ReleaseDedicatedIpv4SubnetForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmd.java new file mode 100644 index 00000000000..3e151b9b58f --- /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 a dedicated zone IPv4 subnet " + getId(); + } + + @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 00000000000..da7a23f50d9 --- /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 " + getId(); + } + + @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/bgp/ChangeBgpPeersForNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmd.java new file mode 100644 index 00000000000..1d6bffca342 --- /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 " + getNetworkId(); + } + + @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 00000000000..0c89f3f1d43 --- /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 " + getVpcId(); + } + + @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 00000000000..80642124938 --- /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=" + getZoneId(); + } + + @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 00000000000..ec3d0ea1162 --- /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 " + getId(); + } + + @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 00000000000..a01711efa44 --- /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 " + getId(); + } + + @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 00000000000..ea15f0970e8 --- /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 00000000000..92610c233ef --- /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 a dedicated Bgp Peer " + getId(); + } + + @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 00000000000..ae44330ea03 --- /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 " + getId(); + } + + @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/CreateServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index 4562aa7da19..3d20ed50a5d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -29,14 +29,17 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; import org.apache.cloudstack.api.response.VsphereStoragePoliciesResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.api.response.DiskOfferingResponse; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.StringUtils; +import org.apache.cloudstack.vm.lease.VMLeaseManager; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.offering.ServiceOffering; @@ -54,7 +57,11 @@ public class CreateServiceOfferingCmd extends BaseCmd { @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 VM will have access to the host's CPU. The `shares` value does not have a unit, and its purpose is being a weight value for the host to compare between its guest" + + " VMs. For more information, see https://libvirt.org/formatdomain.html#cpu-tuning.") private Integer cpuSpeed; @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the service offering, defaults to 'name'.") @@ -242,6 +249,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 +315,7 @@ public class CreateServiceOfferingCmd extends BaseCmd { 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 +395,15 @@ public class CreateServiceOfferingCmd extends BaseCmd { } } } + + detailsMap.putAll(getExternalDetails()); return detailsMap; } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + public Long getRootDiskSize() { return rootDiskSize; } @@ -477,6 +529,38 @@ public class CreateServiceOfferingCmd extends BaseCmd { return false; } + public VMLeaseManager.ExpiryAction getLeaseExpiryAction() { + if (StringUtils.isBlank(leaseExpiryAction)) { + return null; + } + VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction); + if (action == null) { + throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " + + com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values())); + } + return action; + } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public boolean isPurgeResources() { + return Boolean.TRUE.equals(purgeResources); + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public Integer getGpuCount() { + return gpuCount; + } + + public Boolean getGpuDisplay() { + return Boolean.TRUE.equals(gpuDisplay); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/IsAccountAllowedToCreateOfferingsWithTagsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/IsAccountAllowedToCreateOfferingsWithTagsCmd.java index e94bff1fce8..fcd6b03d3e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/IsAccountAllowedToCreateOfferingsWithTagsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/IsAccountAllowedToCreateOfferingsWithTagsCmd.java @@ -29,7 +29,7 @@ import org.apache.cloudstack.api.response.IsAccountAllowedToCreateOfferingsWithT responseObject = IsAccountAllowedToCreateOfferingsWithTagsResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class IsAccountAllowedToCreateOfferingsWithTagsCmd extends BaseCmd { - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account UUID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account UUID", required = true) private Long id; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java index 7d6bae86083..7f66ac7058a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.admin.offering; import java.util.ArrayList; import java.util.List; +import java.util.Map; import com.cloud.offering.ServiceOffering.State; import org.apache.cloudstack.api.APICommand; @@ -89,6 +90,17 @@ public class UpdateServiceOfferingCmd extends BaseCmd { 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; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -185,6 +197,14 @@ public class UpdateServiceOfferingCmd extends BaseCmd { return state; } + public boolean isPurgeResources() { + return Boolean.TRUE.equals(purgeResources); + } + + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java index c1d9a6db429..36ad00e6e4a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java @@ -30,6 +30,8 @@ import org.apache.cloudstack.api.response.ZoneResponse; import com.cloud.dc.Pod; import com.cloud.user.Account; +import java.util.List; + @APICommand(name = "createPod", description = "Creates a new Pod.", responseObject = PodResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreatePodCmd extends BaseCmd { @@ -63,6 +65,12 @@ public class CreatePodCmd extends BaseCmd { @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this Pod for allocation of new resources") private String allocationState; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the hosts in the pod", + since = "4.21.0") + private List storageAccessGroups; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -95,6 +103,10 @@ public class CreatePodCmd extends BaseCmd { return allocationState; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -111,7 +123,7 @@ public class CreatePodCmd extends BaseCmd { @Override public void execute() { - Pod result = _configService.createPod(getZoneId(), getPodName(), getStartIp(), getEndIp(), getGateway(), getNetmask(), getAllocationState()); + Pod result = _configService.createPod(getZoneId(), getPodName(), getStartIp(), getEndIp(), getGateway(), getNetmask(), getAllocationState(), getStorageAccessGroups()); if (result != null) { PodResponse response = _responseGenerator.createPodResponse(result, false); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/ListPodsByCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/ListPodsByCmd.java index 5ad0b457ced..264e5263c4d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/ListPodsByCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/ListPodsByCmd.java @@ -55,6 +55,11 @@ public class ListPodsByCmd extends BaseListCmd { @Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "flag to display the capacity of the pods") private Boolean showCapacities; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -79,6 +84,18 @@ public class ListPodsByCmd extends BaseListCmd { return showCapacities; } + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListPodsByCmd() { + + } + + public ListPodsByCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -86,8 +103,8 @@ public class ListPodsByCmd extends BaseListCmd { @Override public void execute() { Pair, Integer> result = _mgr.searchForPods(this); - ListResponse response = new ListResponse(); - List podResponses = new ArrayList(); + ListResponse response = new ListResponse<>(); + List podResponses = new ArrayList<>(); for (Pod pod : result.first()) { PodResponse podResponse = _responseGenerator.createPodResponse(pod, showCapacities); podResponse.setObjectName("pod"); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmd.java index 6b31c4cc43c..f46cecdef50 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmd.java @@ -132,11 +132,12 @@ public class ListCapacityCmd extends BaseListCmd { Collections.sort(capacityResponses, new Comparator() { public int compare(CapacityResponse resp1, CapacityResponse resp2) { int res = resp1.getZoneName().compareTo(resp2.getZoneName()); + // Group by zone if (res != 0) { return res; - } else { - return resp1.getCapacityType().compareTo(resp2.getCapacityType()); } + // Sort by capacity type only if not already sorted by usage + return (getSortBy() != null) ? 0 : resp1.getCapacityType().compareTo(resp2.getCapacityType()); } }); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmd.java new file mode 100644 index 00000000000..b6833f09733 --- /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/router/ListRoutersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java index e0cdc0dcf80..9e34c05ce21 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java @@ -16,8 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.router; -import org.apache.commons.lang.BooleanUtils; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -32,7 +30,10 @@ import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.network.router.VirtualRouter.Role; import com.cloud.vm.VirtualMachine; @@ -86,6 +87,11 @@ public class ListRoutersCmd extends BaseListProjectAndAccountResourcesCmd { description = "if true is passed for this parameter, also fetch last executed health check results for the router. Default is false") private Boolean fetchHealthCheckResults; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the router", + since = "4.20.1") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -146,6 +152,10 @@ public class ListRoutersCmd extends BaseListProjectAndAccountResourcesCmd { return BooleanUtils.isTrue(fetchHealthCheckResults); } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java index b779ba2a2b4..460b8d642e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java @@ -56,6 +56,9 @@ public class AddObjectStoragePoolCmd extends BaseCmd { @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for the storage pool") private String tags; + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the total size of the object store in GiB. Used for tracking capacity and sending alerts", since = "4.21") + private Long size; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -68,6 +71,10 @@ public class AddObjectStoragePoolCmd extends BaseCmd { return name; } + public Long getTotalSize() { + return size; + } + public Map getDetails() { Map detailsMap = null; if (details != null && !details.isEmpty()) { @@ -112,7 +119,7 @@ public class AddObjectStoragePoolCmd extends BaseCmd { @Override public void execute(){ try{ - ObjectStore result = _storageService.discoverObjectStore(getName(), getUrl(), getProviderName(), getDetails()); + ObjectStore result = _storageService.discoverObjectStore(getName(), getUrl(), getTotalSize(), getProviderName(), getDetails()); ObjectStoreResponse storeResponse = null; if (result != null) { storeResponse = _responseGenerator.createObjectStoreResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ChangeStoragePoolScopeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ChangeStoragePoolScopeCmd.java new file mode 100644 index 00000000000..d3b6a074610 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ChangeStoragePoolScopeCmd.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.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; +import com.cloud.storage.StoragePool; + +@APICommand(name = "changeStoragePoolScope", description = "Changes the scope of a storage pool when the pool is in Disabled state." + + "This feature is officially tested and supported for Hypervisors: KVM and VMware, Protocols: NFS and Ceph, and Storage Provider: DefaultPrimary. " + + "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() { + String description = "Change storage pool scope. Storage pool Id: "; + StoragePool pool = _entityMgr.findById(StoragePool.class, getId()); + if (pool != null) { + description += pool.getUuid(); + } else { + description += getId(); + } + description += " to " + getScope(); + return description; + } + + @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 00000000000..bfa2589921f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ConfigureStorageAccessCmd.java @@ -0,0 +1,135 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.storage; + +import java.util.List; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; + +import com.cloud.user.Account; + +@APICommand(name = "configureStorageAccess", description = "Configure the storage access groups on zone/pod/cluster/host and storage, accordingly connections to the storage pools", responseObject = SuccessResponse.class, since = "4.21.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ConfigureStorageAccessCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "UUID of the zone") + private Long zoneId; + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "UUID of the pod") + private Long podId; + + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "UUID of the cluster") + private Long clusterId; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "UUID of the host") + private Long hostId; + + @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, description = "UUID of the Storage Pool") + private Long storageId; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for connecting the storage pools and the hosts", + since = "4.21.0") + private List storageAccessGroups; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public Long getPodId() { + return podId; + } + + public Long getClusterId() { + return clusterId; + } + + public Long getHostId() { + return hostId; + } + + public Long getStorageId() { + return storageId; + } + + public List getStorageAccessGroups() { + return storageAccessGroups; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.StoragePool; + } + + @Override + public void execute() { + try { + boolean result = _storageService.configureStorageAccess(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure storage access"); + } + } catch (Exception e) { + logger.debug("Failed to configure storage access ", e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure storage access, " + e.getMessage()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_CONFIGURE_STORAGE_ACCESS; + } + + @Override + public String getEventDescription() { + return "configuring storage access groups"; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java index 75813a7aabf..cbe4b8c06b3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java @@ -61,6 +61,10 @@ public class CreateStoragePoolCmd extends BaseCmd { @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for the storage pool") private String tags; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, type = CommandType.STRING, + description = "comma separated list of storage access groups for connecting to hosts having those specific groups", since = "4.21.0") + private String storageAccessGroups; + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "the URL of the storage pool") private String url; @@ -115,6 +119,10 @@ public class CreateStoragePoolCmd extends BaseCmd { return tags; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + public String getUrl() { return url; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageAccessGroupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageAccessGroupsCmd.java new file mode 100644 index 00000000000..d2a1757839f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageAccessGroupsCmd.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.api.command.admin.storage; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.StorageAccessGroupResponse; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.response.ListResponse; + +@APICommand(name = "listStorageAccessGroups", description = "Lists storage access groups", responseObject = StorageAccessGroupResponse.class, since = "4.21.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListStorageAccessGroupsCmd extends BaseListCmd { + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, description = "Name of the Storage access group") + private String name; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public String getName() { + return name; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.StoragePool; + } + + @Override + public void execute() { + ListResponse response = _queryService.searchForStorageAccessGroups(this); + + response.setResponseName(getCommandName()); + + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java index 293ed3103cb..0f2c9f3416e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java @@ -41,7 +41,7 @@ public class ListStoragePoolsCmd extends BaseListCmd { @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, - description = "list storage pools belongig to the specific cluster") + description = "list storage pools belonging to the specific cluster") private Long clusterId; @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the IP address for the storage pool") @@ -72,6 +72,11 @@ public class ListStoragePoolsCmd extends BaseListCmd { @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 class ListStoragePoolsCmd extends BaseListCmd { 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/UpdateImageStoreCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateImageStoreCmd.java index bcc438b957b..0e1631a46ba 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 class UpdateImageStoreCmd extends BaseCmd { 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 497179d25ef..ac007137ef1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java @@ -44,6 +44,8 @@ public class UpdateObjectStoragePoolCmd extends BaseCmd { @Parameter(name = ApiConstants.URL, type = CommandType.STRING, description = "the url for the object store") private String url; + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the total size of the object store in GiB. Used for tracking capacity and sending alerts. Set to 0 to stop tracking.", since = "4.21") + private Long size; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -61,6 +63,10 @@ public class UpdateObjectStoragePoolCmd extends BaseCmd { return url; } + public Long getSize() { + return size; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStoragePoolCmd.java index 13f02ef83c2..f2d7bbeb189 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 org.apache.cloudstack.api.response.StoragePoolResponse; 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", @@ -147,7 +149,17 @@ public class UpdateStoragePoolCmd extends BaseCmd { @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); + } + + 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 79554f44782..468c87d4d99 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.api.response.SuccessResponse; 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/systemvm/ListSystemVMsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ListSystemVMsCmd.java index e8e5ee0ebad..13113e17ea4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ListSystemVMsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ListSystemVMsCmd.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.api.command.admin.systemvm; import java.util.ArrayList; import java.util.List; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -31,7 +30,9 @@ import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.SystemVmResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; @@ -74,6 +75,11 @@ public class ListSystemVMsCmd extends BaseListCmd { since = "3.0.1") private Long storageId; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the system VM", + since = "4.20.1") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -110,6 +116,10 @@ public class ListSystemVMsCmd extends BaseListCmd { return storageId; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/PatchSystemVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/PatchSystemVMCmd.java index 4f4b2631667..eafee7424ff 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/PatchSystemVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/PatchSystemVMCmd.java @@ -46,7 +46,7 @@ public class PatchSystemVMCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, description = "If true, initiates copy of scripts and restart of the agent, even if the scripts version matches." + "To be used with ID parameter only") - private Boolean force; + private Boolean forced; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -58,7 +58,7 @@ public class PatchSystemVMCmd extends BaseAsyncCmd { } public boolean isForced() { - return force != null && force; + return forced != null && forced; } ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/GenerateUsageRecordsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/GenerateUsageRecordsCmd.java index 491b0fe85ba..a0314586d92 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/ListUsageRecordsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageRecordsCmd.java index 3cb148c2af0..9ce1fcb2bc9 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 class ListUsageRecordsCmd extends BaseListCmd { } 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 class ListUsageRecordsCmd extends BaseListCmd { @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 2772743c75a..b993735dba7 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.APICommand; 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 class ListUsageTypesCmd extends BaseCmd { @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/user/GetUserKeysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserKeysCmd.java index 3a3414d95d8..cdd239f72b5 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,6 +20,7 @@ package org.apache.cloudstack.api.command.admin.user; 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; @@ -54,11 +55,13 @@ public class GetUserKeysCmd extends BaseCmd{ else return Account.ACCOUNT_ID_SYSTEM; } public void execute(){ - Map keys = _accountService.getKeys(this); + Pair> keys = _accountService.getKeys(this); + RegisterResponse response = new RegisterResponse(); if(keys != null){ - response.setApiKey(keys.get("apikey")); - response.setSecretKey(keys.get("secretkey")); + 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/ListUsersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java index ef9e3fa2240..2f29b1ec1e4 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") 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 class ListUsersCmd extends BaseListAccountResourcesCmd { 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/UpdateUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java index 3f8d386d266..3d7f51ae220 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 @@ package org.apache.cloudstack.api.command.admin.user; 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; @@ -66,9 +67,12 @@ 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") + @Parameter(name = ApiConstants.USER_SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey") private String secretKey; + @Parameter(name = ApiConstants.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, description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") @@ -120,6 +124,10 @@ public class UpdateUserCmd extends BaseCmd { return secretKey; } + public String getApiKeyAccess() { + return apiKeyAccess; + } + public String getTimezone() { return timezone; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java index c0ba99a8233..5cb384925e8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.command.admin.vlan; +import com.cloud.configuration.ConfigurationService; +import com.cloud.network.Network; import com.cloud.utils.net.NetUtils; import org.apache.cloudstack.api.APICommand; @@ -39,7 +41,6 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; -import java.util.Objects; @APICommand(name = "createVlanIpRange", description = "Creates a VLAN IP range.", responseObject = VlanIpRangeResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -114,8 +115,8 @@ public class CreateVlanIpRangeCmd extends BaseCmd { @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if IP range is set to system vms, false if not") private Boolean forSystemVms; - @Parameter(name = ApiConstants.FOR_NSX, type = CommandType.BOOLEAN, description = "true if the IP range is used for NSX resource", since = "4.20.0") - private boolean forNsx; + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "Provider name for which the IP range is reserved for", since = "4.21.0") + private String provider; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -157,12 +158,12 @@ public class CreateVlanIpRangeCmd extends BaseCmd { return startIp; } - public boolean isForNsx() { - return !Objects.isNull(forNsx) && forNsx; + public Network.Provider getProvider() { + return Network.Provider.getProvider(provider); } public String getVlan() { - if ((vlan == null || vlan.isEmpty()) && !isForNsx()) { + if ((vlan == null || vlan.isEmpty()) && !ConfigurationService.IsIpRangeForProvider(getProvider())) { vlan = "untagged"; } return vlan; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java index ac63a5efac3..6f6a5237b3d 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 @@ -120,27 +120,16 @@ public class AssignVMCmd extends BaseCmd { @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 VM [%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 00000000000..d95f17ef304 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/CreateVMFromBackupCmdByAdmin.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.vm; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.UserVmResponse; + +import com.cloud.vm.VirtualMachine; + +@APICommand(name = "createVMFromBackup", + description = "Creates and automatically starts a VM from a backup.", + responseObject = UserVmResponse.class, + responseView = ResponseObject.ResponseView.Full, + entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, + since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateVMFromBackupCmdByAdmin extends CreateVMFromBackupCmd implements AdminCmd { + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "destination Pod ID to deploy the VM to - parameter available for root admin only", since = "4.21") + private Long podId; + + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "destination Cluster ID to deploy the VM to - parameter available for root admin only", since = "4.21") + private Long clusterId; + + public Long getPodId() { + return podId; + } + + public Long getClusterId() { + return clusterId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportUnmanagedInstanceCmd.java index dd897218a4d..ae6ceff26c7 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 @@ -201,8 +201,8 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd { 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)); + if (logger.isDebugEnabled()) { + logger.debug(String.format("nic, '%s', goes on net, '%s'", 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,8 +219,8 @@ public class ImportUnmanagedInstanceCmd extends BaseAsyncCmd { 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)); + if (logger.isDebugEnabled()) { + logger.debug(String.format("nic, '%s', gets ip, '%s'", nic, ipAddress)); } if (StringUtils.isEmpty(nic)) { throw new InvalidParameterValueException(String.format("NIC ID: '%s' is invalid for IP address mapping", nic)); 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 1a34b7ea6cc..db43b53ab9a 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.UserVmResponse; 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,48 @@ 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; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -194,10 +203,18 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd { 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; } 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 00000000000..d586a81b685 --- /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/ListVnfAppliancesCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListVnfAppliancesCmdByAdmin.java new file mode 100644 index 00000000000..1a820ab9a48 --- /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 8881a2bc354..0bc993ef1f7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.admin.vm; +import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.APICommand; @@ -145,6 +146,10 @@ public class MigrateVMCmd extends BaseAsyncCmd { throw new InvalidParameterValueException("Unable to find the VM by id=" + getVirtualMachineId()); } + if (Hypervisor.HypervisorType.External.equals(userVm.getHypervisorType())) { + throw new InvalidParameterValueException("Migrate VM instance operation is not allowed for External hypervisor type"); + } + Host destinationHost = null; // OfflineMigration performed when this parameter is specified StoragePool destStoragePool = null; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmd.java new file mode 100644 index 00000000000..57c3ee586d3 --- /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.finalyzeAccountId(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 00000000000..dbe3d37e406 --- /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/UnmanageVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmd.java new file mode 100644 index 00000000000..dcc8b2af8d7 --- /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 String.format("Unmanaging Volume with ID %s", volumeId); + } + + ///////////////////////////////////////////////////// + /////////////// 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/CreateVPCCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdmin.java index bd00876ed36..9dc31f8cefe 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 dd5c815238e..84f4d451460 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.admin.vpc; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -25,10 +26,12 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.Network; import com.cloud.network.VirtualRouterProvider; +import com.cloud.offering.NetworkOffering; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.collections.CollectionUtils; @@ -57,6 +60,7 @@ 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; @APICommand(name = "createVPCOffering", description = "Creates VPC offering", responseObject = VpcOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -112,17 +116,18 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { since = "4.13") private List zoneIds; + @Deprecated @Parameter(name = ApiConstants.FOR_NSX, type = CommandType.BOOLEAN, description = "true if network offering is meant to be used for NSX, false otherwise.", since = "4.20.0") private Boolean forNsx; - @Parameter(name = ApiConstants.NSX_MODE, + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, - description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", - since = "4.20.0") - private String nsxMode; + description = "Name of the provider providing the service", + since = "4.21.0") + private String provider; @Parameter(name = ApiConstants.NSX_SUPPORT_LB, type = CommandType.BOOLEAN, @@ -136,6 +141,22 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { 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 /////////////////////// ///////////////////////////////////////////////////// @@ -148,20 +169,31 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { return StringUtils.isEmpty(displayText) ? vpcOfferingName : displayText; } + public boolean isExternalNetworkProvider() { + return Arrays.asList("NSX", "Netris").stream() + .anyMatch(s -> provider != null && s.equalsIgnoreCase(provider)); + } + public List getSupportedServices() { - if (!isForNsx() && CollectionUtils.isEmpty(supportedServices)) { + if (!isExternalNetworkProvider() && CollectionUtils.isEmpty(supportedServices)) { throw new InvalidParameterValueException("Supported services needs to be provided"); } - if (isForNsx()) { + if (isExternalNetworkProvider()) { supportedServices = new ArrayList<>(List.of( Dhcp.getName(), Dns.getName(), - StaticNat.getName(), - SourceNat.getName(), NetworkACL.getName(), - PortForwarding.getName(), UserData.getName() )); + if (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()) { supportedServices.add(Lb.getName()); } @@ -169,12 +201,12 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { return supportedServices; } - public boolean isForNsx() { - return BooleanUtils.isTrue(forNsx); + public String getProvider() { + return provider; } - public String getNsxMode() { - return nsxMode; + public String getNetworkMode() { + return networkMode; } public boolean getNsxSupportsLbService() { @@ -183,7 +215,7 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { public Map> getServiceProviders() { Map> serviceProviderMap = new HashMap<>(); - if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isForNsx()) { + if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) { Collection> servicesCollection = serviceProviderList.values(); Iterator> iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -203,16 +235,18 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { providerList.add(provider); serviceProviderMap.put(service, providerList); } - } else if (Boolean.TRUE.equals(forNsx)) { - getServiceProviderMapForNsx(serviceProviderMap); + } else if (isExternalNetworkProvider()) { + getServiceProviderMapForExternalProvider(serviceProviderMap, Network.Provider.getProvider(provider).getName()); } return serviceProviderMap; } - private void getServiceProviderMapForNsx(Map> serviceProviderMap) { - List unsupportedServices = List.of("Vpn", "BaremetalPxeService", "SecurityGroup", "Connectivity", - "Gateway", "Firewall"); + private void getServiceProviderMapForExternalProvider(Map> serviceProviderMap, String provider) { + List unsupportedServices = new ArrayList<>(List.of("Vpn", "BaremetalPxeService", "SecurityGroup", "Connectivity", "Firewall")); + if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) { + unsupportedServices.add("Gateway"); + } List routerSupported = List.of("Dhcp", "Dns", "UserData"); List allServices = Network.Service.listAllServices().stream().map(Network.Service::getName).collect(Collectors.toList()); for (String service : allServices) { @@ -220,8 +254,10 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { continue; if (routerSupported.contains(service)) serviceProviderMap.put(service, List.of(VirtualRouterProvider.Type.VPCVirtualRouter.name())); - else - serviceProviderMap.put(service, List.of(Network.Provider.Nsx.getName())); + else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) || + Stream.of(NetworkACL.getName(), Gateway.getName()).anyMatch(s -> s.equalsIgnoreCase(service))) { + serviceProviderMap.put(service, List.of(provider)); + } } if (!getNsxSupportsLbService()) { serviceProviderMap.remove(Lb.getName()); @@ -265,6 +301,14 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { 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/zone/CreateZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java index 24660e41ed9..f3c1bab260b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java @@ -31,6 +31,8 @@ import org.apache.cloudstack.context.CallContext; import com.cloud.dc.DataCenter; import com.cloud.user.Account; +import java.util.List; + @APICommand(name = "createZone", description = "Creates a Zone.", responseObject = ZoneResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateZoneCmd extends BaseCmd { @@ -88,6 +90,11 @@ public class CreateZoneCmd extends BaseCmd { @Parameter(name = ApiConstants.IS_EDGE, type = CommandType.BOOLEAN, description = "true if the zone is an edge zone, false otherwise", since = "4.18.0") private Boolean isEdge; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the hosts in the zone", + since = "4.21.0") + private List storageAccessGroups; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -162,6 +169,10 @@ public class CreateZoneCmd extends BaseCmd { return isEdge; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java index d38ae057f05..9cd845c774c 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 @@ -103,7 +103,7 @@ public class AddUserToProjectCmd extends BaseAsyncCmd { @Override public String getEventDescription() { - return "Adding user "+getUsername()+" to Project "+getProjectId(); + return "Adding user " + getUsername() + " to project: " + getProjectId(); } ///////////////////////////////////////////////////// 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 8319911c5c8..0731d837804 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 @@ -81,7 +81,6 @@ public class DeleteUserFromProjectCmd extends BaseAsyncCmd { return "Removing user " + userId + " from project: " + projectId; } - @Override public long getEntityOwnerId() { Project project = _projectService.getProject(projectId); 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 0a962b19e4f..bd9ab30f4f1 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.ArrayList; 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; @@ -70,6 +71,9 @@ public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserC 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") private Boolean showIcon; @@ -120,6 +124,10 @@ public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserC return dv; } + public String getApiKeyAccess() { + return apiKeyAccess; + } + public boolean getShowIcon() { return showIcon != null ? showIcon : false; } @@ -149,7 +157,10 @@ public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserC if (CollectionUtils.isEmpty(response)) { return; } - _resourceLimitService.updateTaggedResourceLimitsAndCountsForAccounts(response, getTag()); + EnumSet details = getDetails(); + if (details.contains(DomainDetails.all) || details.contains(DomainDetails.resource)) { + _resourceLimitService.updateTaggedResourceLimitsAndCountsForAccounts(response, getTag()); + } if (!getShowIcon()) { return; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java index 5760ca3ba1c..357f0c83ed7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java @@ -108,6 +108,9 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if range is dedicated for system VMs", since = "4.20.0") private Boolean forSystemVMs; + @Parameter(name = ApiConstants.FOR_PROVIDER, type = CommandType.BOOLEAN, description = "true if range is dedicated for external network provider", since = "4.21.0") + private Boolean forProvider; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -183,6 +186,10 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC return BooleanUtils.isTrue(forSystemVMs); } + public boolean isForProvider() { + return BooleanUtils.isTrue(forProvider); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java index f5b8c3da855..2d84b20c582 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 @@ -100,7 +100,7 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd { 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." + + "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") 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 e8ca502b5cd..0b73fd91b52 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 @@ -95,7 +95,7 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd { 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." + + "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") 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 558f92e4006..4e9028f5217 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java @@ -60,6 +60,27 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd { description = "ID of the VM") 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 class CreateBackupCmd extends BaseAsyncCreateCmd { return vmId; } + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Boolean getQuiesceVM() { + return quiesceVM; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -75,7 +108,7 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.createBackup(getVmId()); + boolean result = backupManager.createBackup(this, getJob()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java index 5dc06af2123..a352c688f12 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java @@ -75,6 +75,19 @@ public class CreateBackupScheduleCmd extends BaseCmd { description = "Specifies a timezone for this command. For more information on the timezone parameter, see TimeZone Format.") private String timezone; + @Parameter(name = ApiConstants.MAX_BACKUPS, type = CommandType.INTEGER, + 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 +108,14 @@ public class CreateBackupScheduleCmd extends BaseCmd { return timezone; } + public Integer getMaxBackups() { + return maxBackups; + } + + public Boolean getQuiesceVM() { + return quiesceVM; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java index 0245f228b89..dbfd21d5a56 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.backup.BackupManager; @@ -42,7 +43,7 @@ import com.cloud.utils.exception.CloudRuntimeException; description = "Deletes the backup schedule of a VM", responseObject = SuccessResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) -public class DeleteBackupScheduleCmd extends BaseCmd { +public class DeleteBackupScheduleCmd extends BaseCmd { @Inject private BackupManager backupManager; @@ -51,13 +52,15 @@ public class DeleteBackupScheduleCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, - type = CommandType.UUID, - entityType = UserVmResponse.class, - required = true, - description = "ID of the VM") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, + description = "ID of the VM from which all backup schedules will be deleted.") private Long vmId; + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BackupScheduleResponse.class, + since = "4.20.1", description = "ID of the backup schedule to be deleted. It has precedence over the 'virtualmachineid' parameter, " + + "i.e., when the 'id' parameter is specified, the 'virtualmachineid' parameter will be ignored.") + private Long id; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -66,6 +69,9 @@ public class DeleteBackupScheduleCmd extends BaseCmd { return vmId; } + public Long getId() { return id; } + + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -73,7 +79,7 @@ public class DeleteBackupScheduleCmd extends BaseCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.deleteBackupSchedule(getVmId()); + boolean result = backupManager.deleteBackupSchedule(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java index 6cc765328f6..fa6e3ea5d45 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,6 +19,7 @@ package org.apache.cloudstack.api.command.user.backup; 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; @@ -27,6 +28,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.ListResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.backup.BackupManager; import org.apache.cloudstack.backup.BackupSchedule; @@ -39,6 +41,9 @@ 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", responseObject = BackupScheduleResponse.class, since = "4.14.0", @@ -74,9 +79,14 @@ public class ListBackupScheduleCmd extends BaseCmd { @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); + List schedules = backupManager.listBackupSchedule(getVmId()); + ListResponse response = new ListResponse<>(); + List scheduleResponses = new ArrayList<>(); + if (!CollectionUtils.isNullOrEmpty(schedules)) { + for (BackupSchedule schedule : schedules) { + scheduleResponses.add(_responseGenerator.createBackupScheduleResponse(schedule)); + } + response.setResponses(scheduleResponses, schedules.size()); response.setResponseName(getCommandName()); setResponseObject(response); } else { 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 8597d97278c..0c2b4f9b91e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupOfferingResponse; import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UserVmResponse; @@ -75,6 +76,25 @@ public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { description = "list backups by zone id") private Long zoneId; + @Parameter(name = ApiConstants.NAME, + type = CommandType.STRING, + since = "4.21.0", + description = "list backups by name") + private String name; + + @Parameter(name = ApiConstants.BACKUP_OFFERING_ID, + type = CommandType.UUID, + entityType = BackupOfferingResponse.class, + since = "4.21.0", + description = "list backups by backup offering") + private Long backupOfferingId; + + @Parameter(name = ApiConstants.LIST_VM_DETAILS, + type = CommandType.BOOLEAN, + since = "4.21.0", + description = "list backups with VM details") + private Boolean listVmDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -87,10 +107,22 @@ public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { return vmId; } + public String getName() { + return name; + } + + public Long getBackupOfferingId() { + return backupOfferingId; + } + public Long getZoneId() { return zoneId; } + public Boolean getListVmDetails() { + return listVmDetails; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -101,7 +133,7 @@ public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { if (backup == null) { continue; } - BackupResponse backupResponse = _responseGenerator.createBackupResponse(backup); + BackupResponse backupResponse = backupManager.createBackupResponse(backup, this.getListVmDetails()); responses.add(backupResponse); } final ListResponse response = new ListResponse<>(); @@ -116,6 +148,7 @@ public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { Pair, Integer> result = backupManager.listBackups(this); setupResponseBackupList(result.first(), result.second()); } catch (Exception e) { + logger.debug("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/repository/AddBackupRepositoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java new file mode 100644 index 00000000000..5d0c838bc37 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java @@ -0,0 +1,137 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, 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.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") + private Long zoneId; + + @Parameter(name = ApiConstants.CAPACITY_BYTES, type = CommandType.LONG, description = "capacity of this backup repository") + private Long capacityBytes; + + + ///////////////////////////////////////////////////// + /////////////////// 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 mountOptions == null ? "" : mountOptions; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getCapacityBytes() { + return capacityBytes; + } + + ///////////////////////////////////////////////////// + /////////////////// 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 00000000000..912170eb4ca --- /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 00000000000..8293afb657d --- /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/bgp/ListASNumbersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmd.java new file mode 100644 index 00000000000..b835f7225df --- /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 d2c91e57871..722556b8e2d 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") 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 8e281b20e91..f913373c04b 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; ///////////////////////////////////////////////////// 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 cf25dfaf5b5..e73bb97a21b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java @@ -55,6 +55,7 @@ public class ListCapabilitiesCmd extends BaseCmd { 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 +70,11 @@ public class ListCapabilitiesCmd extends BaseCmd { 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.setObjectName("capability"); response.setResponseName(getCommandName()); this.setResponseObject(response); 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 24b5a78c085..efccb5c09b0 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 @@ package org.apache.cloudstack.api.command.user.firewall; 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.exception.ResourceUnavailableException; 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}, @@ -94,21 +96,44 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal 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; } - } // /////////////////////////////////////////////////// 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 3545b3d21fb..9d1fe176019 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 @@ -105,8 +105,12 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P description = "the ID of the virtual machine 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; " + "if false - firewall rule has to be created explicitly. If not specified 1) defaulted to false when PF" @@ -155,11 +159,7 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P @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() { @@ -332,12 +332,6 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P @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())) { @@ -345,6 +339,8 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P } } + _rulesService.validatePortForwardingSourceCidrList(sourceCidrList); + try { PortForwardingRule result = _rulesService.createPortForwardingRule(this, virtualMachineId, privateIp, getOpenFirewall(), isDisplay()); setEntityId(result.getId()); 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 3fb66bd861f..ee4bd18ad40 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,6 +32,8 @@ import com.cloud.network.rules.PortForwardingRule; 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}, @@ -63,6 +65,13 @@ public class UpdatePortForwardingRuleCmd extends BaseAsyncCustomIdCmd { @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 class UpdatePortForwardingRuleCmd extends BaseAsyncCustomIdCmd { return display; } + public List getSourceCidrList() { + return sourceCidrList; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -130,7 +143,7 @@ public class UpdatePortForwardingRuleCmd extends BaseAsyncCustomIdCmd { @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 00000000000..b035ecbe71b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmd.java @@ -0,0 +1,96 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gpu; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.ListResponse; + +@APICommand(name = "listGpuCards", description = "Lists all available GPU cards", + responseObject = GpuCardResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class ListGpuCardsCmd extends BaseListCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "ID of the GPU card") + private Long id; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, + description = "vendor name of the GPU card") + private String vendorName; + + @Parameter(name = ApiConstants.VENDOR_ID, type = CommandType.STRING, + description = "vendor ID of the GPU card") + private String vendorId; + + @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.STRING, + description = "device ID of the GPU card") + private String deviceId; + + @Parameter(name = ApiConstants.DEVICE_NAME, type = CommandType.STRING, + description = "device name of the GPU card") + private String deviceName; + + @Parameter(name = ApiConstants.ACTIVE_ONLY, type = CommandType.BOOLEAN, + description = "If true, only GPU cards which have a device will be listed. If false, all GPU cards will be listed.") + private Boolean activeOnly; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getVendorName() { + return vendorName; + } + + public String getVendorId() { + return vendorId; + } + + public String getDeviceId() { + return deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public boolean getActiveOnly() { + return Boolean.TRUE.equals(activeOnly); + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override public void execute() { + ListResponse response = gpuService.listGpuCards(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmd.java new file mode 100644 index 00000000000..19c92062851 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmd.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gpu; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "listGpuDevices", description = "Lists all available GPU devices", + responseView = ResponseObject.ResponseView.Restricted, + responseObject = GpuDeviceResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListGpuDevicesCmd extends BaseListCmd implements UserCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, + description = "the virtual machine ID to which the GPU device is allocated") + private Long vmId; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation ////////////////// + /// ////////////////////////////////////////////////// + + @Override + public void execute() { + CallContext.current().setEventDetails("Listing GPU devices"); + ListResponse response = gpuService.listGpuDevices(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmd.java new file mode 100644 index 00000000000..85bf91d7aee --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmd.java @@ -0,0 +1,80 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gpu; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + +@APICommand(name = "listVgpuProfiles", description = "Lists all available vGPU profiles", + responseObject = VgpuProfileResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class ListVgpuProfilesCmd extends BaseListCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + description = "ID of the vGPU profile") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the vGPU profile") + private String name; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "the GPU card ID associated with this GPU device") + private Long cardId; + + @Parameter(name = ApiConstants.ACTIVE_ONLY, type = CommandType.BOOLEAN, + description = "If true, only vGPU profiles which have a device will be listed. If false, all vGPU profiles will be listed.") + private Boolean activeOnly; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Long getCardId() { + return cardId; + } + + public boolean getActiveOnly() { + return Boolean.TRUE.equals(activeOnly); + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override public void execute() { + ListResponse response = gpuService.listVgpuProfiles(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java index c74514d662c..413798970bf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java @@ -26,7 +26,9 @@ import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import com.cloud.cpu.CPU; import com.cloud.storage.GuestOsCategory; import com.cloud.utils.Pair; @@ -39,12 +41,48 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "list Os category by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "List OS category by id") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list os category by name", since = "3.0.1") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List OS category by name", since = "3.0.1") private String name; + @Parameter(name = ApiConstants.IS_FEATURED, + type = CommandType.BOOLEAN, + description = "List available OS categories by featured or not", + since = "4.21.0") + private Boolean featured; + + @Parameter(name = ApiConstants.IS_ISO, + type = CommandType.BOOLEAN, + description = "List OS categories for which an ISO is available", + since = "4.21.0") + private Boolean iso; + + @Parameter(name = ApiConstants.IS_VNF, type = CommandType.BOOLEAN, + description = "List OS categories for which a VNF template is available", + since = "4.21.0") + private Boolean vnf; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "List available OS categories types for the zone", + since = "4.21.0") + private Long zoneId; + + @Parameter(name = ApiConstants.ARCH, + type = CommandType.STRING, + description = "List OS categories types available for given CPU architecture", + since = "4.21.0") + private String arch; + + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, + type = CommandType.BOOLEAN, + description = "flag to display the resource image for the OS category", + since = "4.21.0") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -57,6 +95,30 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd { return name; } + public Boolean isFeatured() { + return featured; + } + + public Boolean isIso() { + return iso; + } + + public Boolean isVnf() { + return vnf; + } + + public Long getZoneId() { + return zoneId; + } + + public CPU.CPUArch getArch() { + return arch == null ? null : CPU.CPUArch.fromType(arch); + } + + public boolean isShowIcon() { + return Boolean.TRUE.equals(showIcon); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -64,14 +126,11 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd { @Override public void execute() { Pair, Integer> result = _mgr.listGuestOSCategoriesByCriteria(this); - ListResponse response = new ListResponse(); - List osCatResponses = new ArrayList(); + ListResponse response = new ListResponse<>(); + List osCatResponses = new ArrayList<>(); for (GuestOsCategory osCategory : result.first()) { - GuestOSCategoryResponse categoryResponse = new GuestOSCategoryResponse(); - categoryResponse.setId(osCategory.getUuid()); - categoryResponse.setName(osCategory.getName()); - - categoryResponse.setObjectName("oscategory"); + GuestOSCategoryResponse categoryResponse = _responseGenerator.createGuestOSCategoryResponse(osCategory, + isShowIcon()); osCatResponses.add(categoryResponse); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java new file mode 100644 index 00000000000..8566b413cc1 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "createGuiTheme", description = "Creates a customized GUI theme for a set of Common Names (fixed or wildcard), a set of domain UUIDs, and/or a set of " + + "account UUIDs.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0.0", authorized = {RoleType.Admin}) +public class CreateGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.NAME, required = true, type = CommandType.STRING, length = 2048, description = "A name to identify the theme.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, length = 4096, description = "A description for the theme.") + private String description; + + @Parameter(name = ApiConstants.CSS, type = CommandType.STRING, length = 65535, description = "The CSS to be retrieved and imported into the GUI " + + "when matching the theme access configurations.") + private String css; + + @Parameter(name = ApiConstants.JSON_CONFIGURATION, type = CommandType.STRING, length = 65535, description = "The JSON with the configurations to be " + + "retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @Parameter(name = ApiConstants.COMMON_NAMES, type = CommandType.STRING, length = 65535, description = "A set of Common Names (CN) (fixed or " + + "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, length = 65535, description = "A set of domain UUIDs (also known as ID for " + + "the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.STRING, length = 65535, description = "A set of account UUIDs (also known as ID for" + + " the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Defines whether a theme can be retrieved by anyone when only " + + "the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is considered as `false`.") + private Boolean isPublic = true; + + @Parameter(name = ApiConstants.RECURSIVE_DOMAINS, type = CommandType.BOOLEAN, description = "Defines whether the subdomains of the informed domains are considered. Default " + + "value is false.") + private Boolean recursiveDomains = false; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public Boolean getPublic() { + return isPublic; + } + + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + @Override + public void execute() { + GuiThemeJoin guiThemeJoin = guiThemeService.createGuiTheme(this); + + if (guiThemeJoin == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create the GUI theme."); + } + + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoin); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java new file mode 100644 index 00000000000..35a0a749aa9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "listGuiThemes", description = "Lists GUI themes.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.User, RoleType.DomainAdmin, + RoleType.ResourceAdmin}) +public class ListGuiThemesCmd extends BaseListCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "The theme ID.", entityType = GuiThemeResponse.class) + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the theme.") + private String name; + + @Parameter(name = ApiConstants.COMMON_NAME, type = CommandType.STRING, description = "The internet Common Name (CN) to be filtered.") + private String commonName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The ID of the domain to be filtered.") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "The ID of the account to be filtered.") + private Long accountId; + + @Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, description = "Whether to list all themes.") + private boolean listAll = false; + + @Parameter(name = ApiConstants.SHOW_REMOVED, type = CommandType.BOOLEAN, description = "Whether to list removed themes.") + private boolean showRemoved = false; + + @Parameter(name = ApiConstants.SHOW_PUBLIC, type = CommandType.BOOLEAN, description = "Whether to list public themes.") + private Boolean showPublic; + + @Parameter(name = ApiConstants.LIST_ONLY_DEFAULT_THEME, type = CommandType.BOOLEAN, description = "Whether to only list the default theme.") + private boolean listOnlyDefaultTheme = false; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getCommonName() { + return commonName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getAccountId() { + return accountId; + } + + public boolean getListAll() { + return listAll; + } + + public boolean getShowRemoved() { + return showRemoved; + } + + public Boolean getShowPublic() { + return showPublic; + } + + public boolean getListOnlyDefaultTheme() { + return listOnlyDefaultTheme; + } + + @Override + public void execute() { + ListResponse response = guiThemeService.listGuiThemes(this); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java new file mode 100644 index 00000000000..64164838eba --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java @@ -0,0 +1,60 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "removeGuiTheme", description = "Removes an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class RemoveGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuiThemeResponse.class, required = true, + description = "The unique identifier of the GUI theme to be removed.") + private Long id; + + public Long getId() { + return id; + } + + @Override + public void execute() { + guiThemeService.removeGuiTheme(this); + final SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + response.setSuccess(true); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java new file mode 100644 index 00000000000..daef2235ce8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java @@ -0,0 +1,136 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + + +@APICommand(name = "updateGuiTheme", description = "Updates an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class UpdateGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, required = true, type = CommandType.UUID, entityType = GuiThemeResponse.class, description = "The ID of the theme to be updated.") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, length = 2048, description = "A name to identify the theme.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, length = 4096, description = "A description for the theme.") + private String description; + + @Parameter(name = ApiConstants.CSS, type = CommandType.STRING, length = 65535, description = "The CSS to be retrieved and imported into the GUI " + + "when matching the theme access configurations.") + private String css; + + @Parameter(name = ApiConstants.JSON_CONFIGURATION, type = CommandType.STRING, length = 65535, description = "The JSON with the configurations to be " + + "retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @Parameter(name = ApiConstants.COMMON_NAMES, type = CommandType.STRING, length = 65535, description = "A set of Common Names (CN) (fixed or " + + "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, length = 65535, description = "A set of domain UUIDs (also known as ID for " + + "the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @Parameter(name = ApiConstants.RECURSIVE_DOMAINS, type = CommandType.BOOLEAN, description = "Defines whether the subdomains of the informed domains are considered. Default " + + "value is false.") + private Boolean recursiveDomains = false; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.STRING, length = 65535, description = "A set of account UUIDs (also known as ID for" + + " the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Defines whether a theme can be retrieved by anyone when only " + + "the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is considered as `false`.") + private Boolean isPublic = true; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + public Boolean getIsPublic() { + return isPublic; + } + + @Override + public void execute() { + GuiThemeJoin guiThemeJoin = guiThemeService.updateGuiTheme(this); + + if (guiThemeJoin == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update the GUI theme."); + } + + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoin); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java index 4e3cf4621ef..18af5b2973e 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.user.Account; 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, diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java index 292e1c6f099..78f1a4bcdee 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java @@ -104,7 +104,7 @@ public class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { @Override public void execute() { - boolean result = _templateService.detachIso(virtualMachineId, isForced()); + boolean result = _templateService.detachIso(virtualMachineId, null, isForced()); if (result) { UserVm userVm = _entityMgr.findById(UserVm.class, virtualMachineId); UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", userVm).get(0); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java index 5db680066a6..4d11f0dc3d2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.iso; +import com.cloud.dc.DataCenter; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -101,7 +102,15 @@ public class ExtractIsoCmd extends BaseAsyncCmd { @Override public String getEventDescription() { - return "extracting ISO: " + getId() + " from zone: " + getZoneId(); + String isoId = this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()); + String baseDescription = String.format("Extracting ISO: %s", isoId); + + Long zoneId = getZoneId(); + if (zoneId == null) { + return baseDescription; + } + + return String.format("%s from zone: %s", baseDescription, this._uuidMgr.getUuid(DataCenter.class, zoneId)); } @Override @@ -120,7 +129,7 @@ public class ExtractIsoCmd extends BaseAsyncCmd { 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/ListIsosCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java index 04dcbf8ca96..346eca8cff0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java @@ -16,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.BaseListTaggedResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import 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 { @@ -59,7 +57,7 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "true if the ISO is publicly available to all users, false otherwise.") private Boolean publicIso; - @Parameter(name = ApiConstants.IS_READY, type = CommandType.BOOLEAN, description = "true if this ISO is ready to be deployed") + @Parameter(name = ApiConstants.IS_READY, type = CommandType.BOOLEAN, description = "list ISOs that are ready to be deployed") private Boolean ready; @Parameter(name = ApiConstants.ISO_FILTER, @@ -88,6 +86,16 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @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", + 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 class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { 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 class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Override public void execute() { ListResponse response = _queryService.listIsos(this); - if (response != null && response.getCount() > 0 && getShowIcon()) { - updateIsoResponse(response.getResponses()); + if (response != null && getShowIcon()) { + _responseGenerator.updateTemplateIsoResponsesForIcons(response.getResponses(), + ResourceTag.ResourceObjectType.ISO); } response.setResponseName(getCommandName()); setResponseObject(response); } - private void updateIsoResponse(List response) { - for (TemplateResponse templateResponse : response) { - ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.ISO, templateResponse.getId()); - if (resourceIcon == null) { - continue; - } - ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); - templateResponse.setResourceIconResponse(iconResponse); - } - } - public Long getStoragePoolId() { return null; }; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java index becfdcd653d..81f52552289 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 @@ package org.apache.cloudstack.api.command.user.iso; 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; @@ -118,6 +119,11 @@ public class RegisterIsoCmd extends BaseCmd implements UserCmd { 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", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -229,6 +235,14 @@ public class RegisterIsoCmd extends BaseCmd implements UserCmd { return passwordEnabled == null ? false : passwordEnabled; } + public void setArch(String arch) { + this.arch = arch; + } + + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 81a52ce2dfe..c4424b1d937 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 @@ -72,7 +72,7 @@ public class AssignToLoadBalancerRuleCmd extends BaseAsyncCmd { @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; 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 f86d1ae85da..34798c4efe1 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 @@ -104,7 +104,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements L @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; 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 127661b1820..8d8e598bcab 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 @@ -113,6 +113,10 @@ public class CreateNetworkACLCmd extends BaseAsyncCreateCmd { return p; } + public void setProtocol(String protocol) { + this.protocol = protocol; + } + public List getSourceCidrList() { if (cidrlist != null) { return cidrlist; @@ -136,6 +140,9 @@ public class CreateNetworkACLCmd extends BaseAsyncCreateCmd { throw new InvalidParameterValueException("Invalid traffic type " + trafficType); } + public void setTrafficType(String trafficType) { + this.trafficType = trafficType; + } // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -144,15 +151,23 @@ public class CreateNetworkACLCmd extends BaseAsyncCreateCmd { 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 class CreateNetworkACLCmd extends BaseAsyncCreateCmd { 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,6 +230,10 @@ public class CreateNetworkACLCmd extends BaseAsyncCreateCmd { return aclId; } + public void setAclId(Long aclId) { + this.aclId = aclId; + } + public String getReason() { return reason; } 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 2395339a477..aca3d3ca1b4 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 @@ -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 class CreateNetworkCmd extends BaseCmd implements UserCmd { return NetUtils.standardizeIp6Cidr(ip6Cidr); } + public Integer getCidrSize() { + return cidrSize; + } + public Long getAclId() { return aclId; } @@ -391,6 +403,10 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { return ip6Dns2; } + public Long getAsNumber() { + return asNumber; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 33f452008d9..bdc89d804cd 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 @@ -110,6 +110,12 @@ public class ListNetworkOfferingsCmd extends BaseListCmd { @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 class ListNetworkOfferingsCmd extends BaseListCmd { return forVpc; } + public String getRoutingMode() { return routingMode; } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java new file mode 100644 index 00000000000..7146d1ae1d1 --- /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: " + getEntityId()); + 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 00000000000..16696f5f71b --- /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 ID=%s", 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 ID: " + 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 00000000000..3fdf3b0f5b4 --- /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 00000000000..c6f6034b1ba --- /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: " + getId()); + 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/ListServiceOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java index 1b3f531e370..3b693fe57b7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; @@ -110,6 +111,19 @@ public class ListServiceOfferingsCmd extends BaseListProjectAndAccountResourcesC since = "4.20.0") private Long templateId; + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, + type = CommandType.UUID, + entityType = VgpuProfileResponse.class, + description = "The ID of the vGPU profile that listed offerings must support", + since = "4.21.0") + private Long vgpuProfileId; + + @Parameter(name = ApiConstants.GPU_ENABLED, + type = CommandType.BOOLEAN, + description = "Flag to indicate if the service offering supports GPU. If set to true, only service offerings that support GPU will be returned.", + since = "4.21.0") + private Boolean gpuEnabled; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -171,6 +185,14 @@ public class ListServiceOfferingsCmd extends BaseListProjectAndAccountResourcesC return templateId; } + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public Boolean getGpuEnabled() { + return gpuEnabled; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java index 0ea22b38a37..49c6ee605c8 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 org.apache.cloudstack.context.CallContext; 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 { 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 52afd2b1760..3538a389a6e 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 @@ -62,6 +62,7 @@ public class UpdateResourceLimitCmd extends BaseCmd { + "2 - Volume. Number of disk volumes a user can create. " + "3 - Snapshot. Number of snapshots a user can create. " + "4 - Template. Number of templates that a user can register/create. " + + "5 - Project. Number of projects a user can create. " + "6 - Network. Number of guest network a user can create. " + "7 - VPC. Number of VPC a user can create. " + "8 - CPU. Total number of CPU cores a user can use. " 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 6bebdc09f59..75089cece8e 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 @@ -124,10 +124,10 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd { 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); @@ -153,7 +153,7 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException { - Snapshot snapshot = this._volumeService.allocSnapshotForVm(getVmId(), getVolumeId(), getSnapshotName()); + Snapshot snapshot = this._volumeService.allocSnapshotForVm(getVmId(), getVolumeId(), getSnapshotName(), getVMSnapshotId()); if (snapshot != null) { this.setEntityId(snapshot.getId()); this.setEntityUuid(snapshot.getUuid()); @@ -164,8 +164,9 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd { @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 vm snapshot {} with id {} and snapshot [id: {}, uuid: {}]", vmSnapshot, getVMSnapshotId(), getEntityId(), getEntityUuid()); + CallContext.current().setEventDetails("Vm Snapshot Id: " + vmSnapshot.getUuid()); Snapshot snapshot = null; try { snapshot = _snapshotService.backupSnapshotFromVmSnapshot(getEntityId(), getVmId(), getVolumeId(), getVMSnapshotId()); @@ -174,19 +175,19 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd { 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 vm 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 vm 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); } } } 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 00000000000..3f0f82ea4e3 --- /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 "Snapshot extraction job"; + } + + @Override + public void execute() { + CallContext.current().setEventDetails("Snapshot ID: " + this._uuidMgr.getUuid(Snapshot.class, getId())); + 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/storage/sharedfs/ChangeSharedFSDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSDiskOfferingCmd.java new file mode 100644 index 00000000000..b078ce4aae9 --- /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 " + 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 00000000000..70fb543d64c --- /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 " + 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 00000000000..ddaa31612a8 --- /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.finalyzeAccountId(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 00000000000..09fae53f128 --- /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 " + 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 00000000000..39b99218b66 --- /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 " + 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 00000000000..c52c691ac0b --- /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 00000000000..940e07225cf --- /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 00000000000..6e5bbaa4d8a --- /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 00000000000..576c472b6eb --- /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 " + 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 00000000000..bd384aceef7 --- /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 " + 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 00000000000..d6e0737144a --- /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 " + 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 00000000000..daad6cc78c5 --- /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/template/CreateTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java index 0a7bf291843..5f09ac6698d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import com.cloud.cpu.CPU; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -148,6 +149,11 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd { since = "4.19.0") private String accountName; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template. Valid options are: x86_64, aarch64. Defaults to x86_64", + since = "4.20.2") + private String arch; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -234,6 +240,10 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd { return accountName; } + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/DeleteTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/DeleteTemplateCmd.java index 245baf1e07e..5e9bf317fe1 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 @@ package org.apache.cloudstack.api.command.user.template; 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; @@ -53,6 +54,9 @@ public class DeleteTemplateCmd extends BaseAsyncCmd { @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 class DeleteTemplateCmd extends BaseAsyncCmd { return BooleanUtils.toBooleanDefaultIfNull(forced, false); } + public boolean getIsSystem() { + return BooleanUtils.toBooleanDefaultIfNull(isSystem, false); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ExtractTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ExtractTemplateCmd.java index ce6ba5e300c..22f59351e9a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ExtractTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ExtractTemplateCmd.java @@ -101,7 +101,15 @@ public class ExtractTemplateCmd extends BaseAsyncCmd { @Override public String getEventDescription() { - return "extracting template: " + this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()) + ((getZoneId() != null) ? " from zone: " + this._uuidMgr.getUuid(DataCenter.class, getZoneId()) : ""); + String templateId = this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()); + String baseDescription = String.format("Extracting template: %s", templateId); + + Long zoneId = getZoneId(); + if (zoneId == null) { + return baseDescription; + } + + return String.format("%s from zone: %s", baseDescription, this._uuidMgr.getUuid(DataCenter.class, zoneId)); } @Override @@ -120,8 +128,9 @@ public class ExtractTemplateCmd extends BaseAsyncCmd { 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"); 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 c878fda8240..330224a6055 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java @@ -22,6 +22,7 @@ import java.net.MalformedURLException; 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; @@ -55,6 +56,11 @@ public class GetUploadParamsForTemplateCmd extends AbstractGetUploadParamsCmd { 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", + 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; @@ -93,6 +99,11 @@ public class GetUploadParamsForTemplateCmd extends AbstractGetUploadParamsCmd { description = "(VMware only) true if VM deployments should preserve all the configurations defined for this template", since = "4.15.1") private Boolean deployAsIs; + @Parameter(name=ApiConstants.FOR_CKS, + type = CommandType.BOOLEAN, + description = "if true, the templates would be available for deploying CKS clusters", since = "4.21.0") + protected Boolean forCks; + public String getDisplayText() { return StringUtils.isBlank(displayText) ? getName() : displayText; } @@ -162,6 +173,14 @@ public class GetUploadParamsForTemplateCmd extends AbstractGetUploadParamsCmd { Boolean.TRUE.equals(deployAsIs); } + public boolean isForCks() { + return Boolean.TRUE.equals(forCks); + } + + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + @Override public void execute() throws ServerApiException { validateRequest(); 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 113080257d0..223ac57b11f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java @@ -16,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,11 +28,18 @@ import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.ExtensionResponse; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.server.ResourceTag; import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; @@ -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", + 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,6 +198,13 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User boolean onlyReady = (templateFilter == TemplateFilter.featured) || (templateFilter == TemplateFilter.selfexecutable) || (templateFilter == TemplateFilter.sharedexecutable) || (templateFilter == TemplateFilter.executable && isAccountSpecific) || (templateFilter == TemplateFilter.community); + + if (!onlyReady) { + if (isReady() != null && isReady().booleanValue() != onlyReady) { + onlyReady = isReady().booleanValue(); + } + } + return onlyReady; } @@ -191,6 +223,27 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User 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 class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User @Override public void execute() { ListResponse response = _queryService.listTemplates(this); - if (response != null && response.getCount() > 0 && getShowIcon()) { - updateTemplateResponse(response.getResponses()); + if (response != null && getShowIcon()) { + _responseGenerator.updateTemplateIsoResponsesForIcons(response.getResponses(), + ResourceTag.ResourceObjectType.Template); } response.setResponseName(getCommandName()); setResponseObject(response); } - private void updateTemplateResponse(List response) { - for (TemplateResponse templateResponse : response) { - ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Template, templateResponse.getId()); - if (resourceIcon == null) { - continue; - } - ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); - templateResponse.setResourceIconResponse(iconResponse); - } - } - public List getIds() { if (ids == null) { return Collections.emptyList(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java index 1e5c4af9c15..5d5cab219c1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java @@ -16,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.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ExtensionResponse; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ProjectResponse; @@ -42,7 +41,10 @@ import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.exception.ResourceAllocationException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.HypervisorGuru; import com.cloud.template.VirtualMachineTemplate; @APICommand(name = "registerTemplate", description = "Registers an existing template into the CloudStack cloud. ", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, @@ -167,11 +169,29 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { description = "(VMware only) true if VM deployments should preserve all the configurations defined for this template", since = "4.15.1") protected Boolean deployAsIs; + @Parameter(name=ApiConstants.FOR_CKS, + type = CommandType.BOOLEAN, + description = "if true, the templates would be available for deploying CKS clusters", since = "4.21.0") + protected Boolean forCks; + @Parameter(name = ApiConstants.TEMPLATE_TYPE, type = CommandType.STRING, description = "the type of the template. Valid options are: USER/VNF (for all users) and SYSTEM/ROUTING/BUILTIN (for admins only).", since = "4.19.0") private String templateType; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + + @Parameter(name = ApiConstants.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 /////////////////////// ///////////////////////////////////////////////////// @@ -289,10 +309,26 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { 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/////////////////// ///////////////////////////////////////////////////// @@ -352,7 +388,9 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { "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)); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmd.java index dbbd771293a..20849d1ba6c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmd.java @@ -46,6 +46,11 @@ public class UpdateTemplateCmd extends BaseUpdateTemplateOrIsoCmd implements Use @Parameter(name = ApiConstants.TEMPLATE_TAG, type = CommandType.STRING, description = "the tag for this template.", since = "4.20.0") private String templateTag; + @Parameter(name = ApiConstants.FOR_CKS, type = CommandType.BOOLEAN, + description = "indicates that the template can be used for deployment of CKS clusters", + since = "4.21.0") + private Boolean forCks; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -63,6 +68,10 @@ public class UpdateTemplateCmd extends BaseUpdateTemplateOrIsoCmd implements Use return templateTag; } + public Boolean getForCks() { + return forCks; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/BaseRegisterUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/BaseRegisterUserDataCmd.java new file mode 100644 index 00000000000..c002bd226a0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/BaseRegisterUserDataCmd.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.userdata; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.NetworkModel; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public abstract class BaseRegisterUserDataCmd extends BaseCmd { + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the user data") + private String name; + + //Owner information + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the user data. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "an optional domainId for the user data. If the account parameter is used, domainId must also be used.") + private Long domainId; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the user data") + private Long projectId; + + @Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING, description = "comma separated list of variables declared in user data content") + private String params; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getProjectId() { + return projectId; + } + + public String getParams() { + checkForVRMetadataFileNames(params); + return params; + } + + public void checkForVRMetadataFileNames(String params) { + if (StringUtils.isNotEmpty(params)) { + List keyValuePairs = new ArrayList<>(Arrays.asList(params.split(","))); + keyValuePairs.retainAll(NetworkModel.metadataFileNames); + if (!keyValuePairs.isEmpty()) { + throw new InvalidParameterValueException(String.format("Params passed here have a few virtual router metadata file names %s", keyValuePairs)); + } + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteCniConfigurationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteCniConfigurationCmd.java new file mode 100644 index 00000000000..8faa612d3d9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteCniConfigurationCmd.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.userdata; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.user.Account; +import com.cloud.user.UserData; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +@APICommand(name = "deleteCniConfiguration", description = "Deletes a CNI Configuration", responseObject = SuccessResponse.class, entityType = {UserData.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DeleteCniConfigurationCmd extends DeleteUserDataCmd { + + public static final Logger logger = LogManager.getLogger(DeleteCniConfigurationCmd.class.getName()); + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + boolean result = _mgr.deleteCniConfiguration(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess(result); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete CNI configuration"); + } + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + Long domainId = this.getDomainId(); + String accountName = this.getAccountName(); + if ((account == null || _accountService.isAdmin(account.getId())) && (domainId != null && accountName != null)) { + Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); + if (userAccount != null) { + return userAccount.getId(); + } + } + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListCniConfigurationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListCniConfigurationCmd.java new file mode 100644 index 00000000000..3a0c3f1168b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListCniConfigurationCmd.java @@ -0,0 +1,59 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.userdata; + +import com.cloud.user.UserData; +import com.cloud.utils.Pair; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserDataResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listCniConfiguration", description = "List user data for CNI plugins", responseObject = UserDataResponse.class, entityType = {UserData.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListCniConfigurationCmd extends ListUserDataCmd { + public static final Logger logger = LogManager.getLogger(ListCniConfigurationCmd.class.getName()); + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + Pair, Integer> resultList = _mgr.listUserDatas(this, true); + List responses = new ArrayList<>(); + for (UserData result : resultList.first()) { + UserDataResponse r = _responseGenerator.createUserDataResponse(result); + r.setObjectName(ApiConstants.CNI_CONFIG); + responses.add(r); + } + + ListResponse response = new ListResponse<>(); + response.setResponses(responses, resultList.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java index 64ab3ec3d70..16bf1e5c1e4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java @@ -61,7 +61,7 @@ public class ListUserDataCmd extends BaseListProjectAndAccountResourcesCmd { @Override public void execute() { - Pair, Integer> resultList = _mgr.listUserDatas(this); + Pair, Integer> resultList = _mgr.listUserDatas(this, false); List responses = new ArrayList<>(); for (UserData result : resultList.first()) { UserDataResponse r = _responseGenerator.createUserDataResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterCniConfigurationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterCniConfigurationCmd.java new file mode 100644 index 00000000000..eb80da3be05 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterCniConfigurationCmd.java @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.userdata; + +import com.cloud.user.UserData; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.UserDataResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@APICommand(name = "registerCniConfiguration", + description = "Register a CNI Configuration to be used with CKS cluster", + since = "4.21.0", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class RegisterCniConfigurationCmd extends BaseRegisterUserDataCmd { + public static final Logger logger = LogManager.getLogger(RegisterCniConfigurationCmd.class.getName()); + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.CNI_CONFIG, type = CommandType.STRING, description = "CNI Configuration content to be registered as User data", length = 1048576) + private String cniConfig; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getCniConfig() { + return cniConfig; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + UserData result = _mgr.registerCniConfiguration(this); + UserDataResponse response = _responseGenerator.createUserDataResponse(result); + response.setResponseName(getCommandName()); + response.setObjectName(ApiConstants.CNI_CONFIG); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true); + if (accountId == null) { + return CallContext.current().getCallingAccount().getId(); + } + + return accountId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java index 8df25541a19..185c9c05b1c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java @@ -16,115 +16,50 @@ // under the License. package org.apache.cloudstack.api.command.user.userdata; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.commons.lang3.StringUtils; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.network.NetworkModel; import com.cloud.user.UserData; @APICommand(name = "registerUserData", - description = "Register a new userdata.", + description = "Register a new User Data.", since = "4.18", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) -public class RegisterUserDataCmd extends BaseCmd { +public class RegisterUserDataCmd extends BaseRegisterUserDataCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the userdata") - private String name; - - //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the userdata. Must be used with domainId.") - private String accountName; - - @Parameter(name = ApiConstants.DOMAIN_ID, - type = CommandType.UUID, - entityType = DomainResponse.class, - description = "an optional domainId for the userdata. If the account parameter is used, domainId must also be used.") - private Long domainId; - - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the userdata") - private Long projectId; - - @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, required = true, description = "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; - - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public String getName() { - return name; - } - - public String getAccountName() { - return accountName; - } - - public Long getDomainId() { - return domainId; - } - - public Long getProjectId() { - return projectId; - } + @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, required = true, description = "User data content", length = 1048576) + protected String userData; public String getUserData() { return userData; } - public String getParams() { - checkForVRMetadataFileNames(params); - return params; - } - - public void checkForVRMetadataFileNames(String params) { - if (StringUtils.isNotEmpty(params)) { - List keyValuePairs = new ArrayList<>(Arrays.asList(params.split(","))); - keyValuePairs.retainAll(NetworkModel.metadataFileNames); - if (!keyValuePairs.isEmpty()) { - throw new InvalidParameterValueException(String.format("Params passed here have a few virtual router metadata file names %s", keyValuePairs)); - } - } - } - ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java index 0dc3dcdbdcc..e76a75ae398 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 @@ -79,7 +79,7 @@ public class AddIpToVmNicCmd extends BaseAsyncCreateCmd { 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 diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java new file mode 100644 index 00000000000..ecbde47692f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java @@ -0,0 +1,848 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; +import org.apache.cloudstack.api.BaseAsyncCreateCustomIdCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.UserDataResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.agent.api.LogLevel; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.Network.IpAddresses; +import com.cloud.offering.DiskOffering; +import com.cloud.template.VirtualMachineTemplate; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.VmDetailConstants; +import com.cloud.vm.VmDiskInfo; +import com.cloud.utils.net.Dhcp; + +public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityGroupAction, UserCmd { + + private static final String s_name = "deployvirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "availability zone for the virtual machine") + private Long zoneId; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "host name for the virtual machine", validations = {ApiArgValidator.RFCComplianceDomainName}) + private String name; + + @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "an optional user generated name for the virtual machine") + private String displayName; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The password of the virtual machine. If null, a random password will be generated for the VM.", + since="4.19.0.0") + protected String password; + + //Owner information + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used. If account is NOT provided then virtual machine will be assigned to the caller account and domain.") + private Long domainId; + + //Network information + //@ACL(accessType = AccessType.UseEntry) + @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, description = "list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter") + private List networkIds; + + @Parameter(name = ApiConstants.BOOT_TYPE, type = CommandType.STRING, required = false, description = "Guest VM Boot option either custom[UEFI] or default boot [BIOS]. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") + private String bootType; + + @Parameter(name = ApiConstants.BOOT_MODE, type = CommandType.STRING, required = false, description = "Boot Mode [Legacy] or [Secure] Applicable when Boot Type Selected is UEFI, otherwise Legacy only for BIOS. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") + private String bootMode; + + @Parameter(name = ApiConstants.BOOT_INTO_SETUP, type = CommandType.BOOLEAN, required = false, description = "Boot into hardware setup or not (ignored if startVm = false, only valid for vmware)", since = "4.15.0.0") + private Boolean bootIntoSetup; + + //DataDisk information + @ACL + @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine. If the template is of ISO format," + + " the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the " + + "offering for the data disk volume. If the templateId parameter passed is from a Template object," + + " the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is " + + "from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") + private Long diskOfferingId; + + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId") + private Long size; + + @Parameter(name = ApiConstants.ROOT_DISK_SIZE, + type = CommandType.LONG, + description = "Optional field to resize root disk on deploy. Value is in GB. Only applies to template-based deployments. Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided", + since = "4.4") + private Long rootdisksize; + + @Parameter(name = ApiConstants.DATADISKS_DETAILS, + type = CommandType.MAP, + since = "4.21.0", + description = "Disk offering details for creating multiple data volumes. Mutually exclusive with diskOfferingId." + + " Example: datadisksdetails[0].diskofferingid=a2a73a84-19db-4852-8930-dfddef053341&datadisksdetails[0].size=10&datadisksdetails[0].miniops=100&datadisksdetails[0].maxiops=200") + private Map dataDisksDetails; + + @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine") + private String group; + + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor on which to deploy the virtual machine. " + + "The parameter is required and respected only when hypervisor info is not set on the ISO/Template passed to the call") + private String hypervisor; + + @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, + description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. " + + "This binary data must be base64 encoded before adding it to the request. " + + "Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. " + + "Using HTTP POST (via POST body), you can send up to 1MB of data after base64 encoding. " + + "You also need to change vm.userdata.max.length value", + length = 1048576) + private String userData; + + @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.18") + private Long userdataId; + + @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, description = "used to specify the parameters values for the variables in userdata.", since = "4.18") + private Map userdataDetails; + + @Deprecated + @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "name of the ssh key pair used to login to the virtual machine") + private String sshKeyPairName; + + @Parameter(name = ApiConstants.SSH_KEYPAIRS, type = CommandType.LIST, collectionType = CommandType.STRING, since="4.17", description = "names of the ssh key pairs used to login to the virtual machine") + private List sshKeyPairNames; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "destination Host ID to deploy the VM to - parameter available for root admin only") + private Long hostId; + + @ACL + @Parameter(name = ApiConstants.SECURITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups id that going to be applied to the virtual machine. " + + "Should be passed only when vm is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") + private List securityGroupIdList; + + @ACL + @Parameter(name = ApiConstants.SECURITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups names that going to be applied to the virtual machine." + + " Should be passed only when vm is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") + private List securityGroupNameList; + + @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter." + + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid&iptonetworklist[0].mac=aa:bb:cc:dd:ee::ff - requests to use ip 10.10.10.11 in network id=uuid") + private Map ipToNetworkList; + + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the ip address for default vm's network") + private String ipAddress; + + @Parameter(name = ApiConstants.IP6_ADDRESS, type = CommandType.STRING, description = "the ipv6 address for default vm's network") + private String ip6Address; + + @Parameter(name = ApiConstants.MAC_ADDRESS, type = CommandType.STRING, description = "the mac address for default vm's network") + private String macAddress; + + @Parameter(name = ApiConstants.KEYBOARD, type = CommandType.STRING, description = "an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us") + private String keyboard; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Deploy vm for the project") + private Long projectId; + + @Parameter(name = ApiConstants.START_VM, type = CommandType.BOOLEAN, description = "true if start vm after creating; defaulted to true if not specified") + private Boolean startVm; + + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are going to be applied to the virtual machine." + + " Mutually exclusive with affinitygroupnames parameter") + private List affinityGroupIdList; + + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are going to be applied to the virtual machine." + + "Mutually exclusive with affinitygroupids parameter") + private List affinityGroupNameList; + + @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, since = "4.2", description = "an optional field, whether to the display the vm to the end user or not.", authorized = {RoleType.Admin}) + private Boolean displayVm; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.3", description = "used to specify the custom parameters. 'extraconfig' is not allowed to be passed in details") + private Map details; + + @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "Deployment planner to use for vm allocation. Available to ROOT admin only", since = "4.4", authorized = { RoleType.Admin }) + private String deploymentPlanner; + + @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the VM on start up" + + " Example: dhcpoptionsnetworklist[0].dhcp:114=url&dhcpoptionsetworklist[0].networkid=networkid&dhcpoptionsetworklist[0].dhcp:66=www.test.com") + private Map dhcpOptionsNetworkList; + + @Parameter(name = ApiConstants.DATADISK_OFFERING_LIST, type = CommandType.MAP, since = "4.11", description = "datadisk template to disk-offering mapping;" + + " an optional parameter used to create additional data disks from datadisk templates; can't be specified with diskOfferingId parameter") + private Map dataDiskTemplateToDiskOfferingList; + + @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, since = "4.12", description = "an optional URL encoded string that can be passed to the virtual machine upon successful deployment", length = 5120) + private String extraConfig; + + @Parameter(name = ApiConstants.COPY_IMAGE_TAGS, type = CommandType.BOOLEAN, since = "4.13", description = "if true the image tags (if any) will be copied to the VM, default value is false") + private Boolean copyImageTags; + + @Parameter(name = ApiConstants.PROPERTIES, type = CommandType.MAP, since = "4.15", + description = "used to specify the vApp properties.") + @LogLevel(LogLevel.Log4jLevel.Off) + private Map vAppProperties; + + @Parameter(name = ApiConstants.NIC_NETWORK_LIST, type = CommandType.MAP, since = "4.15", + description = "VMware only: used to specify network mapping of a vApp VMware template registered \"as-is\"." + + " Example nicnetworklist[0].ip=Nic-101&nicnetworklist[0].network=uuid") + @LogLevel(LogLevel.Log4jLevel.Off) + private Map vAppNetworks; + + @Parameter(name = ApiConstants.DYNAMIC_SCALING_ENABLED, type = CommandType.BOOLEAN, since = "4.16", + description = "true if virtual machine needs to be dynamically scalable") + protected Boolean dynamicScalingEnabled; + + @Parameter(name = ApiConstants.OVERRIDE_DISK_OFFERING_ID, type = CommandType.UUID, since = "4.17", entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine to be used for root volume instead of the disk offering mapped in service offering." + + "In case of virtual machine deploying from ISO, then the diskofferingid specified for root volume is ignored and uses this override disk offering id") + private Long overrideDiskOfferingId; + + @Parameter(name = ApiConstants.IOTHREADS_ENABLED, type = CommandType.BOOLEAN, required = false, + description = "IOThreads are dedicated event loop threads for supported disk devices to perform block I/O requests in order to improve scalability especially on an SMP host/guest with many LUNs.") + private Boolean iothreadsEnabled; + + @Parameter(name = ApiConstants.IO_DRIVER_POLICY, type = CommandType.STRING, description = "Controls specific policies on IO") + private String ioDriverPolicy; + + @Parameter(name = ApiConstants.NIC_MULTIQUEUE_NUMBER, type = CommandType.INTEGER, since = "4.18", + description = "The number of queues for multiqueue NICs.") + private Integer nicMultiqueueNumber; + + @Parameter(name = ApiConstants.NIC_PACKED_VIRTQUEUES_ENABLED, type = CommandType.BOOLEAN, since = "4.18", + description = "Enable packed virtqueues or not.") + private Boolean nicPackedVirtQueues; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION, type = CommandType.INTEGER, since = "4.21.0", + description = "Number of days instance is leased for.") + private Integer leaseDuration; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0", + description = "Lease expiry action, valid values are STOP and DESTROY") + private String leaseExpiryAction; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].server.type=typevalue", + since = "4.21.0") + protected Map externalDetails; + + private List dataDiskInfoList; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public String getAccountName() { + if (accountName == null) { + return CallContext.current().getCallingAccount().getAccountName(); + } + return accountName; + } + + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public String getDeploymentPlanner() { + return deploymentPlanner; + } + + public String getDisplayName() { + return displayName; + } + + public Long getDomainId() { + if (domainId == null) { + return CallContext.current().getCallingAccount().getDomainId(); + } + return domainId; + } + + public ApiConstants.BootType getBootType() { + if (StringUtils.isNotBlank(bootType)) { + try { + String type = bootType.trim().toUpperCase(); + return ApiConstants.BootType.valueOf(type); + } catch (IllegalArgumentException e) { + String errMesg = "Invalid bootType " + bootType + "Specified for vm " + getName() + + " Valid values are: " + Arrays.toString(ApiConstants.BootType.values()); + logger.warn(errMesg); + throw new InvalidParameterValueException(errMesg); + } + } + return null; + } + + public Map getDetails() { + Map customparameterMap = convertDetailsToMap(details); + + if (getBootType() != null) { + customparameterMap.put(getBootType().toString(), getBootMode().toString()); + } + + if (rootdisksize != null && !customparameterMap.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { + customparameterMap.put(VmDetailConstants.ROOT_DISK_SIZE, rootdisksize.toString()); + } + + IoDriverPolicy ioPolicy = getIoDriverPolicy(); + if (ioPolicy != null) { + customparameterMap.put(VmDetailConstants.IO_POLICY, ioPolicy.toString()); + } + + if (BooleanUtils.toBoolean(iothreadsEnabled)) { + customparameterMap.put(VmDetailConstants.IOTHREADS, BooleanUtils.toStringTrueFalse(iothreadsEnabled)); + } + + if (nicMultiqueueNumber != null) { + customparameterMap.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber.toString()); + } + + if (BooleanUtils.toBoolean(nicPackedVirtQueues)) { + customparameterMap.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, BooleanUtils.toStringTrueFalse(nicPackedVirtQueues)); + } + + if (MapUtils.isNotEmpty(externalDetails)) { + customparameterMap.putAll(getExternalDetails()); + } + return customparameterMap; + } + + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + + public ApiConstants.BootMode getBootMode() { + if (StringUtils.isNotBlank(bootMode)) { + try { + String mode = bootMode.trim().toUpperCase(); + return ApiConstants.BootMode.valueOf(mode); + } catch (IllegalArgumentException e) { + String msg = String.format("Invalid %s: %s specified for VM: %s. Valid values are: %s", + ApiConstants.BOOT_MODE, bootMode, getName(), Arrays.toString(ApiConstants.BootMode.values())); + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + } + if (ApiConstants.BootType.UEFI.equals(getBootType())) { + String msg = String.format("%s must be specified for the VM with boot type: %s. Valid values are: %s", + ApiConstants.BOOT_MODE, getBootType(), Arrays.toString(ApiConstants.BootMode.values())); + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + return null; + } + + public Map getVmProperties() { + Map map = new HashMap<>(); + if (MapUtils.isNotEmpty(vAppProperties)) { + Collection parameterCollection = vAppProperties.values(); + Iterator iterator = parameterCollection.iterator(); + while (iterator.hasNext()) { + HashMap entry = (HashMap)iterator.next(); + map.put(entry.get("key"), entry.get("value")); + } + } + return map; + } + + public Map getVmNetworkMap() { + Map map = new HashMap<>(); + if (MapUtils.isNotEmpty(vAppNetworks)) { + Collection parameterCollection = vAppNetworks.values(); + Iterator iterator = parameterCollection.iterator(); + while (iterator.hasNext()) { + HashMap entry = (HashMap) iterator.next(); + Integer nic; + try { + nic = Integer.valueOf(entry.get(VmDetailConstants.NIC)); + } catch (NumberFormatException nfe) { + nic = null; + } + String networkUuid = entry.get(VmDetailConstants.NETWORK); + if (logger.isTraceEnabled()) { + logger.trace(String.format("nic, '%s', goes on net, '%s'", nic, networkUuid)); + } + if (nic == null || StringUtils.isEmpty(networkUuid) || _entityMgr.findByUuid(Network.class, networkUuid) == null) { + throw new InvalidParameterValueException(String.format("Network ID: %s for NIC ID: %s is invalid", networkUuid, nic)); + } + map.put(nic, _entityMgr.findByUuid(Network.class, networkUuid).getId()); + } + } + return map; + } + + public String getGroup() { + return group; + } + + public HypervisorType getHypervisor() { + return HypervisorType.getType(hypervisor); + } + + public Boolean isDisplayVm() { + return displayVm; + } + + @Override + public boolean isDisplay() { + if(displayVm == null) + return true; + else + return displayVm; + } + + public List getSecurityGroupNameList() { + return securityGroupNameList; + } + + public List getSecurityGroupIdList() { + return securityGroupIdList; + } + + public Long getSize() { + return size; + } + + public String getUserData() { + return userData; + } + + public Long getUserdataId() { + return userdataId; + } + + public Map getUserdataDetails() { + return convertDetailsToMap(userdataDetails); + } + + public Long getZoneId() { + return zoneId; + } + + public String getPassword() { + return password; + } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public VMLeaseManager.ExpiryAction getLeaseExpiryAction() { + if (StringUtils.isBlank(leaseExpiryAction)) { + return null; + } + VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction); + if (action == null) { + throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " + + com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values())); + } + return action; + } + + public List getNetworkIds() { + if (MapUtils.isNotEmpty(vAppNetworks)) { + if (CollectionUtils.isNotEmpty(networkIds) || ipAddress != null || getIp6Address() != null || MapUtils.isNotEmpty(ipToNetworkList)) { + throw new InvalidParameterValueException(String.format("%s can't be specified along with %s, %s, %s", ApiConstants.NIC_NETWORK_LIST, ApiConstants.NETWORK_IDS, ApiConstants.IP_ADDRESS, ApiConstants.IP_NETWORK_LIST)); + } else { + return new ArrayList<>(); + } + } + if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { + if ((networkIds != null && !networkIds.isEmpty()) || ipAddress != null || getIp6Address() != null) { + throw new InvalidParameterValueException("ipToNetworkMap can't be specified along with networkIds or ipAddress"); + } else { + List networks = new ArrayList(); + networks.addAll(getIpToNetworkMap().keySet()); + return networks; + } + } + return networkIds; + } + + public String getName() { + return name; + } + + public List getSSHKeyPairNames() { + List sshKeyPairs = new ArrayList(); + if(sshKeyPairNames != null) { + sshKeyPairs = sshKeyPairNames; + } + if(sshKeyPairName != null && !sshKeyPairName.isEmpty()) { + sshKeyPairs.add(sshKeyPairName); + } + return sshKeyPairs; + } + + public List getDataDiskInfoList() { + if (this.dataDiskInfoList != null) { + return this.dataDiskInfoList; + } + if (dataDisksDetails == null || dataDisksDetails.isEmpty()) { + return null; + } + if (dataDiskTemplateToDiskOfferingList != null) { + throw new InvalidParameterValueException("datadisktemplatetodiskofferinglist parameter can't be specified along with datadisksdetails parameter"); + } + List vmDiskInfoList = new ArrayList<>(); + Collection dataDisksCollection = dataDisksDetails.values(); + Iterator iter = dataDisksCollection.iterator(); + while (iter.hasNext()) { + HashMap dataDisk = (HashMap)iter.next(); + String diskOfferingUuid = dataDisk.get(ApiConstants.DISK_OFFERING_ID); + if (diskOfferingUuid == null) { + throw new InvalidParameterValueException("diskofferingid parameter is required for datadiskdetails"); + } + DiskOffering diskOffering = _entityMgr.findByUuid(DiskOffering.class, diskOfferingUuid); + if (diskOffering == null) { + throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingUuid); + } + if (diskOffering.isComputeOnly()) { + throw new InvalidParameterValueException(String.format("The disk offering id %d provided is directly mapped to a service offering, please provide an individual disk offering", diskOffering.getUuid())); + } + + Long size = null; + Long minIops = null; + Long maxIops = null; + if (dataDisk.get(ApiConstants.DEVICE_ID) == null) { + throw new InvalidParameterValueException("deviceid parameter is required for datadiskdetails"); + } + Long deviceId = Long.parseLong(dataDisk.get(ApiConstants.DEVICE_ID)); + if (diskOffering.isCustomized()) { + if (dataDisk.get(ApiConstants.SIZE) == null) { + throw new InvalidParameterValueException("Size is required for custom disk offering"); + } + size = Long.parseLong(dataDisk.get(ApiConstants.SIZE)); + } else { + size = diskOffering.getDiskSize() / (1024 * 1024 * 1024); + } + if (diskOffering.isCustomizedIops() != null && diskOffering.isCustomizedIops()) { + if (dataDisk.get(ApiConstants.MIN_IOPS) == null) { + throw new InvalidParameterValueException("Min IOPS is required for custom disk offering"); + } + if (dataDisk.get(ApiConstants.MAX_IOPS) == null) { + throw new InvalidParameterValueException("Max IOPS is required for custom disk offering"); + } + minIops = Long.parseLong(dataDisk.get(ApiConstants.MIN_IOPS)); + maxIops = Long.parseLong(dataDisk.get(ApiConstants.MAX_IOPS)); + } + VmDiskInfo vmDiskInfo = new VmDiskInfo(diskOffering, size, minIops, maxIops, deviceId); + vmDiskInfoList.add(vmDiskInfo); + } + this.dataDiskInfoList = vmDiskInfoList; + return dataDiskInfoList; + } + + public Long getHostId() { + return hostId; + } + + public boolean getStartVm() { + return startVm == null ? true : startVm; + } + + public Map getIpToNetworkMap() { + if ((networkIds != null || ipAddress != null || getIp6Address() != null) && ipToNetworkList != null) { + throw new InvalidParameterValueException("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter"); + } + LinkedHashMap ipToNetworkMap = null; + if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { + ipToNetworkMap = new LinkedHashMap(); + Collection ipsCollection = ipToNetworkList.values(); + Iterator iter = ipsCollection.iterator(); + while (iter.hasNext()) { + HashMap ips = (HashMap)iter.next(); + Long networkId = getNetworkIdFomIpMap(ips); + IpAddresses addrs = getIpAddressesFromIpMap(ips); + ipToNetworkMap.put(networkId, addrs); + } + } + + return ipToNetworkMap; + } + + @Nonnull + private IpAddresses getIpAddressesFromIpMap(HashMap ips) { + String requestedIp = ips.get("ip"); + String requestedIpv6 = ips.get("ipv6"); + String requestedMac = ips.get("mac"); + if (requestedIpv6 != null) { + requestedIpv6 = NetUtils.standardizeIp6Address(requestedIpv6); + } + if (requestedMac != null) { + if(!NetUtils.isValidMac(requestedMac)) { + throw new InvalidParameterValueException("Mac address is not valid: " + requestedMac); + } else if(!NetUtils.isUnicastMac(requestedMac)) { + throw new InvalidParameterValueException("Mac address is not unicast: " + requestedMac); + } + requestedMac = NetUtils.standardizeMacAddress(requestedMac); + } + return new IpAddresses(requestedIp, requestedIpv6, requestedMac); + } + + @Nonnull + private Long getNetworkIdFomIpMap(HashMap ips) { + Long networkId; + final String networkid = ips.get("networkid"); + Network network = _networkService.getNetwork(networkid); + if (network != null) { + networkId = network.getId(); + } else { + try { + networkId = Long.parseLong(networkid); + } catch (NumberFormatException e) { + throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + networkid); + } + } + return networkId; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getIp6Address() { + if (ip6Address == null) { + return null; + } + return NetUtils.standardizeIp6Address(ip6Address); + } + + + public String getMacAddress() { + if (macAddress == null) { + return null; + } + if(!NetUtils.isValidMac(macAddress)) { + throw new InvalidParameterValueException("Mac address is not valid: " + macAddress); + } else if(!NetUtils.isUnicastMac(macAddress)) { + throw new InvalidParameterValueException("Mac address is not unicast: " + macAddress); + } + return NetUtils.standardizeMacAddress(macAddress); + } + + public List getAffinityGroupIdList() { + if (affinityGroupNameList != null && affinityGroupIdList != null) { + throw new InvalidParameterValueException("affinitygroupids parameter is mutually exclusive with affinitygroupnames parameter"); + } + + // transform group names to ids here + if (affinityGroupNameList != null) { + List affinityGroupIds = new ArrayList(); + for (String groupName : affinityGroupNameList) { + Long groupId = _responseGenerator.getAffinityGroupId(groupName, getEntityOwnerId()); + if (groupId == null) { + throw new InvalidParameterValueException("Unable to find affinity group by name " + groupName); + } else { + affinityGroupIds.add(groupId); + } + } + return affinityGroupIds; + } else { + return affinityGroupIdList; + } + } + + public String getKeyboard() { + // TODO Auto-generated method stub + return keyboard; + } + + public Map> getDhcpOptionsMap() { + Map> dhcpOptionsMap = new HashMap<>(); + if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) { + + Collection> paramsCollection = this.dhcpOptionsNetworkList.values(); + for (Map dhcpNetworkOptions : paramsCollection) { + String networkId = dhcpNetworkOptions.get(ApiConstants.NETWORK_ID); + + if (networkId == null) { + throw new IllegalArgumentException("No networkid specified when providing extra dhcp options."); + } + + Map dhcpOptionsForNetwork = new HashMap<>(); + dhcpOptionsMap.put(networkId, dhcpOptionsForNetwork); + + for (String key : dhcpNetworkOptions.keySet()) { + if (key.startsWith(ApiConstants.DHCP_PREFIX)) { + int dhcpOptionValue = Integer.parseInt(key.replaceFirst(ApiConstants.DHCP_PREFIX, "")); + dhcpOptionsForNetwork.put(dhcpOptionValue, dhcpNetworkOptions.get(key)); + } else if (!key.equals(ApiConstants.NETWORK_ID)) { + Dhcp.DhcpOptionCode dhcpOptionEnum = Dhcp.DhcpOptionCode.valueOfString(key); + dhcpOptionsForNetwork.put(dhcpOptionEnum.getCode(), dhcpNetworkOptions.get(key)); + } + } + + } + } + + return dhcpOptionsMap; + } + + public Map getDataDiskTemplateToDiskOfferingMap() { + if (diskOfferingId != null && dataDiskTemplateToDiskOfferingList != null) { + throw new InvalidParameterValueException("diskofferingid parameter can't be specified along with datadisktemplatetodiskofferinglist parameter"); + } + if (MapUtils.isEmpty(dataDiskTemplateToDiskOfferingList)) { + return new HashMap(); + } + + HashMap dataDiskTemplateToDiskOfferingMap = new HashMap(); + for (Object objDataDiskTemplates : dataDiskTemplateToDiskOfferingList.values()) { + HashMap dataDiskTemplates = (HashMap) objDataDiskTemplates; + Long dataDiskTemplateId; + DiskOffering dataDiskOffering = null; + VirtualMachineTemplate dataDiskTemplate= _entityMgr.findByUuid(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); + if (dataDiskTemplate == null) { + dataDiskTemplate = _entityMgr.findById(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); + if (dataDiskTemplate == null) + throw new InvalidParameterValueException("Unable to translate and find entity with datadisktemplateid " + dataDiskTemplates.get("datadisktemplateid")); + } + dataDiskTemplateId = dataDiskTemplate.getId(); + dataDiskOffering = _entityMgr.findByUuid(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); + if (dataDiskOffering == null) { + dataDiskOffering = _entityMgr.findById(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); + if (dataDiskOffering == null) + throw new InvalidParameterValueException("Unable to translate and find entity with diskofferingId " + dataDiskTemplates.get("diskofferingid")); + } + dataDiskTemplateToDiskOfferingMap.put(dataDiskTemplateId, dataDiskOffering); + } + return dataDiskTemplateToDiskOfferingMap; + } + + public String getExtraConfig() { + return extraConfig; + } + + public boolean getCopyImageTags() { + return copyImageTags == null ? false : copyImageTags; + } + + public Boolean getBootIntoSetup() { + return bootIntoSetup; + } + + public boolean isDynamicScalingEnabled() { + return dynamicScalingEnabled == null ? true : dynamicScalingEnabled; + } + + public Long getOverrideDiskOfferingId() { + return overrideDiskOfferingId; + } + + public IoDriverPolicy getIoDriverPolicy() { + if (StringUtils.isNotBlank(ioDriverPolicy)) { + try { + String policyType = ioDriverPolicy.trim().toUpperCase(); + return IoDriverPolicy.valueOf(policyType); + } catch (IllegalArgumentException e) { + String errMesg = String.format("Invalid io policy %s specified for vm %s. Valid values are: %s", ioDriverPolicy, getName(), Arrays.toString(IoDriverPolicy.values())); + logger.warn(errMesg); + throw new InvalidParameterValueException(errMesg); + } + } + return null; + } + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + if (accountId == null) { + return CallContext.current().getCallingAccount().getId(); + } + + return accountId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_CREATE; + } + + @Override + public String getCreateEventType() { + return EventTypes.EVENT_VM_CREATE; + } + + @Override + public String getCreateEventDescription() { + return "creating Vm"; + } + + @Override + public String getEventDescription() { + if(getStartVm()) { + return "starting Vm. Vm Id: " + getEntityUuid(); + } + return "deploying Vm. Vm Id: " + getEntityUuid(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.VirtualMachine; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java new file mode 100644 index 00000000000..04e413ed67a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java @@ -0,0 +1,153 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.UserVmResponse; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine; + +@APICommand(name = "createVMFromBackup", + description = "Creates and automatically starts a VM from a backup.", + responseObject = UserVmResponse.class, + responseView = ResponseObject.ResponseView.Restricted, + entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, + since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateVMFromBackupCmd extends BaseDeployVMCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.BACKUP_ID, + type = CommandType.UUID, + entityType = BackupResponse.class, + required = true, + description = "backup ID to create the VM from") + private Long backupId; + + @ACL + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "the ID of the service offering for the virtual machine") + private Long serviceOfferingId; + + @ACL + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "the ID of the template for the virtual machine") + private Long templateId; + + @Parameter(name = ApiConstants.PRESERVE_IP, type = CommandType.BOOLEAN, description = "Use the same IP/MAC addresses as stored in the backup metadata. Works only if the original Instance is deleted and the IP/MAC address is available.") + private Boolean preserveIp; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getBackupId() { + return backupId; + } + + public Long getServiceOfferingId() { + return serviceOfferingId; + } + + public Long getTemplateId() { + return templateId; + } + + public boolean getPreserveIp() { + return (preserveIp != null) ? preserveIp : false; + } + + @Override + public void create() { + UserVm vm; + try { + vm = _userVmService.allocateVMFromBackup(this); + if (vm != null) { + setEntityId(vm.getId()); + setEntityUuid(vm.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy vm"); + } + } catch (InsufficientCapacityException ex) { + logger.info(ex); + logger.trace(ex.getMessage(), ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); + } catch (ResourceUnavailableException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (ConcurrentOperationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (ResourceAllocationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); + } + } + + @Override + public void execute () { + UserVm vm = null; + try { + vm = _userVmService.restoreVMFromBackup(this); + } catch (ResourceUnavailableException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (ResourceAllocationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); + } catch (ConcurrentOperationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (InsufficientCapacityException ex) { + StringBuilder message = new StringBuilder(ex.getMessage()); + if (ex instanceof InsufficientServerCapacityException) { + if (((InsufficientServerCapacityException)ex).isAffinityApplied()) { + message.append(", Please check the affinity groups provided, there may not be sufficient capacity to follow them"); + } + } + logger.info(String.format("%s: %s", message.toString(), ex.getLocalizedMessage())); + logger.debug(message.toString(), ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, message.toString()); + } + + if (vm != null) { + UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", vm).get(0); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy vm uuid:"+getEntityUuid()); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java index 935f39bf4dd..5811eb1abfc 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 @@ -68,7 +68,7 @@ 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 VM (start/stop/reboot/force_stop/force_reboot).") private String action; @Parameter(name = ApiConstants.START_DATE, 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 446bdf30f07..dc1ca3583cc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -16,756 +16,77 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; -import javax.annotation.Nonnull; - -import org.apache.cloudstack.acl.RoleType; -import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCreateCustomIdCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.user.UserCmd; -import org.apache.cloudstack.api.response.DiskOfferingResponse; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.NetworkResponse; -import org.apache.cloudstack.api.response.ProjectResponse; -import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.SnapshotResponse; import org.apache.cloudstack.api.response.TemplateResponse; -import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import com.cloud.agent.api.LogLevel; -import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientServerCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.network.Network; -import com.cloud.network.Network.IpAddresses; -import com.cloud.offering.DiskOffering; -import com.cloud.template.VirtualMachineTemplate; import com.cloud.uservm.UserVm; -import com.cloud.utils.net.Dhcp; -import com.cloud.utils.net.NetUtils; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VmDetailConstants; @APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityGroupAction, UserCmd { - - private static final String s_name = "deployvirtualmachineresponse"; +public class DeployVMCmd extends BaseDeployVMCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "availability zone for the virtual machine") - private Long zoneId; - @ACL @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the ID of the service offering for the virtual machine") private Long serviceOfferingId; @ACL - @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the ID of the template for the virtual machine") + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "the ID of the template for the virtual machine") private Long templateId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "host name for the virtual machine") - private String name; + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, since = "4.21") + private Long volumeId; - @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "an optional user generated name for the virtual machine") - private String displayName; - - @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The password of the virtual machine. If null, a random password will be generated for the VM.", - since="4.19.0.0") - protected String password; - - //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.") - private String accountName; - - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used. If account is NOT provided then virtual machine will be assigned to the caller account and domain.") - private Long domainId; - - //Network information - //@ACL(accessType = AccessType.UseEntry) - @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, description = "list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter") - private List networkIds; - - @Parameter(name = ApiConstants.BOOT_TYPE, type = CommandType.STRING, required = false, description = "Guest VM Boot option either custom[UEFI] or default boot [BIOS]. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") - private String bootType; - - @Parameter(name = ApiConstants.BOOT_MODE, type = CommandType.STRING, required = false, description = "Boot Mode [Legacy] or [Secure] Applicable when Boot Type Selected is UEFI, otherwise Legacy only for BIOS. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") - private String bootMode; - - @Parameter(name = ApiConstants.BOOT_INTO_SETUP, type = CommandType.BOOLEAN, required = false, description = "Boot into hardware setup or not (ignored if startVm = false, only valid for vmware)", since = "4.15.0.0") - private Boolean bootIntoSetup; - - //DataDisk information - @ACL - @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine. If the template is of ISO format," - + " the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the " - + "offering for the data disk volume. If the templateId parameter passed is from a Template object," - + " the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is " - + "from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") - private Long diskOfferingId; - - @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId") - private Long size; - - @Parameter(name = ApiConstants.ROOT_DISK_SIZE, - type = CommandType.LONG, - description = "Optional field to resize root disk on deploy. Value is in GB. Only applies to template-based deployments. Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided", - since = "4.4") - private Long rootdisksize; - - @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine") - private String group; - - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor on which to deploy the virtual machine. " - + "The parameter is required and respected only when hypervisor info is not set on the ISO/Template passed to the call") - private String hypervisor; - - @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, - description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 1MB of data after base64 encoding.", - length = 1048576) - private String userData; - - @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.18") - private Long userdataId; - - @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, description = "used to specify the parameters values for the variables in userdata.", since = "4.18") - private Map userdataDetails; - - @Deprecated - @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "name of the ssh key pair used to login to the virtual machine") - private String sshKeyPairName; - - @Parameter(name = ApiConstants.SSH_KEYPAIRS, type = CommandType.LIST, collectionType = CommandType.STRING, since="4.17", description = "names of the ssh key pairs used to login to the virtual machine") - private List sshKeyPairNames; - - @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "destination Host ID to deploy the VM to - parameter available for root admin only") - private Long hostId; - - @ACL - @Parameter(name = ApiConstants.SECURITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups id that going to be applied to the virtual machine. " - + "Should be passed only when vm is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") - private List securityGroupIdList; - - @ACL - @Parameter(name = ApiConstants.SECURITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups names that going to be applied to the virtual machine." - + " Should be passed only when vm is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") - private List securityGroupNameList; - - @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter." - + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid&iptonetworklist[0].mac=aa:bb:cc:dd:ee::ff - requests to use ip 10.10.10.11 in network id=uuid") - private Map ipToNetworkList; - - @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the ip address for default vm's network") - private String ipAddress; - - @Parameter(name = ApiConstants.IP6_ADDRESS, type = CommandType.STRING, description = "the ipv6 address for default vm's network") - private String ip6Address; - - @Parameter(name = ApiConstants.MAC_ADDRESS, type = CommandType.STRING, description = "the mac address for default vm's network") - private String macAddress; - - @Parameter(name = ApiConstants.KEYBOARD, type = CommandType.STRING, description = "an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us") - private String keyboard; - - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Deploy vm for the project") - private Long projectId; - - @Parameter(name = ApiConstants.START_VM, type = CommandType.BOOLEAN, description = "true if start vm after creating; defaulted to true if not specified") - private Boolean startVm; - - @ACL - @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are going to be applied to the virtual machine." - + " Mutually exclusive with affinitygroupnames parameter") - private List affinityGroupIdList; - - @ACL - @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are going to be applied to the virtual machine." - + "Mutually exclusive with affinitygroupids parameter") - private List affinityGroupNameList; - - @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, since = "4.2", description = "an optional field, whether to the display the vm to the end user or not.", authorized = {RoleType.Admin}) - private Boolean displayVm; - - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.3", description = "used to specify the custom parameters. 'extraconfig' is not allowed to be passed in details") - private Map details; - - @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "Deployment planner to use for vm allocation. Available to ROOT admin only", since = "4.4", authorized = { RoleType.Admin }) - private String deploymentPlanner; - - @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the VM on start up" - + " Example: dhcpoptionsnetworklist[0].dhcp:114=url&dhcpoptionsetworklist[0].networkid=networkid&dhcpoptionsetworklist[0].dhcp:66=www.test.com") - private Map dhcpOptionsNetworkList; - - @Parameter(name = ApiConstants.DATADISK_OFFERING_LIST, type = CommandType.MAP, since = "4.11", description = "datadisk template to disk-offering mapping;" + - " an optional parameter used to create additional data disks from datadisk templates; can't be specified with diskOfferingId parameter") - private Map dataDiskTemplateToDiskOfferingList; - - @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, since = "4.12", description = "an optional URL encoded string that can be passed to the virtual machine upon successful deployment", length = 5120) - private String extraConfig; - - @Parameter(name = ApiConstants.COPY_IMAGE_TAGS, type = CommandType.BOOLEAN, since = "4.13", description = "if true the image tags (if any) will be copied to the VM, default value is false") - private Boolean copyImageTags; - - @Parameter(name = ApiConstants.PROPERTIES, type = CommandType.MAP, since = "4.15", - description = "used to specify the vApp properties.") - @LogLevel(LogLevel.Log4jLevel.Off) - private Map vAppProperties; - - @Parameter(name = ApiConstants.NIC_NETWORK_LIST, type = CommandType.MAP, since = "4.15", - description = "VMware only: used to specify network mapping of a vApp VMware template registered \"as-is\"." + - " Example nicnetworklist[0].ip=Nic-101&nicnetworklist[0].network=uuid") - @LogLevel(LogLevel.Log4jLevel.Off) - private Map vAppNetworks; - - @Parameter(name = ApiConstants.DYNAMIC_SCALING_ENABLED, type = CommandType.BOOLEAN, since = "4.16", - description = "true if virtual machine needs to be dynamically scalable") - protected Boolean dynamicScalingEnabled; - - @Parameter(name = ApiConstants.OVERRIDE_DISK_OFFERING_ID, type = CommandType.UUID, since = "4.17", entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine to be used for root volume instead of the disk offering mapped in service offering." + - "In case of virtual machine deploying from ISO, then the diskofferingid specified for root volume is ignored and uses this override disk offering id") - private Long overrideDiskOfferingId; - - @Parameter(name = ApiConstants.IOTHREADS_ENABLED, type = CommandType.BOOLEAN, required = false, - description = "IOThreads are dedicated event loop threads for supported disk devices to perform block I/O requests in order to improve scalability especially on an SMP host/guest with many LUNs.") - private Boolean iothreadsEnabled; - - @Parameter(name = ApiConstants.IO_DRIVER_POLICY, type = CommandType.STRING, description = "Controls specific policies on IO") - private String ioDriverPolicy; - - @Parameter(name = ApiConstants.NIC_MULTIQUEUE_NUMBER, type = CommandType.INTEGER, since = "4.18", - description = "The number of queues for multiqueue NICs.") - private Integer nicMultiqueueNumber; - - @Parameter(name = ApiConstants.NIC_PACKED_VIRTQUEUES_ENABLED, type = CommandType.BOOLEAN, since = "4.18", - description = "Enable packed virtqueues or not.") - private Boolean nicPackedVirtQueues; + @Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.UUID, entityType = SnapshotResponse.class, since = "4.21") + private Long snapshotId; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getAccountName() { - if (accountName == null) { - return CallContext.current().getCallingAccount().getAccountName(); - } - return accountName; - } - - public Long getDiskOfferingId() { - return diskOfferingId; - } - - public String getDeploymentPlanner() { - return deploymentPlanner; - } - - public String getDisplayName() { - return displayName; - } - - public Long getDomainId() { - if (domainId == null) { - return CallContext.current().getCallingAccount().getDomainId(); - } - return domainId; - } - - public ApiConstants.BootType getBootType() { - if (StringUtils.isNotBlank(bootType)) { - try { - String type = bootType.trim().toUpperCase(); - return ApiConstants.BootType.valueOf(type); - } catch (IllegalArgumentException e) { - String errMesg = "Invalid bootType " + bootType + "Specified for vm " + getName() - + " Valid values are: " + Arrays.toString(ApiConstants.BootType.values()); - logger.warn(errMesg); - throw new InvalidParameterValueException(errMesg); - } - } - return null; - } - - public Map getDetails() { - Map customparameterMap = convertDetailsToMap(details); - - if (getBootType() != null) { - customparameterMap.put(getBootType().toString(), getBootMode().toString()); - } - - if (rootdisksize != null && !customparameterMap.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { - customparameterMap.put(VmDetailConstants.ROOT_DISK_SIZE, rootdisksize.toString()); - } - - IoDriverPolicy ioPolicy = getIoDriverPolicy(); - if (ioPolicy != null) { - customparameterMap.put(VmDetailConstants.IO_POLICY, ioPolicy.toString()); - } - - if (BooleanUtils.toBoolean(iothreadsEnabled)) { - customparameterMap.put(VmDetailConstants.IOTHREADS, BooleanUtils.toStringTrueFalse(iothreadsEnabled)); - } - - if (nicMultiqueueNumber != null) { - customparameterMap.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber.toString()); - } - - if (BooleanUtils.toBoolean(nicPackedVirtQueues)) { - customparameterMap.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, BooleanUtils.toStringTrueFalse(nicPackedVirtQueues)); - } - - return customparameterMap; - } - - - public ApiConstants.BootMode getBootMode() { - if (StringUtils.isNotBlank(bootMode)) { - try { - String mode = bootMode.trim().toUpperCase(); - return ApiConstants.BootMode.valueOf(mode); - } catch (IllegalArgumentException e) { - String msg = String.format("Invalid %s: %s specified for VM: %s. Valid values are: %s", - ApiConstants.BOOT_MODE, bootMode, getName(), Arrays.toString(ApiConstants.BootMode.values())); - logger.error(msg); - throw new InvalidParameterValueException(msg); - } - } - if (ApiConstants.BootType.UEFI.equals(getBootType())) { - String msg = String.format("%s must be specified for the VM with boot type: %s. Valid values are: %s", - ApiConstants.BOOT_MODE, getBootType(), Arrays.toString(ApiConstants.BootMode.values())); - logger.error(msg); - throw new InvalidParameterValueException(msg); - } - return null; - } - - public Map getVmProperties() { - Map map = new HashMap<>(); - if (MapUtils.isNotEmpty(vAppProperties)) { - Collection parameterCollection = vAppProperties.values(); - Iterator iterator = parameterCollection.iterator(); - while (iterator.hasNext()) { - HashMap entry = (HashMap)iterator.next(); - map.put(entry.get("key"), entry.get("value")); - } - } - return map; - } - - public Map getVmNetworkMap() { - Map map = new HashMap<>(); - if (MapUtils.isNotEmpty(vAppNetworks)) { - Collection parameterCollection = vAppNetworks.values(); - Iterator iterator = parameterCollection.iterator(); - while (iterator.hasNext()) { - HashMap entry = (HashMap) iterator.next(); - Integer nic; - try { - nic = Integer.valueOf(entry.get(VmDetailConstants.NIC)); - } catch (NumberFormatException nfe) { - nic = null; - } - String networkUuid = entry.get(VmDetailConstants.NETWORK); - if (logger.isTraceEnabled()) { - logger.trace(String.format("nic, '%s', goes on net, '%s'", nic, networkUuid)); - } - if (nic == null || StringUtils.isEmpty(networkUuid) || _entityMgr.findByUuid(Network.class, networkUuid) == null) { - throw new InvalidParameterValueException(String.format("Network ID: %s for NIC ID: %s is invalid", networkUuid, nic)); - } - map.put(nic, _entityMgr.findByUuid(Network.class, networkUuid).getId()); - } - } - return map; - } - - public String getGroup() { - return group; - } - - public HypervisorType getHypervisor() { - return HypervisorType.getType(hypervisor); - } - - public Boolean isDisplayVm() { - return displayVm; - } - - @Override - public boolean isDisplay() { - if(displayVm == null) - return true; - else - return displayVm; - } - - public List getSecurityGroupNameList() { - return securityGroupNameList; - } - - public List getSecurityGroupIdList() { - return securityGroupIdList; - } - public Long getServiceOfferingId() { return serviceOfferingId; } - public Long getSize() { - return size; - } - public Long getTemplateId() { return templateId; } - public String getUserData() { - return userData; + public Long getVolumeId() { + return volumeId; } - public Long getUserdataId() { - return userdataId; + public Long getSnapshotId() { + return snapshotId; } - public Map getUserdataDetails() { - return convertDetailsToMap(userdataDetails); + public boolean isVolumeOrSnapshotProvided() { + return volumeId != null || snapshotId != null; } - - public Long getZoneId() { - return zoneId; - } - - public String getPassword() { - return password; - } - - public List getNetworkIds() { - if (MapUtils.isNotEmpty(vAppNetworks)) { - if (CollectionUtils.isNotEmpty(networkIds) || ipAddress != null || getIp6Address() != null || MapUtils.isNotEmpty(ipToNetworkList)) { - throw new InvalidParameterValueException(String.format("%s can't be specified along with %s, %s, %s", ApiConstants.NIC_NETWORK_LIST, ApiConstants.NETWORK_IDS, ApiConstants.IP_ADDRESS, ApiConstants.IP_NETWORK_LIST)); - } else { - return new ArrayList<>(); - } - } - if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { - if ((networkIds != null && !networkIds.isEmpty()) || ipAddress != null || getIp6Address() != null) { - throw new InvalidParameterValueException("ipToNetworkMap can't be specified along with networkIds or ipAddress"); - } else { - List networks = new ArrayList(); - networks.addAll(getIpToNetworkMap().keySet()); - return networks; - } - } - return networkIds; - } - - public String getName() { - return name; - } - - public List getSSHKeyPairNames() { - List sshKeyPairs = new ArrayList(); - if(sshKeyPairNames != null) { - sshKeyPairs = sshKeyPairNames; - } - if(sshKeyPairName != null && !sshKeyPairName.isEmpty()) { - sshKeyPairs.add(sshKeyPairName); - } - return sshKeyPairs; - } - - public Long getHostId() { - return hostId; - } - - public boolean getStartVm() { - return startVm == null ? true : startVm; - } - - public Map getIpToNetworkMap() { - if ((networkIds != null || ipAddress != null || getIp6Address() != null) && ipToNetworkList != null) { - throw new InvalidParameterValueException("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter"); - } - LinkedHashMap ipToNetworkMap = null; - if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { - ipToNetworkMap = new LinkedHashMap(); - Collection ipsCollection = ipToNetworkList.values(); - Iterator iter = ipsCollection.iterator(); - while (iter.hasNext()) { - HashMap ips = (HashMap)iter.next(); - Long networkId = getNetworkIdFomIpMap(ips); - IpAddresses addrs = getIpAddressesFromIpMap(ips); - ipToNetworkMap.put(networkId, addrs); - } - } - - return ipToNetworkMap; - } - - @Nonnull - private IpAddresses getIpAddressesFromIpMap(HashMap ips) { - String requestedIp = ips.get("ip"); - String requestedIpv6 = ips.get("ipv6"); - String requestedMac = ips.get("mac"); - if (requestedIpv6 != null) { - requestedIpv6 = NetUtils.standardizeIp6Address(requestedIpv6); - } - if (requestedMac != null) { - if(!NetUtils.isValidMac(requestedMac)) { - throw new InvalidParameterValueException("Mac address is not valid: " + requestedMac); - } else if(!NetUtils.isUnicastMac(requestedMac)) { - throw new InvalidParameterValueException("Mac address is not unicast: " + requestedMac); - } - requestedMac = NetUtils.standardizeMacAddress(requestedMac); - } - return new IpAddresses(requestedIp, requestedIpv6, requestedMac); - } - - @Nonnull - private Long getNetworkIdFomIpMap(HashMap ips) { - Long networkId; - final String networkid = ips.get("networkid"); - Network network = _networkService.getNetwork(networkid); - if (network != null) { - networkId = network.getId(); - } else { - try { - networkId = Long.parseLong(networkid); - } catch (NumberFormatException e) { - throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + networkid); - } - } - return networkId; - } - - public String getIpAddress() { - return ipAddress; - } - - public String getIp6Address() { - if (ip6Address == null) { - return null; - } - return NetUtils.standardizeIp6Address(ip6Address); - } - - - public String getMacAddress() { - if (macAddress == null) { - return null; - } - if(!NetUtils.isValidMac(macAddress)) { - throw new InvalidParameterValueException("Mac address is not valid: " + macAddress); - } else if(!NetUtils.isUnicastMac(macAddress)) { - throw new InvalidParameterValueException("Mac address is not unicast: " + macAddress); - } - return NetUtils.standardizeMacAddress(macAddress); - } - - public List getAffinityGroupIdList() { - if (affinityGroupNameList != null && affinityGroupIdList != null) { - throw new InvalidParameterValueException("affinitygroupids parameter is mutually exclusive with affinitygroupnames parameter"); - } - - // transform group names to ids here - if (affinityGroupNameList != null) { - List affinityGroupIds = new ArrayList(); - for (String groupName : affinityGroupNameList) { - Long groupId = _responseGenerator.getAffinityGroupId(groupName, getEntityOwnerId()); - if (groupId == null) { - throw new InvalidParameterValueException("Unable to find affinity group by name " + groupName); - } else { - affinityGroupIds.add(groupId); - } - } - return affinityGroupIds; - } else { - return affinityGroupIdList; - } - } - - public String getKeyboard() { - // TODO Auto-generated method stub - return keyboard; - } - - public Map> getDhcpOptionsMap() { - Map> dhcpOptionsMap = new HashMap<>(); - if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) { - - Collection> paramsCollection = this.dhcpOptionsNetworkList.values(); - for (Map dhcpNetworkOptions : paramsCollection) { - String networkId = dhcpNetworkOptions.get(ApiConstants.NETWORK_ID); - - if (networkId == null) { - throw new IllegalArgumentException("No networkid specified when providing extra dhcp options."); - } - - Map dhcpOptionsForNetwork = new HashMap<>(); - dhcpOptionsMap.put(networkId, dhcpOptionsForNetwork); - - for (String key : dhcpNetworkOptions.keySet()) { - if (key.startsWith(ApiConstants.DHCP_PREFIX)) { - int dhcpOptionValue = Integer.parseInt(key.replaceFirst(ApiConstants.DHCP_PREFIX, "")); - dhcpOptionsForNetwork.put(dhcpOptionValue, dhcpNetworkOptions.get(key)); - } else if (!key.equals(ApiConstants.NETWORK_ID)) { - Dhcp.DhcpOptionCode dhcpOptionEnum = Dhcp.DhcpOptionCode.valueOfString(key); - dhcpOptionsForNetwork.put(dhcpOptionEnum.getCode(), dhcpNetworkOptions.get(key)); - } - } - - } - } - - return dhcpOptionsMap; - } - - public Map getDataDiskTemplateToDiskOfferingMap() { - if (diskOfferingId != null && dataDiskTemplateToDiskOfferingList != null) { - throw new InvalidParameterValueException("diskofferingid parameter can't be specified along with datadisktemplatetodiskofferinglist parameter"); - } - if (MapUtils.isEmpty(dataDiskTemplateToDiskOfferingList)) { - return new HashMap(); - } - - HashMap dataDiskTemplateToDiskOfferingMap = new HashMap(); - for (Object objDataDiskTemplates : dataDiskTemplateToDiskOfferingList.values()) { - HashMap dataDiskTemplates = (HashMap) objDataDiskTemplates; - Long dataDiskTemplateId; - DiskOffering dataDiskOffering = null; - VirtualMachineTemplate dataDiskTemplate= _entityMgr.findByUuid(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); - if (dataDiskTemplate == null) { - dataDiskTemplate = _entityMgr.findById(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); - if (dataDiskTemplate == null) - throw new InvalidParameterValueException("Unable to translate and find entity with datadisktemplateid " + dataDiskTemplates.get("datadisktemplateid")); - } - dataDiskTemplateId = dataDiskTemplate.getId(); - dataDiskOffering = _entityMgr.findByUuid(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); - if (dataDiskOffering == null) { - dataDiskOffering = _entityMgr.findById(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); - if (dataDiskOffering == null) - throw new InvalidParameterValueException("Unable to translate and find entity with diskofferingId " + dataDiskTemplates.get("diskofferingid")); - } - dataDiskTemplateToDiskOfferingMap.put(dataDiskTemplateId, dataDiskOffering); - } - return dataDiskTemplateToDiskOfferingMap; - } - - public String getExtraConfig() { - return extraConfig; - } - - public boolean getCopyImageTags() { - return copyImageTags == null ? false : copyImageTags; - } - - public Boolean getBootIntoSetup() { - return bootIntoSetup; - } - - public boolean isDynamicScalingEnabled() { - return dynamicScalingEnabled == null ? true : dynamicScalingEnabled; - } - - public Long getOverrideDiskOfferingId() { - return overrideDiskOfferingId; - } - - public ApiConstants.IoDriverPolicy getIoDriverPolicy() { - if (StringUtils.isNotBlank(ioDriverPolicy)) { - try { - String policyType = ioDriverPolicy.trim().toUpperCase(); - return ApiConstants.IoDriverPolicy.valueOf(policyType); - } catch (IllegalArgumentException e) { - String errMesg = String.format("Invalid io policy %s specified for vm %s. Valid values are: %s", ioDriverPolicy, getName(), Arrays.toString(ApiConstants.IoDriverPolicy.values())); - logger.warn(errMesg); - throw new InvalidParameterValueException(errMesg); - } - } - return null; - } - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - @Override - public String getCommandName() { - return s_name; - } - - public static String getResultObjectName() { - return "virtualmachine"; - } - - @Override - public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); - if (accountId == null) { - return CallContext.current().getCallingAccount().getId(); - } - - return accountId; - } - - @Override - public String getEventType() { - return EventTypes.EVENT_VM_CREATE; - } - - @Override - public String getCreateEventType() { - return EventTypes.EVENT_VM_CREATE; - } - - @Override - public String getCreateEventDescription() { - return "creating Vm"; - } - - @Override - public String getEventDescription() { - if(getStartVm()) { - return "starting Vm. Vm Id: " + getEntityUuid(); - } - return "deploying Vm. Vm Id: " + getEntityUuid(); - } - - @Override - public ApiCommandResourceType getApiResourceType() { - return ApiCommandResourceType.VirtualMachine; - } - @Override public void execute() { UserVm result; @@ -808,9 +129,12 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG } } - @Override public void create() throws ResourceAllocationException { + if (Stream.of(templateId, snapshotId, volumeId).filter(Objects::nonNull).count() != 1) { + throw new CloudRuntimeException("Please provide only one of the following parameters - template ID, volume ID or snapshot ID"); + } + try { UserVm vm = _userVmService.createVirtualMachine(this); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java index aa121162cb4..18a9d2058a6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java @@ -140,7 +140,8 @@ public class DestroyVMCmd extends BaseAsyncCmd implements UserCmd { if (responses != null && !responses.isEmpty()) { response = responses.get(0); } - response.setResponseName("virtualmachine"); + response.setResponseName(getCommandName()); + response.setObjectName("virtualmachine"); setResponseObject(response); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy vm"); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/GetVMPasswordCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/GetVMPasswordCmd.java index ce6114c7fd8..11534fd4375 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,6 +25,7 @@ import org.apache.cloudstack.api.Parameter; 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; @@ -61,7 +59,7 @@ public class GetVMPasswordCmd extends BaseCmd { 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 VM with id '" + getId() + "' found."); setResponseObject(new GetVMPasswordResponse(getCommandName(), passwd)); } 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 2d1160fb7a7..a561b6fd24f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java @@ -18,7 +18,13 @@ package org.apache.cloudstack.api.command.user.vm; import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -32,6 +38,7 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.BackupOfferingResponse; +import org.apache.cloudstack.api.response.ExtensionResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.IsoVmResponse; import org.apache.cloudstack.api.response.ListResponse; @@ -40,15 +47,20 @@ import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.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; @@ -56,7 +68,6 @@ import com.cloud.vm.VirtualMachine; requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd { - private static final String s_name = "listvirtualmachinesresponse"; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -96,7 +107,8 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements collectionType = CommandType.STRING, description = "comma separated list of vm 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") @@ -147,6 +159,30 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements @Parameter(name = ApiConstants.USER_DATA, type = CommandType.BOOLEAN, description = "Whether to return the VMs' user data or not. By default, user data will not be returned.", since = "4.18.0.0") private Boolean showUserData; + @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, required = false, description = "the instances by userdata", since = "4.20.1") + private Long userdataId; + + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the VM", + since = "4.20.1") + private String arch; + + @Parameter(name = ApiConstants.LEASED, type = CommandType.BOOLEAN, + description = "Whether to return only leased instances", + since = "4.21.0") + private Boolean onlyLeasedInstances = false; + + @Parameter(name = ApiConstants.GPU_ENABLED, + type = CommandType.BOOLEAN, + description = "Flag to indicate if the VMs should be filtered by GPU support. If set to true, only VMs that support GPU will be returned.", + since = "4.21.0") + private Boolean gpuEnabled; + + @Parameter(name = ApiConstants.EXTENSION_ID, type = CommandType.UUID, + entityType = ExtensionResponse.class, description = "The ID of the Orchestrator extension for the VM", + since = "4.21.0") + private Long extensionId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -237,22 +273,36 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements 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 class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements return isVnf; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + + public boolean getOnlyLeasedInstances() { + return BooleanUtils.toBoolean(onlyLeasedInstances); + } + + public Boolean getGpuEnabled() { + return gpuEnabled; + } + + public Long getExtensionId() { + return extensionId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// - @Override - public String getCommandName() { - return s_name; - } @Override public ApiCommandResourceType getApiResourceType() { @@ -295,22 +357,75 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements setResponseObject(response); } - protected void updateVMResponse(List response) { - for (UserVmResponse vmResponse : response) { - ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.UserVm, vmResponse.getId()); - if (resourceIcon == null) { - ResourceTag.ResourceObjectType type = ResourceTag.ResourceObjectType.Template; - String uuid = vmResponse.getTemplateId(); - if (vmResponse.getIsoId() != null) { - uuid = vmResponse.getIsoId(); - type = ResourceTag.ResourceObjectType.ISO; - } - resourceIcon = resourceIconManager.getByResourceTypeAndUuid(type, uuid); - if (resourceIcon == null) { - continue; - } + protected Map getResourceIconsUsingOsCategory(List responses) { + Set guestOsIds = responses.stream().map(UserVmResponse::getGuestOsId).collect(Collectors.toSet()); + List guestOSList = _entityMgr.listByUuids(GuestOS.class, guestOsIds); + Map guestOSMap = guestOSList.stream() + .collect(Collectors.toMap(GuestOS::getUuid, Function.identity())); + Set guestOsCategoryIds = guestOSMap.values().stream() + .map(GuestOS::getCategoryId) + .collect(Collectors.toSet()); + Map guestOsCategoryIcons = + resourceIconManager.getByResourceTypeAndIds(ResourceTag.ResourceObjectType.GuestOsCategory, + guestOsCategoryIds); + Map vmIcons = new HashMap<>(); + for (UserVmResponse response : responses) { + GuestOS guestOS = guestOSMap.get(response.getGuestOsId()); + if (guestOS != null) { + vmIcons.put(response.getId(), guestOsCategoryIcons.get(guestOS.getCategoryId())); } - ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + } + return vmIcons; + } + + protected Map getResourceIconsForUsingTemplateIso(List responses) { + Map vmTemplateIsoIdMap = new HashMap<>(); + Set templateUuids = new HashSet<>(); + Set isoUuids = new HashSet<>(); + for (UserVmResponse vmResponse : responses) { + if (vmResponse.getTemplateId() != null) { + templateUuids.add(vmResponse.getTemplateId()); + vmTemplateIsoIdMap.put(vmResponse.getId(), vmResponse.getTemplateId()); + } + if (vmResponse.getIsoId() != null) { + isoUuids.add(vmResponse.getIsoId()); + vmTemplateIsoIdMap.put(vmResponse.getId(), vmResponse.getIsoId()); + } + } + Map templateOrIsoIcons = resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.Template, templateUuids); + templateOrIsoIcons.putAll(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.ISO, isoUuids)); + Map vmIcons = new HashMap<>(); + List noTemplateIsoIconResponses = new ArrayList<>(); + for (UserVmResponse response : responses) { + String uuid = vmTemplateIsoIdMap.get(response.getId()); + if (StringUtils.isNotBlank(uuid) && templateOrIsoIcons.containsKey(uuid)) { + vmIcons.put(response.getId(), + templateOrIsoIcons.get(vmTemplateIsoIdMap.get(response.getId()))); + continue; + } + noTemplateIsoIconResponses.add(response); + } + vmIcons.putAll(getResourceIconsUsingOsCategory(noTemplateIsoIconResponses)); + return vmIcons; + } + + protected void updateVMResponse(List responses) { + if (CollectionUtils.isEmpty(responses)) { + return; + } + Set vmUuids = responses.stream().map(UserVmResponse::getId).collect(Collectors.toSet()); + Map vmIcons = resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.UserVm, vmUuids); + List noVmIconResponses = responses + .stream() + .filter(r -> !vmIcons.containsKey(r.getId())) + .collect(Collectors.toList()); + vmIcons.putAll(getResourceIconsForUsingTemplateIso(noVmIconResponses)); + for (UserVmResponse vmResponse : responses) { + ResourceIcon icon = vmIcons.get(vmResponse.getId()); + if (icon == null) { + continue; + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(icon); vmResponse.setResourceIconResponse(iconResponse); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVnfAppliancesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVnfAppliancesCmd.java new file mode 100644 index 00000000000..e7bbb97d78c --- /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 10900f61b22..153f5ea6563 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; @@ -113,7 +114,7 @@ public class RebootVMCmd extends BaseAsyncCmd implements UserCmd { } @Override - public void execute() throws ResourceUnavailableException, InsufficientCapacityException { + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); UserVm result; result = _userVmService.rebootVirtualMachine(this); 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 a4cd6159dfc..2f53c3d4e4c 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 @@ -127,7 +127,7 @@ public class RemoveIpFromVmNicCmd extends BaseAsyncCmd { 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 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 089dfaecf94..0ecf4ff1384 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 @@ -60,7 +60,7 @@ public class ResetVMUserDataCmd extends BaseCmd implements UserCmd { 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." + + "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; 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 8bc4f0ff3b1..c0311d59997 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 @@ -102,6 +102,10 @@ public class StartVMCmd extends BaseAsyncCmd implements UserCmd { return id; } + public void setId(Long id) { + this.id = id; + } + public Long getHostId() { return hostId; } 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 9f72ac17c8f..7906ec632a4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java @@ -16,18 +16,19 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.uservm.UserVm; import com.cloud.utils.exception.CloudRuntimeException; -import org.apache.cloudstack.api.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,15 +39,17 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.user.Account; -import com.cloud.uservm.UserVm; -import com.cloud.utils.net.Dhcp; -import com.cloud.vm.VirtualMachine; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @APICommand(name = "updateVirtualMachine", description="Updates properties of a virtual machine. The VM has to be stopped and restarted for the " + "new properties to take effect. UpdateVirtualMachine does not first check whether the VM is stopped. " + @@ -84,7 +87,7 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, 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." + + "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") @@ -104,7 +107,7 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, 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") 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 vm. The VM 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}) @@ -141,9 +144,25 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, + " 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 virtual machine 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; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -213,6 +232,10 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, return cleanupDetails == null ? false : cleanupDetails.booleanValue(); } + public Boolean getDeleteProtection() { + return deleteProtection; + } + public Map> getDhcpOptionsMap() { Map> dhcpOptionsMap = new HashMap<>(); if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) { @@ -310,4 +333,21 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, public ApiCommandResourceType getApiResourceType() { return ApiCommandResourceType.VirtualMachine; } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public VMLeaseManager.ExpiryAction getLeaseExpiryAction() { + if (StringUtils.isBlank(leaseExpiryAction)) { + return null; + } + VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction); + if (action == null) { + throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " + + com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values())); + } + return action; + } + } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java index 1146f80f0e2..6ddb5f1e567 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.volume; +import com.cloud.dc.DataCenter; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; @@ -31,9 +32,7 @@ import org.apache.cloudstack.api.response.VolumeResponse; 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; @@ -116,28 +115,19 @@ public class ExtractVolumeCmd extends BaseAsyncCmd { @Override public String getEventDescription() { - return "Extraction job"; + String volumeId = this._uuidMgr.getUuid(Volume.class, getId()); + String zoneId = this._uuidMgr.getUuid(DataCenter.class, getZoneId()); + + return String.format("Extracting volume: %s from zone: %s", volumeId, zoneId); } @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId())); + CallContext.current().setEventDetails(getEventDescription()); String uploadUrl = _volumeService.extractVolume(this); if (uploadUrl != null) { - ExtractResponse response = 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/ListVolumesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java index a583675da76..a1024a98898 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.DiskOfferingResponse; 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; @@ -80,6 +81,12 @@ public class ListVolumesCmd extends BaseListRetrieveOnlyResourceCountCmd impleme RoleType.Admin}) private String storageId; + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, + entityType = ServiceOfferingResponse.class, + description = "list volumes by disk offering of a service offering. If both service offering and " + + "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; @@ -94,6 +101,9 @@ public class ListVolumesCmd extends BaseListRetrieveOnlyResourceCountCmd impleme @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 class ListVolumesCmd extends BaseListRetrieveOnlyResourceCountCmd impleme return podId; } + public Long getServiceOfferingId() { + return serviceOfferingId; + } + public Long getDiskOfferingId() { return diskOfferingId; } @@ -151,6 +165,10 @@ public class ListVolumesCmd extends BaseListRetrieveOnlyResourceCountCmd impleme return state; } + public Boolean isEncrypted() { + return encrypted; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 9254bad207b..eb89115cf46 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 @@ -73,6 +73,10 @@ public class ResizeVolumeCmd extends BaseAsyncCmd implements UserCmd { 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 class ResizeVolumeCmd extends BaseAsyncCmd implements UserCmd { return getEntityId(); } + public void setId(Long id) { + this.id = id; + } + public Long getMinIops() { return minIops; } @@ -113,6 +121,10 @@ public class ResizeVolumeCmd extends BaseAsyncCmd implements UserCmd { return size; } + public void setSize(Long size) { + this.size = size; + } + public boolean isShrinkOk() { return shrinkOk; } @@ -121,6 +133,10 @@ public class ResizeVolumeCmd extends BaseAsyncCmd implements UserCmd { return newDiskOfferingId; } + public boolean getAutoMigrate() { + return autoMigrate == null ? false : autoMigrate; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 467c587cc73..22b819c8cba 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 @@ -77,6 +77,14 @@ public class UpdateVolumeCmd extends BaseAsyncCustomIdCmd implements UserCmd { @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 class UpdateVolumeCmd extends BaseAsyncCustomIdCmd implements UserCmd { return name; } + public Boolean getDeleteProtection() { + return deleteProtection; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -168,7 +180,7 @@ public class UpdateVolumeCmd extends BaseAsyncCustomIdCmd implements UserCmd { public void execute() { CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId())); 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/vpc/CreateStaticRouteCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java index b28c02cb800..c4b5e159b5c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.PrivateGatewayResponse; import org.apache.cloudstack.api.response.StaticRouteResponse; +import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.context.CallContext; import com.cloud.event.EventTypes; @@ -45,20 +46,40 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.GATEWAY_ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, - required = true, - description = "the gateway id we are creating static route for") + description = "the gateway id we are creating static route for. Mutually exclusive with the nexthop parameter") private Long gatewayId; + @Parameter(name = ApiConstants.VPC_ID, + type = CommandType.UUID, + entityType = VpcResponse.class, + description = "the vpc id for which the static route is created. This is required for nexthop parameter", + since = "4.21.0") + private Long vpcId; + + @Parameter(name = ApiConstants.NEXT_HOP, + type = CommandType.STRING, + description = "the next hop of static route. Mutually exclusive with the gatewayid parameter", + since = "4.21.0") + private String nextHop; + @Parameter(name = ApiConstants.CIDR, required = true, type = CommandType.STRING, description = "static route cidr") private String cidr; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public long getGatewayId() { + public Long getGatewayId() { return gatewayId; } + public Long getVpcId() { + return vpcId; + } + + public String getNextHop() { + return nextHop; + } + public String getCidr() { return cidr; } @@ -69,7 +90,7 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException { try { - StaticRoute result = _vpcService.createStaticRoute(getGatewayId(), getCidr()); + StaticRoute result = _vpcService.createStaticRoute(getGatewayId(), getVpcId(), getNextHop(), getCidr()); setEntityId(result.getId()); setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { @@ -114,11 +135,8 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { @Override public long getEntityOwnerId() { - VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId); - if (gateway == null) { - throw new InvalidParameterValueException("Invalid gateway id is specified"); - } - return _entityMgr.findById(Vpc.class, gateway.getVpcId()).getAccountId(); + Long vpcId = getSyncObjId(); + return _entityMgr.findById(Vpc.class, vpcId).getAccountId(); } @Override @@ -128,11 +146,20 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { @Override public Long getSyncObjId() { - VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId); - if (gateway == null) { - throw new InvalidParameterValueException("Invalid id is specified for the gateway"); + if (gatewayId != null) { + VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId); + if (gateway == null) { + throw new InvalidParameterValueException("Invalid id is specified for the gateway"); + } + return gateway.getVpcId(); + } else if (vpcId != null) { + Vpc vpc = _entityMgr.findById(Vpc.class, vpcId); + if (vpc == null) { + throw new InvalidParameterValueException("Invalid vpc id is specified"); + } + return vpc.getId(); } - return gateway.getVpcId(); + throw new InvalidParameterValueException("One of vpcId or gatewayId must be specified"); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java index 94f05f707a0..08b7b9911b1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vpc; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.acl.RoleType; @@ -75,10 +76,15 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { 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 networks' 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") private Long vpcOffering; @@ -117,6 +123,13 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { 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 class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { return cidr; } + public Integer getCidrSize() { + return cidrSize; + } + public String getDisplayText() { return StringUtils.isEmpty(displayText) ? vpcName : displayText; } @@ -189,6 +206,14 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { return sourceNatIP; } + public Long getAsNumber() { + return asNumber; + } + + public boolean getUseVrIpResolver() { + return BooleanUtils.toBoolean(useVrIpResolver); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -208,11 +233,7 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { 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); 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 9e950310cdc..59ba7e94b04 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 @@ -131,6 +131,7 @@ public class AddVpnUserCmd extends BaseAsyncCreateCmd { if (domain != null) { vpnResponse.setDomainId(domain.getUuid()); vpnResponse.setDomainName(domain.getName()); + vpnResponse.setDomainPath(domain.getPath()); } vpnResponse.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java index 6f31176c4ff..6c3f57b3dfa 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.IPAddressResponse; import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.context.CallContext; @@ -44,9 +45,16 @@ public class CreateVpnGatewayCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = VpcResponse.class, required = true, - description = "public ip address id of the vpn gateway") + description = "id of the vpc") private Long vpcId; + @Parameter(name = ApiConstants.IP_ADDRESS_ID, + type = CommandType.UUID, + entityType = IPAddressResponse.class, + description = "the public IP address ID for which VPN gateway is being enabled. By default the source NAT IP or router IP will be used.", + since = "4.21.0") + private Long ipAddressId; + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the vpn to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @@ -58,6 +66,10 @@ public class CreateVpnGatewayCmd extends BaseAsyncCreateCmd { return vpcId; } + public Long getIpAddressId() { + return ipAddressId; + } + @Deprecated public Boolean getDisplay() { return display; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java index 48e7a9ee519..0697987b04d 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 @@ -104,7 +104,7 @@ public class RemoveVpnUserCmd extends BaseAsyncCmd { 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); logger.error(errorMessage); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java index d926257437e..36ce23cbc6e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java @@ -34,8 +34,6 @@ import org.apache.cloudstack.api.response.ZoneResponse; requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListZonesCmd extends BaseListCmd implements UserCmd { - private static final String s_name = "listzonesresponse"; - ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -69,6 +67,11 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd { @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource image for the zones") private Boolean showIcon; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -109,15 +112,22 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd { return showIcon != null ? showIcon : false; } + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListZonesCmd() { + + } + + public ListZonesCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// - @Override - public String getCommandName() { - return s_name; - } - @Override public void execute() { ListResponse response = _queryService.listDataCenters(this); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ASNRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ASNRangeResponse.java new file mode 100644 index 00000000000..86dab54ca6b --- /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 00000000000..45884250984 --- /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 7a84e85a4a6..3c99e2cbec6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java @@ -127,6 +127,30 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou @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") private String templateLimit; @@ -207,6 +231,18 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou @Param(description = "the total memory (in MB) available to be created for this account", since = "4.2.0") private String memoryAvailable; + @SerializedName("gpulimit") + @Param(description = "the total number of gpus the account can own", since = "4.21.0") + private String gpuLimit; + + @SerializedName("gputotal") + @Param(description = "the total number of gpus owned by account", since = "4.21.0") + private Long gpuTotal; + + @SerializedName("gpuavailable") + @Param(description = "the total number of gpus available to be created for this account", since = "4.21.0") + private String gpuAvailable; + @SerializedName("primarystoragelimit") @Param(description = "the total primary storage space (in GiB) the account can own", since = "4.2.0") private String primaryStorageLimit; @@ -231,6 +267,30 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou @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") private String state; @@ -271,6 +331,10 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou @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; @@ -382,6 +446,36 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou 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; @@ -407,6 +501,21 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou this.vmRunning = vmRunning; } + @Override + public void setGpuLimit(String gpuLimit) { + this.gpuLimit = gpuLimit; + } + + @Override + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + @Override + public void setGpuAvailable(String gpuAvailable) { + this.gpuAvailable = gpuAvailable; + } + public void setState(String state) { this.state = state; } @@ -533,6 +642,36 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou 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; } @@ -554,4 +693,8 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou 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 7270fa949c8..06351a40d9d 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 @@ -75,6 +75,10 @@ public class AcquireIPAddressResponse extends BaseResponse implements Controlle @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") private Boolean forVirtualNetwork; @@ -190,6 +194,11 @@ public class AcquireIPAddressResponse extends BaseResponse implements Controlle 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 77c4d0d3ffc..2226efd063d 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 @@ -44,7 +44,7 @@ public class AcquirePodIpCmdResponse extends BaseResponse { @SerializedName(ApiConstants.NIC_ID) @Param(description = "the ID of the nic") - private Long instanceId; + private Long nicId; @SerializedName(ApiConstants.HOST_MAC) @Param(description = "MAC address of the pod the IP") @@ -58,8 +58,8 @@ public class AcquirePodIpCmdResponse extends BaseResponse { 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 class AcquirePodIpCmdResponse extends BaseResponse { 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/ApplicationLoadBalancerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ApplicationLoadBalancerResponse.java index 53e3f868a3b..0b6485bfc14 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 @@ -76,6 +76,10 @@ public class ApplicationLoadBalancerResponse extends BaseResponse implements Con @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) private List lbRules; @@ -107,6 +111,11 @@ public class ApplicationLoadBalancerResponse extends BaseResponse implements Con 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/AsyncJobResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java index 3eeaaef2afa..edbc94bc498 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java @@ -55,10 +55,6 @@ public class AsyncJobResponse extends BaseResponse { @Param(description = "the async command executed") private String cmd; - @SerializedName("jobstatus") - @Param(description = "the current job status-should be 0 for PENDING") - private Integer jobStatus; - @SerializedName("jobprocstatus") @Param(description = "the progress information of the PENDING job") private Integer jobProcStatus; @@ -83,9 +79,13 @@ public class AsyncJobResponse extends BaseResponse { @Param(description = "the unique ID of the instance/entity object related to the job") private String jobInstanceId; - @SerializedName("managementserverid") + @SerializedName(ApiConstants.MANAGEMENT_SERVER_ID) @Param(description = "the msid of the management server on which the job is running", since = "4.19") - private Long msid; + 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") @@ -119,11 +119,6 @@ public class AsyncJobResponse extends BaseResponse { this.cmd = cmd; } - @Override - public void setJobStatus(Integer jobStatus) { - this.jobStatus = jobStatus; - } - public void setJobProcStatus(Integer jobProcStatus) { this.jobProcStatus = jobProcStatus; } @@ -156,7 +151,11 @@ public class AsyncJobResponse extends BaseResponse { 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 ae3462f1fec..e3f9902dec6 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 @@ -74,6 +74,10 @@ public class AutoScalePolicyResponse extends BaseResponse implements ControlledE @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 class AutoScalePolicyResponse extends BaseResponse implements ControlledE 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 656a62ddda3..b1389acdecb 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 @@ -123,6 +123,10 @@ public class AutoScaleVmGroupResponse extends BaseResponseWithAnnotations implem @Param(description = "the domain name of the vm 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}) private Boolean forDisplay; @@ -227,6 +231,11 @@ public class AutoScaleVmGroupResponse extends BaseResponseWithAnnotations implem 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 9f238344730..22e4eb2288b 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 @@ -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 VM user data") private String userData; @SerializedName(ApiConstants.USER_DATA_ID) @Param(description="the id of userdata used for the VM", since = "4.18.1") @@ -114,6 +114,10 @@ public class AutoScaleVmProfileResponse extends BaseResponse implements Controll @Param(description = "the domain name of the vm 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}) private Boolean forDisplay; @@ -196,6 +200,10 @@ public class AutoScaleVmProfileResponse extends BaseResponse implements Controll 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 480ebcfb13d..0e895fa4e96 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java @@ -41,6 +41,10 @@ public class BackupOfferingResponse extends BaseResponse { @Param(description = "description for the backup offering") private String description; + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "provider name", since = "4.21.0") + private String provider; + @SerializedName(ApiConstants.EXTERNAL_ID) @Param(description = "external ID on the provider side") private String externalId; @@ -69,6 +73,10 @@ public class BackupOfferingResponse extends BaseResponse { this.externalId = externalId; } + public void setProvider(String provider) { + this.provider = provider; + } + public void setName(String name) { this.name = name; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java new file mode 100644 index 00000000000..0db51f04034 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java @@ -0,0 +1,142 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.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.CAPACITY_BYTES) + @Param(description = "capacity of the backup repository") + private Long capacityBytes; + + @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 Long getCapacityBytes() { + return capacityBytes; + } + + public void setCapacityBytes(Long capacityBytes) { + this.capacityBytes = capacityBytes; + } + + 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 63419680fea..0ae558ac803 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java @@ -26,6 +26,7 @@ import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; import java.util.Date; +import java.util.Map; @EntityReference(value = Backup.class) public class BackupResponse extends BaseResponse { @@ -34,6 +35,14 @@ public class BackupResponse extends BaseResponse { @Param(description = "ID of the VM backup") private String id; + @SerializedName(ApiConstants.NAME) + @Param(description = "name of the backup", since = "4.21.0") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "description for the backup", since = "4.21.0") + private String description; + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) @Param(description = "ID of the VM") private String vmId; @@ -102,6 +111,18 @@ public class BackupResponse extends BaseResponse { @Param(description = "zone name") private String zone; + @SerializedName(ApiConstants.VM_DETAILS) + @Param(description = "Lists the vm specific details for the backup", since = "4.21.0") + private Map vmDetails; + + @SerializedName(ApiConstants.INTERVAL_TYPE) + @Param(description = "Interval type of the backup", since = "4.21.0") + private String intervalType; + + @SerializedName(ApiConstants.BACKUP_VM_OFFERING_REMOVED) + @Param(description = "The backup offering corresponding to this backup was removed from the VM", since = "4.21.0") + private Boolean vmOfferingRemoved; + public String getId() { return id; } @@ -110,6 +131,22 @@ public class BackupResponse extends BaseResponse { this.id = id; } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public String getVmId() { return vmId; } @@ -245,4 +282,28 @@ public class BackupResponse extends BaseResponse { public void setZone(String zone) { this.zone = zone; } + + public Map getVmDetails() { + return vmDetails; + } + + public void setVmDetails(Map vmDetails) { + this.vmDetails = vmDetails; + } + + public String getIntervalType() { + return this.intervalType; + } + + public void setIntervalType(String intervalType) { + this.intervalType = intervalType; + } + + public Boolean getVmOfferingRemoved() { + return this.vmOfferingRemoved; + } + + public void setVmOfferingRemoved(Boolean vmOfferingRemoved) { + this.vmOfferingRemoved = vmOfferingRemoved; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java index ba44f1e024f..d8c2980774d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java @@ -28,6 +28,9 @@ import com.google.gson.annotations.SerializedName; @EntityReference(value = BackupSchedule.class) public class BackupScheduleResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the backup schedule.", since = "4.21.0") + private String id; @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) @Param(description = "name of the VM") @@ -37,18 +40,30 @@ public class BackupScheduleResponse extends BaseResponse { @Param(description = "ID of the VM") private String vmId; - @SerializedName("schedule") + @SerializedName(ApiConstants.SCHEDULE) @Param(description = "time the backup is scheduled to be taken.") private String schedule; - @SerializedName("intervaltype") + @SerializedName(ApiConstants.INTERVAL_TYPE) @Param(description = "the interval type of the backup schedule") private DateUtil.IntervalType intervalType; - @SerializedName("timezone") + @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 class BackupScheduleResponse extends BaseResponse { 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/BgpPeerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BgpPeerResponse.java new file mode 100644 index 00000000000..344e65c6bad --- /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 b75f3604324..cde140839ec 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 class BucketResponse extends BaseResponseWithTagInformation implements Co 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/CapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java index e4224c85e97..eb0daf75148 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.response; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; @@ -92,6 +93,10 @@ public class CapabilitiesResponse extends BaseResponse { @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") private boolean kubernetesServiceEnabled; @@ -124,6 +129,26 @@ public class CapabilitiesResponse extends BaseResponse { @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; + public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) { this.securityGroupsEnabled = securityGroupsEnabled; } @@ -192,6 +217,10 @@ public class CapabilitiesResponse extends BaseResponse { this.allowUserViewAllDomainAccounts = allowUserViewAllDomainAccounts; } + public void setAllowUserForceStopVM(boolean allowUserForceStopVM) { + this.allowUserForceStopVM = allowUserForceStopVM; + } + public void setKubernetesServiceEnabled(boolean kubernetesServiceEnabled) { this.kubernetesServiceEnabled = kubernetesServiceEnabled; } @@ -223,4 +252,24 @@ public class CapabilitiesResponse extends BaseResponse { 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; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java index 4114c228e26..f399c1260f7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java @@ -50,13 +50,13 @@ public class ClusterDrsPlanMigrationResponse extends BaseResponse { @Param(description = "Destination host for VM migration") String destHostName; - @SerializedName(ApiConstants.JOB_ID) + @SerializedName(ApiConstants.MIGRATION_JOB_ID) @Param(description = "id of VM migration async job") - private Long jobId; + private Long migrationJobId; - @SerializedName(ApiConstants.JOB_STATUS) + @SerializedName(ApiConstants.MIGRATION_JOB_STATUS) @Param(description = "Job status of VM migration async job") - private JobInfo.Status jobStatus; + private JobInfo.Status migrationJobStatus; public ClusterDrsPlanMigrationResponse(String vmId, String vmName, String srcHostId, String srcHostName, @@ -68,8 +68,8 @@ public class ClusterDrsPlanMigrationResponse extends BaseResponse { this.srcHostName = srcHostName; this.destHostId = destHostId; this.destHostName = destHostName; - this.jobId = jobId; - this.jobStatus = jobStatus; + this.migrationJobId = jobId; + this.migrationJobStatus = jobStatus; this.setObjectName(ApiConstants.MIGRATIONS); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java index 72dab3da3b1..202ff4bd870 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java @@ -31,6 +31,8 @@ import com.google.gson.annotations.SerializedName; @EntityReference(value = Cluster.class) public class ClusterResponse extends BaseResponseWithAnnotations { + private transient long internalId; + @SerializedName(ApiConstants.ID) @Param(description = "the cluster ID") private String id; @@ -73,7 +75,7 @@ public class ClusterResponse extends BaseResponseWithAnnotations { @SerializedName("capacity") @Param(description = "the capacity of the Cluster", responseObject = CapacityResponse.class) - private List capacitites; + private List capacities; @SerializedName("cpuovercommitratio") @Param(description = "The cpu overcommit ratio of the cluster") @@ -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 class ClusterResponse extends BaseResponseWithAnnotations { 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 class ClusterResponse extends BaseResponseWithAnnotations { 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 15671430bf1..1038177cb86 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 @@ -61,6 +61,10 @@ public class ConditionResponse extends BaseResponse implements ControlledEntityR @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") private String zoneId; @@ -138,4 +142,9 @@ public class ConditionResponse extends BaseResponse implements ControlledEntityR 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/ControlledEntityResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ControlledEntityResponse.java index 598ef082253..dc021705d78 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 abe4dd77143..730c2c38fc5 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/CreateConsoleEndpointResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CreateConsoleEndpointResponse.java index c60917bbe7a..c1e04beee73 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CreateConsoleEndpointResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CreateConsoleEndpointResponse.java @@ -26,7 +26,7 @@ public class CreateConsoleEndpointResponse extends BaseResponse { public CreateConsoleEndpointResponse() { } - @SerializedName(ApiConstants.RESULT) + @SerializedName(ApiConstants.SUCCESS) @Param(description = "true if the console endpoint is generated properly") private Boolean result; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponse.java new file mode 100644 index 00000000000..a1a87794a88 --- /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/DomainResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java index 7c6ad3a91c3..e2246a1d531 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java @@ -105,6 +105,30 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou @SerializedName("snapshotavailable") @Param(description="the total number of snapshots available for this domain") private String snapshotAvailable; + @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; @@ -159,6 +183,15 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou @SerializedName("memoryavailable") @Param(description="the total memory (in MB) available to be created for this domain", since="4.2.0") private String memoryAvailable; + @SerializedName("gpulimit") @Param(description="the total number of gpus the domain can own", since="4.21.0") + private String gpuLimit; + + @SerializedName("gputotal") @Param(description="the total number of gpus owned by domain", since="4.21.0") + private Long gpuTotal; + + @SerializedName("gpuavailable") @Param(description="the total number of gpus available to be created for this domain", since="4.21.0") + private String gpuAvailable; + @SerializedName("primarystoragelimit") @Param(description="the total primary storage space (in GiB) the domain can own", since="4.2.0") private String primaryStorageLimit; @@ -177,6 +210,30 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou @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; @@ -313,6 +370,36 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou 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; @@ -400,6 +487,21 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou this.memoryAvailable = memoryAvailable; } + @Override + public void setGpuLimit(String gpuLimit) { + this.gpuLimit = gpuLimit; + } + + @Override + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + @Override + public void setGpuAvailable(String gpuAvailable) { + this.gpuAvailable = gpuAvailable; + } + @Override public void setPrimaryStorageLimit(String primaryStorageLimit) { this.primaryStorageLimit = primaryStorageLimit; @@ -430,6 +532,36 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou 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; } 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 99e5f6ccdfa..4b5e886a16e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java @@ -185,6 +185,10 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements @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") private String serviceOfferingId; @@ -241,6 +245,10 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements @Param(description = "the version of the code / software in the router") private String softwareVersion; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU arch of the router", since = "4.20.1") + private String arch; + public DomainRouterResponse() { nics = new LinkedHashSet(); } @@ -381,6 +389,10 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements 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 class DomainRouterResponse extends BaseResponseWithAnnotations implements public void setSoftwareVersion(String softwareVersion) { this.softwareVersion = softwareVersion; } + + public void setArch(String arch) { + this.arch = arch; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java index 8f65492cb70..751d00922f1 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 @@ -69,6 +69,10 @@ public class EventResponse extends BaseResponse implements ControlledViewEntityR @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") private String resourceId; @@ -132,6 +136,11 @@ public class EventResponse extends BaseResponse implements ControlledViewEntityR 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/ExtensionCustomActionParameterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionParameterResponse.java new file mode 100644 index 00000000000..d627f8077dc --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionParameterResponse.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ExtensionCustomActionParameterResponse extends BaseResponse { + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the parameter") + private String name; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Type of the parameter") + private String type; + + @SerializedName(ApiConstants.VALIDATION_FORMAT) + @Param(description = "Validation format for value of the parameter. Available for specific types") + private String validationFormat; + + @SerializedName(ApiConstants.VALUE_OPTIONS) + @Param(description = "Comma-separated list of options for value of the parameter") + private List valueOptions; + + @SerializedName(ApiConstants.REQUIRED) + @Param(description = "Whether the parameter is required or not") + private Boolean required; + + public ExtensionCustomActionParameterResponse(String name, String type, String validationFormat, List valueOptions, + boolean required) { + this.name = name; + this.type = type; + this.validationFormat = validationFormat; + this.valueOptions = valueOptions; + this.required = required; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionResponse.java new file mode 100644 index 00000000000..96edf6d2fd8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionResponse.java @@ -0,0 +1,184 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.extension.ExtensionCustomAction; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = ExtensionCustomAction.class) +public class ExtensionCustomActionResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the custom action") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the custom action") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the custom action") + private String description; + + @SerializedName(ApiConstants.EXTENSION_ID) + @Param(description = "ID of the extension that this custom action belongs to") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) + @Param(description = "Name of the extension that this custom action belongs to") + private String extensionName; + + @SerializedName(ApiConstants.RESOURCE_TYPE) + @Param(description = "Resource type for which the action is available") + private String resourceType; + + @SerializedName(ApiConstants.ALLOWED_ROLE_TYPES) + @Param(description = "List of role types allowed for the custom action") + private List allowedRoleTypes; + + @SerializedName(ApiConstants.SUCCESS_MESSAGE) + @Param(description = "Message that will be used on successful execution of the action") + private String successMessage; + + @SerializedName(ApiConstants.ERROR_MESSAGE) + @Param(description = "Message that will be used on failure during execution of the action") + private String errorMessage; + + @SerializedName(ApiConstants.TIMEOUT) + @Param(description = "Specifies the timeout in seconds to wait for the action to complete before failing") + private Integer timeout; + + @SerializedName(ApiConstants.ENABLED) + @Param(description = "Whether the custom action is enabled or not") + private Boolean enabled; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "Details of the custom action") + private Map details; + + @SerializedName(ApiConstants.PARAMETERS) + @Param(description = "List of the parameters for the action", responseObject = ExtensionCustomActionParameterResponse.class) + private List parameters; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Creation timestamp of the custom action") + private Date created; + + public ExtensionCustomActionResponse(String id, String name, String description) { + this.id = id; + this.name = name; + this.description = description; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setExtensionId(String extensionId) { + this.extensionId = extensionId; + } + + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } + + public String getResourceType() { + return resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public List getAllowedRoleTypes() { + return allowedRoleTypes; + } + + public void setAllowedRoleTypes(List allowedRoleTypes) { + this.allowedRoleTypes = allowedRoleTypes; + } + + public void setSuccessMessage(String successMessage) { + this.successMessage = successMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public void setTimeout(Integer timeout) { + this.timeout = timeout; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public List getParameters() { + return parameters; + } + + public void setDetails(Map details) { + this.details = details; + } + + public Map getDetails() { + return details; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResourceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResourceResponse.java new file mode 100644 index 00000000000..aa370887b74 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResourceResponse.java @@ -0,0 +1,95 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response; + +import org.apache.cloudstack.extension.ExtensionResourceMap; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; +import java.util.Map; + +@EntityReference(value = ExtensionResourceMap.class) +public class ExtensionResourceResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the resource associated with the extension") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the resource associated with this mapping") + private String name; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Type of the resource") + private String type; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "the details of the resource map") + private Map details; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Creation timestamp of the mapping") + private Date created; + + public ExtensionResourceResponse() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java new file mode 100644 index 00000000000..fdf1e87df50 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java @@ -0,0 +1,182 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.extension.Extension; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = Extension.class) +public class ExtensionResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the extension") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the extension") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the extension") + private String description; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Type of the extension") + private String type; + + @SerializedName(ApiConstants.PATH) + @Param(description = "The path of the entry point fo the extension") + private String path; + + @SerializedName(ApiConstants.PATH_READY) + @Param(description = "True if the extension path is in ready state across management servers") + private Boolean pathReady; + + @SerializedName(ApiConstants.IS_USER_DEFINED) + @Param(description = "True if the extension is added by admin") + private Boolean userDefined; + + @SerializedName(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM) + @Parameter(description = "Only honored when type is Orchestrator. Whether prepare VM is needed or not") + private Boolean orchestratorRequiresPrepareVm; + + @SerializedName(ApiConstants.STATE) + @Param(description = "The state of the extension") + private String state; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "The details of the extension") + private Map details; + + @SerializedName(ApiConstants.RESOURCES) + @Param(description = "List of resources to which extension is registered to", responseObject = ExtensionResourceResponse.class) + private List resources; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Creation timestamp of the extension") + private Date created; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "Removal timestamp of the extension, if applicable") + private Date removed; + + public ExtensionResponse(String id, String name, String description, String type) { + this.id = id; + this.name = name; + this.description = description; + this.type = type; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getType() { + return type; + } + + public String getPath() { + return path; + } + + public Boolean isPathReady() { + return pathReady; + } + + public Boolean isUserDefined() { + return userDefined; + } + + public Boolean isOrchestratorRequiresPrepareVm() { + return orchestratorRequiresPrepareVm; + } + + public String getState() { + return state; + } + + public Map getDetails() { + return details; + } + + public void setPath(String path) { + this.path = path; + } + + public void setPathReady(Boolean pathReady) { + this.pathReady = pathReady; + } + + public void setUserDefined(Boolean userDefined) { + this.userDefined = userDefined; + } + + public void setOrchestratorRequiresPrepareVm(Boolean orchestratorRequiresPrepareVm) { + this.orchestratorRequiresPrepareVm = orchestratorRequiresPrepareVm; + } + + public void setState(String state) { + this.state = state; + } + + public void setDetails(Map details) { + this.details = details; + } + + public List getResources() { + return resources; + } + + public void setResources(List resources) { + this.resources = resources; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GlobalLoadBalancerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GlobalLoadBalancerResponse.java index aac7e29a173..d41e4d2ab34 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 @@ -82,6 +82,10 @@ public class GlobalLoadBalancerResponse extends BaseResponse implements Controll @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) private List siteLoadBalancers; @@ -143,6 +147,11 @@ public class GlobalLoadBalancerResponse extends BaseResponse implements Controll 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 00000000000..ad91b3490ef --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GpuCardResponse.java @@ -0,0 +1,109 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gpu.GpuCard; + +@EntityReference(value = GpuCard.class) +public class GpuCardResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the GPU card") + protected String id; + + @SerializedName("deviceid") + @Param(description = "the device ID of the GPU card") + protected String deviceId; + + @SerializedName("devicename") + @Param(description = "the device name of the GPU card") + protected String deviceName; + + @SerializedName("name") + @Param(description = "the display name of the GPU card") + protected String name; + + @SerializedName("vendorname") + @Param(description = "the vendor name of the GPU card") + protected String vendorName; + + @SerializedName("vendorid") + @Param(description = "the vendor ID of the GPU card") + protected String vendorId; + + public GpuCardResponse(GpuCard gpuCard) { + super("gpucard"); + id = gpuCard.getUuid(); + deviceId = gpuCard.getDeviceId(); + deviceName = gpuCard.getDeviceName(); + name = gpuCard.getName(); + vendorName = gpuCard.getVendorName(); + vendorId = gpuCard.getVendorId(); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVendorName() { + return vendorName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + public String getVendorId() { + return vendorId; + } + + public void setVendorId(String vendorId) { + this.vendorId = vendorId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GpuDeviceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GpuDeviceResponse.java new file mode 100644 index 00000000000..09e98b54eaa --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GpuDeviceResponse.java @@ -0,0 +1,227 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.cloud.vm.VirtualMachine; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gpu.GpuDevice; + +@EntityReference(value = GpuDevice.class) +public class GpuDeviceResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the GPU device") + private String id; + + @SerializedName(ApiConstants.BUS_ADDRESS) + @Param(description = "bus address of the GPU device or MDEV UUID for vGPU devices") + private String bussAddress; + + @SerializedName(ApiConstants.GPU_DEVICE_TYPE) + @Param(description = "bus address of the GPU device") + private GpuDevice.DeviceType type; + + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "the host ID where the GPU device is attached") + private String hostId; + + @SerializedName(ApiConstants.HOST_NAME) + @Param(description = "the host name where the GPU device is attached") + private String hostName; + + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the GPU card ID associated with this GPU device") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the GPU card name associated with this GPU device") + private String gpuCardName; + + @SerializedName(ApiConstants.VGPU_PROFILE_ID) + @Param(description = "the vGPU profile ID assigned to this GPU device") + private String vgpuProfileId; + + @SerializedName(ApiConstants.VGPU_PROFILE_NAME) + @Param(description = "the vGPU profile name assigned to this GPU device") + private String vgpuProfileName; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) + @Param(description = "the vGPU profile ID assigned to this GPU device") + private String vmId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) + @Param(description = "the vGPU profile name assigned to this GPU device") + private String vmName; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_STATE) + @Param(description = "the state of the virtual machine to which this GPU device is allocated") + private VirtualMachine.State vmState; + + @SerializedName(ApiConstants.STATE) + @Param(description = "the vGPU profile name assigned to this GPU device") + private GpuDevice.State state; + + @SerializedName(ApiConstants.MANAGED_STATE) + @Param(description = "the managed state of the GPU device (Enabled/Disabled)") + private GpuDevice.ManagedState managedState; + + @SerializedName(ApiConstants.PARENT_GPU_DEVICE_ID) + @Param(description = "the ID of the parent GPU device, if this is a vGPU") + private String parentGpuDeviceId; + + @SerializedName(ApiConstants.NUMA_NODE) + @Param(description = "the NUMA node where the GPU device is located") + private String numaNode; + + + public GpuDeviceResponse() { + // Empty constructor for serialization + super("gpudevice"); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getBussAddress() { + return bussAddress; + } + + public void setBussAddress(String bussAddress) { + this.bussAddress = bussAddress; + } + + public GpuDevice.DeviceType getType() { + return type; + } + + public void setType(GpuDevice.DeviceType type) { + this.type = type; + } + + public String getHostId() { + return hostId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public String getGpuCardId() { + return gpuCardId; + } + + public void setGpuCardId(String gpuCardId) { + this.gpuCardId = gpuCardId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public void setGpuCardName(String gpuCardName) { + this.gpuCardName = gpuCardName; + } + + public String getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(String vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public String getVgpuProfileName() { + return vgpuProfileName; + } + + public void setVgpuProfileName(String vgpuProfileName) { + this.vgpuProfileName = vgpuProfileName; + } + + public String getVmId() { + return vmId; + } + + public void setVmId(String vmId) { + this.vmId = vmId; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public void setVmState(VirtualMachine.State vmState) { + this.vmState = vmState; + } + + public GpuDevice.State getState() { + return state; + } + + public void setState(GpuDevice.State state) { + this.state = state; + } + + public GpuDevice.ManagedState getManagedState() { + return managedState; + } + + public void setManagedState(GpuDevice.ManagedState managedState) { + this.managedState = managedState; + } + + public String getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public void setParentGpuDeviceId(String parentGpuDeviceId) { + this.parentGpuDeviceId = parentGpuDeviceId; + } + + public String getNumaNode() { + return numaNode; + } + + public void setNumaNode(String numaNode) { + this.numaNode = numaNode; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSCategoryResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSCategoryResponse.java index 7872bf22085..495e349856a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSCategoryResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSCategoryResponse.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.response; +import java.util.Date; + import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.ApiConstants; @@ -26,7 +28,7 @@ import com.cloud.serializer.Param; import com.cloud.storage.GuestOsCategory; @EntityReference(value = GuestOsCategory.class) -public class GuestOSCategoryResponse extends BaseResponse { +public class GuestOSCategoryResponse extends BaseResponse implements SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "the ID of the OS category") private String id; @@ -35,6 +37,18 @@ public class GuestOSCategoryResponse extends BaseResponse { @Param(description = "the name of the OS category") private String name; + @SerializedName(ApiConstants.IS_FEATURED) + @Param(description = "Whether the OS category is featured", since = "4.21.0") + private Boolean featured; + + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.21.0") + private ResourceIconResponse resourceIconResponse; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Date when the OS category was created." ) + private Date created; + public String getId() { return id; } @@ -50,4 +64,17 @@ public class GuestOSCategoryResponse extends BaseResponse { public void setName(String name) { this.name = name; } + + public void setFeatured(Boolean featured) { + this.featured = featured; + } + + @Override + public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) { + this.resourceIconResponse = resourceIconResponse; + } + + public void setCreated(Date created) { + this.created = created; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java index fab2a2cb4ce..133338d27ae 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 @@ -44,6 +44,10 @@ public class GuestVlanRangeResponse extends BaseResponse implements ControlledEn @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") private String guestVlanRange; @@ -83,6 +87,10 @@ public class GuestVlanRangeResponse extends BaseResponse implements ControlledEn 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 6bcc1d35cb1..6716911ab82 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 @@ -49,6 +49,10 @@ public class GuestVlanResponse extends BaseResponse implements ControlledEntityR @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") private String projectId; @@ -108,6 +112,10 @@ public class GuestVlanResponse extends BaseResponse implements ControlledEntityR 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 00000000000..fe8a85b4176 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java @@ -0,0 +1,179 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; + +import java.util.Date; + +@EntityReference(value = {GuiThemeJoin.class}) +public class GuiThemeResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the custom GUI theme.") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the GUI theme.") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the GUI theme.") + private String description; + + @SerializedName(ApiConstants.CSS) + @Param(description = "The CSS to be retrieved and imported into the GUI when matching the theme access configurations.") + private String css; + + @SerializedName(ApiConstants.JSON_CONFIGURATION) + @Param(description = "The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @SerializedName(ApiConstants.COMMON_NAMES) + @Param(description = "A set of Common Names (CN) (fixed or wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @SerializedName(ApiConstants.DOMAIN_IDS) + @Param(description = "A set of domain UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @SerializedName(ApiConstants.RECURSIVE_DOMAINS) + @Param(description = "Whether to consider the subdomains of the informed domain IDs.") + private Boolean recursiveDomains; + + @SerializedName(ApiConstants.ACCOUNT_IDS) + @Param(description = "A set of account UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @SerializedName(ApiConstants.IS_PUBLIC) + @Param(description = "Defines whether a theme can be retrieved by anyone when only the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is " + + "considered as `false`.") + private Boolean isPublic; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "When the GUI theme was created.") + private Date created; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "When the GUI theme was removed.") + private Date removed; + + public GuiThemeResponse() { + super("guiThemes"); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCss() { + return css; + } + + public void setCss(String css) { + this.css = css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public void setJsonConfiguration(String jsonConfiguration) { + this.jsonConfiguration = jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public void setCommonNames(String commonNames) { + this.commonNames = commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public void setDomainIds(String domainIds) { + this.domainIds = domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public void setAccountIds(String accountIds) { + this.accountIds = accountIds; + } + + public Boolean getPublic() { + return isPublic; + } + + public void setPublic(Boolean aPublic) { + isPublic = aPublic; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + public void setRecursiveDomains(Boolean recursiveDomains) { + this.recursiveDomains = recursiveDomains; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java index 41a0fdc4567..b4de48baec4 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") 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/HostResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java index d72d23b99c9..ca31bd8b155 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement; 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; @@ -89,7 +90,6 @@ public class HostResponse extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.HYPERVISOR) @Param(description = "the host hypervisor") private String hypervisor; - @SerializedName("cpusockets") @Param(description = "the number of CPU sockets on the host") private Integer cpuSockets; @@ -165,6 +165,14 @@ public class HostResponse extends BaseResponseWithAnnotations { @Param(description = "the amount of the host's memory currently used") private Long memoryUsed; + @SerializedName("gputotal") + @Param(description = "Total GPUs on the Host", responseObject = Long.class, since = "4.21") + private Long gpuTotal; + + @SerializedName("gpuused") + @Param(description = "Used GPUs on the Host", responseObject = Long.class, since = "4.21") + private Long gpuUsed; + @SerializedName(ApiConstants.GPUGROUP) @Param(description = "GPU cards present in the host", responseObject = GpuResponse.class, since = "4.4") private List gpuGroup; @@ -185,10 +193,20 @@ public class HostResponse extends BaseResponseWithAnnotations { @Param(description = "the date and time the host was last pinged") private Date lastPinged; - @SerializedName("managementserverid") + @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") private String clusterId; @@ -221,6 +239,14 @@ public class HostResponse extends BaseResponseWithAnnotations { @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; @@ -271,12 +297,44 @@ public class HostResponse extends BaseResponseWithAnnotations { @SerializedName("ueficapability") @Param(description = "true if the host has capability to support UEFI boot") - private Boolean uefiCapabilty; + private Boolean uefiCapability; @SerializedName(ApiConstants.ENCRYPTION_SUPPORTED) @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 class HostResponse extends BaseResponseWithAnnotations { this.memoryUsed = memoryUsed; } + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + public void setGpuUsed(Long gpuUsed) { + this.gpuUsed = gpuUsed; + } + public void setGpuGroup(List gpuGroup) { this.gpuGroup = gpuGroup; } @@ -418,10 +484,26 @@ public class HostResponse extends BaseResponseWithAnnotations { 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 class HostResponse extends BaseResponseWithAnnotations { this.hostTags = hostTags; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + public String getClusterStorageAccessGroups() { + return clusterStorageAccessGroups; + } + + public void setClusterStorageAccessGroups(String clusterStorageAccessGroups) { + this.clusterStorageAccessGroups = clusterStorageAccessGroups; + } + + public String getPodStorageAccessGroups() { + return podStorageAccessGroups; + } + + public void setPodStorageAccessGroups(String podStorageAccessGroups) { + this.podStorageAccessGroups = podStorageAccessGroups; + } + + public String getZoneStorageAccessGroups() { + return zoneStorageAccessGroups; + } + + public void setZoneStorageAccessGroups(String zoneStorageAccessGroups) { + this.zoneStorageAccessGroups = zoneStorageAccessGroups; + } + + public String getExplicitHostTags() { + return explicitHostTags; + } + + 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 class HostResponse extends BaseResponseWithAnnotations { 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 class HostResponse extends BaseResponseWithAnnotations { 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 class HostResponse extends BaseResponseWithAnnotations { 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 class HostResponse extends BaseResponseWithAnnotations { return clusterType; } - public Boolean isLocalStorageActive() { + public Boolean getLocalStorageActive() { return localStorageActive; } @@ -717,7 +864,7 @@ public class HostResponse extends BaseResponseWithAnnotations { return hasEnoughCapacity; } - public Boolean isSuitableForMigration() { + public Boolean getSuitableForMigration() { return suitableForMigration; } @@ -729,14 +876,18 @@ public class HostResponse extends BaseResponseWithAnnotations { 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 class HostResponse extends BaseResponseWithAnnotations { 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 4a924ea78a0..f772da6dcb6 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,6 +19,7 @@ package org.apache.cloudstack.api.response; 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 { @@ -34,6 +35,10 @@ public class HostTagResponse extends BaseResponse { @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 class HostTagResponse extends BaseResponse { 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/IPAddressResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java index 8a9bf7789dd..8b0e3cc0201 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java @@ -75,6 +75,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co @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") private Boolean forVirtualNetwork; @@ -171,6 +175,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co @Param(description="true if range is dedicated for System VMs") private boolean forSystemVms; + @SerializedName(ApiConstants.FOR_PROVIDER) + @Param(description="true if range is dedicated for external network providers", since = "4.21.0") + private boolean forProvider; + public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } @@ -211,6 +219,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setForVirtualNetwork(Boolean forVirtualNetwork) { this.forVirtualNetwork = forVirtualNetwork; } @@ -324,4 +336,8 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co public void setForSystemVms(boolean forSystemVms) { this.forSystemVms = forSystemVms; } + + public void setForProvider(boolean forProvider) { + this.forProvider = forProvider; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ImageStoreResponse.java index 532963dbddc..ee44b6bc474 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,11 +27,11 @@ import com.google.gson.annotations.SerializedName; @EntityReference(value = ImageStore.class) public class ImageStoreResponse extends BaseResponseWithAnnotations { - @SerializedName("id") + @SerializedName(ApiConstants.ID) @Param(description = "the ID of the image store") private String id; - @SerializedName("zoneid") + @SerializedName(ApiConstants.ZONE_ID) @Param(description = "the Zone ID of the image store") private String zoneId; @@ -39,15 +39,15 @@ public class ImageStoreResponse extends BaseResponseWithAnnotations { @Param(description = "the Zone name of the image store") private String zoneName; - @SerializedName("name") + @SerializedName(ApiConstants.NAME) @Param(description = "the name of the image store") private String name; - @SerializedName("url") + @SerializedName(ApiConstants.URL) @Param(description = "the url of the image store") private String url; - @SerializedName("protocol") + @SerializedName(ApiConstants.PROTOCOL) @Param(description = "the protocol of the image store") private String protocol; @@ -55,11 +55,11 @@ public class ImageStoreResponse extends BaseResponseWithAnnotations { @Param(description = "the provider name of the image store") private String providerName; - @SerializedName("scope") + @SerializedName(ApiConstants.SCOPE) @Param(description = "the scope of the image store") private ScopeType scope; - @SerializedName("readonly") + @SerializedName(ApiConstants.READ_ONLY) @Param(description = "defines if store is read-only") private Boolean readonly; 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 e1241cc19bc..9c7a4fc09a1 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 @@ -63,6 +63,10 @@ public class InstanceGroupResponse extends BaseResponseWithAnnotations implement @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 class InstanceGroupResponse extends BaseResponseWithAnnotations implement 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/Ipv4RouteResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Ipv4RouteResponse.java new file mode 100644 index 00000000000..136c87971b7 --- /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 00000000000..1430bcd059c --- /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/KubernetesUserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java new file mode 100644 index 00000000000..cef5cdae2f4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java @@ -0,0 +1,51 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.network.router.VirtualRouter; +import com.cloud.serializer.Param; +import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.EntityReference; + +@EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class}) +public class KubernetesUserVmResponse extends UserVmResponse { + @SerializedName(ApiConstants.IS_EXTERNAL_NODE) + @Param(description = "If the VM is an externally added node") + private boolean isExternalNode; + + @SerializedName(ApiConstants.IS_ETCD_NODE) + @Param(description = "If the VM is an etcd node") + private boolean isEtcdNode; + + @SerializedName(ApiConstants.KUBERNETES_NODE_VERSION) + @Param(description = "Kubernetes version of the node") + private String nodeVersion; + + + public void setExternalNode(boolean externalNode) { + isExternalNode = externalNode; + } + + public void setEtcdNode(boolean etcdNode) { + isEtcdNode = etcdNode; + } + + public void setNodeVersion(String nodeVersion) { this.nodeVersion = nodeVersion;} +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/LoadBalancerResponse.java index 32748269587..e520dec09e7 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 @@ -87,6 +87,10 @@ public class LoadBalancerResponse extends BaseResponse implements ControlledEnti @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") private String state; @@ -158,6 +162,11 @@ public class LoadBalancerResponse extends BaseResponse implements ControlledEnti 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/LoginCmdResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/LoginCmdResponse.java index 84c79d32321..43f92db84cb 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 @@ -86,6 +86,10 @@ 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; + public String getUsername() { return username; } @@ -211,4 +215,12 @@ public class LoginCmdResponse extends AuthenticationCmdResponse { public void setIssuerFor2FA(String issuerFor2FA) { this.issuerFor2FA = issuerFor2FA; } + + public String getManagementServerId() { + return managementServerId; + } + + public void setManagementServerId(String managementServerId) { + this.managementServerId = managementServerId; + } } 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 330f91e69f3..8592a762c61 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java @@ -24,7 +24,9 @@ import org.apache.cloudstack.api.BaseResponse; 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 { @@ -72,10 +74,35 @@ public class ManagementServerResponse extends BaseResponse { @Param(description = "the running OS kernel version for this Management Server") private String kernelVersion; + @Deprecated @SerializedName(ApiConstants.SERVICE_IP) - @Param(description = "the IP Address for this Management Server") + @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 class ManagementServerResponse extends BaseResponse { 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 class ManagementServerResponse extends BaseResponse { 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/NetworkOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java index b73163a5d05..81a8129ecb7 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 @@ -107,9 +107,9 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if network offering can be used by Tungsten-Fabric networks only") private Boolean forTungsten; - @SerializedName(ApiConstants.NSX_MODE) - @Param(description = "Mode in which the network will operate. This parameter is only relevant for NSX offerings") - private String nsxMode; + @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") @@ -159,6 +159,14 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { @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; } @@ -235,8 +243,8 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { this.forTungsten = forTungsten; } - public void setNsxMode(String nsxMode) { - this.nsxMode = nsxMode; + public void setNetworkMode(String networkMode) { + this.networkMode = networkMode; } public void setIsPersistent(Boolean isPersistent) { @@ -306,4 +314,20 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { 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/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java index d34f949372a..d5db8b4dabb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java @@ -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,12 +28,11 @@ import org.apache.cloudstack.api.BaseResponseWithAssociatedNetwork; import org.apache.cloudstack.api.EntityReference; import com.cloud.network.Network; -import com.cloud.projects.ProjectAccount; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") -@EntityReference(value = {Network.class, ProjectAccount.class}) +@EntityReference(value = {Network.class}) public class NetworkResponse extends BaseResponseWithAssociatedNetwork implements ControlledEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) @@ -135,6 +135,14 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @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") private String aclType; @@ -187,6 +195,10 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @Param(description = "true 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") private Boolean specifyIpRanges; @@ -199,14 +211,6 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @Param(description = "Name of the VPC to which this network belongs", since = "4.15") private String vpcName; - @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) - @Param(description = "the ID of the Network associated with this network") - private String associatedNetworkId; - - @SerializedName(ApiConstants.ASSOCIATED_NETWORK) - @Param(description = "the name of the Network associated with this network") - private String associatedNetworkName; - @SerializedName(ApiConstants.TUNGSTEN_VIRTUAL_ROUTER_UUID) @Param(description = "Tungsten-Fabric virtual router the network belongs to") private String tungstenVirtualRouterUuid; @@ -292,7 +296,7 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement 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) @@ -315,6 +319,18 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @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 class NetworkResponse extends BaseResponseWithAssociatedNetwork implement 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 class NetworkResponse extends BaseResponseWithAssociatedNetwork implement this.domain = domain; } + @Override public void setDomainPath(String domainPath) { this.domainPath = domainPath; } @@ -486,6 +511,10 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement this.restartRequired = restartRequired; } + public void setSpecifyVlan(Boolean specifyVlan) { + this.specifyVlan = specifyVlan; + } + public void setSpecifyIpRanges(Boolean specifyIpRanges) { this.specifyIpRanges = specifyIpRanges; } @@ -582,14 +611,6 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement this.vpcName = vpcName; } - public void setAssociatedNetworkId(String associatedNetworkId) { - this.associatedNetworkId = associatedNetworkId; - } - - public void setAssociatedNetworkName(String associatedNetworkName) { - this.associatedNetworkName = associatedNetworkName; - } - @Override public void setResourceIconResponse(ResourceIconResponse icon) { this.icon = icon; @@ -623,6 +644,18 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement 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 class NetworkResponse extends BaseResponseWithAssociatedNetwork implement 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/ObjectStoreResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java index e4030799aa7..dcb93aaaf1d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.api.response; import com.cloud.serializer.Param; + +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.storage.object.ObjectStore; import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponseWithAnnotations; @@ -24,15 +26,15 @@ import org.apache.cloudstack.api.EntityReference; @EntityReference(value = ObjectStore.class) public class ObjectStoreResponse extends BaseResponseWithAnnotations { - @SerializedName("id") + @SerializedName(ApiConstants.ID) @Param(description = "the ID of the object store") private String id; - @SerializedName("name") + @SerializedName(ApiConstants.NAME) @Param(description = "the name of the object store") private String name; - @SerializedName("url") + @SerializedName(ApiConstants.URL) @Param(description = "the url of the object store") private String url; @@ -44,6 +46,10 @@ public class ObjectStoreResponse extends BaseResponseWithAnnotations { @Param(description = "the total size of the object store") private Long storageTotal; + @SerializedName("storageallocated") + @Param(description = "the allocated size of the object store") + private Long storageAllocated; + @SerializedName("storageused") @Param(description = "the object store currently used size") private Long storageUsed; @@ -96,6 +102,14 @@ public class ObjectStoreResponse extends BaseResponseWithAnnotations { this.storageTotal = storageTotal; } + public Long getStorageAllocated() { + return storageAllocated; + } + + public void setStorageAllocated(Long storageAllocated) { + this.storageAllocated = storageAllocated; + } + public Long getStorageUsed() { return storageUsed; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/OvsProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/OvsProviderResponse.java index ac5b9e309c8..2b67e1618dc 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 @@ -54,6 +54,10 @@ public class OvsProviderResponse extends BaseResponse implements @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 class OvsProviderResponse extends BaseResponse implements 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 00000000000..802294171fa --- /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/PodResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PodResponse.java index 587fabfae8d..6a1afaecbcf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/PodResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/PodResponse.java @@ -85,6 +85,14 @@ public class PodResponse extends BaseResponseWithAnnotations { @Param(description = "the capacity of the Pod", responseObject = CapacityResponse.class) private List capacities; + @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups for the pod", since = "4.21.0") + private String storageAccessGroups; + + @SerializedName(ApiConstants.ZONE_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the zone", since = "4.21.0") + private String zoneStorageAccessGroups; + public String getId() { return id; } @@ -184,4 +192,20 @@ public class PodResponse extends BaseResponseWithAnnotations { public void setCapacities(List capacities) { this.capacities = capacities; } + + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + public String getZoneStorageAccessGroups() { + return zoneStorageAccessGroups; + } + + public void setZoneStorageAccessGroups(String zoneStorageAccessGroups) { + this.zoneStorageAccessGroups = zoneStorageAccessGroups; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java index 65401eb2a02..414aed94bad 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 @@ -89,6 +89,10 @@ public class PrivateGatewayResponse extends BaseResponseWithAssociatedNetwork im @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; @@ -165,6 +169,10 @@ public class PrivateGatewayResponse extends BaseResponseWithAssociatedNetwork im 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 aab03074155..d035622f65f 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 @@ -73,6 +73,10 @@ public class ProjectAccountResponse extends BaseResponse implements ControlledVi @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) private List users; @@ -110,6 +114,11 @@ public class ProjectAccountResponse extends BaseResponse implements ControlledVi 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 4462ea91568..ad3f99f6b3f 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 @@ -51,6 +51,10 @@ public class ProjectInvitationResponse extends BaseResponse implements Controlle @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") private String accountName; @@ -87,6 +91,11 @@ public class ProjectInvitationResponse extends BaseResponse implements Controlle 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 1c63697559b..e72f6b86036 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java @@ -116,6 +116,18 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou @Param(description = "the total memory (in MB) available to be created for this project", since = "4.2.0") private String memoryAvailable; + @SerializedName("gpulimit") + @Param(description = "the total number of gpus the project can own", since = "4.21.0") + private String gpuLimit; + + @SerializedName("gputotal") + @Param(description = "the total number of gpus owned by project", since = "4.21.0") + private Long gpuTotal; + + @SerializedName("gpuavailable") + @Param(description = "the total number of gpus available to be created for this project", since = "4.21.0") + private String gpuAvailable; + @SerializedName("primarystoragelimit") @Param(description = "the total primary storage space (in GiB) the project can own", since = "4.2.0") private String primaryStorageLimit; @@ -140,6 +152,30 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou @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") private String vmLimit; @@ -188,6 +224,30 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou @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") private String templateLimit; @@ -320,6 +380,36 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou 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; @@ -405,6 +495,21 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou this.memoryAvailable = memoryAvailable; } + @Override + public void setGpuLimit(String gpuLimit) { + this.gpuLimit = gpuLimit; + } + + @Override + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + @Override + public void setGpuAvailable(String gpuAvailable) { + this.gpuAvailable = gpuAvailable; + } + @Override public void setPrimaryStorageLimit(String primaryStorageLimit) { this.primaryStorageLimit = primaryStorageLimit; @@ -435,6 +540,36 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou 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; } 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 00000000000..3807d0d5b16 --- /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/RegisterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RegisterResponse.java index 5faedabfc16..dd17cc5cc8a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RegisterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RegisterResponse.java @@ -18,19 +18,24 @@ 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 RegisterResponse extends BaseResponse { - @SerializedName("apikey") + @SerializedName(ApiConstants.API_KEY) @Param(description = "the api key of the registered user", isSensitive = true) private String apiKey; - @SerializedName("secretkey") + @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; } @@ -46,4 +51,8 @@ public class RegisterResponse extends BaseResponse { 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 0e078bea5bd..54164e3ac34 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 @@ -65,6 +65,10 @@ public class RemoteAccessVpnResponse extends BaseResponse implements ControlledE @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") private String state; @@ -104,6 +108,10 @@ public class RemoteAccessVpnResponse extends BaseResponse implements ControlledE 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 3a698618585..74511a0f743 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 @@ -45,6 +45,10 @@ public class ResourceCountResponse extends BaseResponse implements ControlledEnt @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.") private String resourceType; @@ -76,6 +80,11 @@ public class ResourceCountResponse extends BaseResponse implements ControlledEnt 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(); 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 f9e6df3a038..66de71dd763 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitAndCountResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitAndCountResponse.java @@ -48,6 +48,12 @@ public interface ResourceLimitAndCountResponse { public void setMemoryAvailable(String memoryAvailable); + public void setGpuLimit(String gpuLimit); + + public void setGpuTotal(Long gpuTotal); + + public void setGpuAvailable(String gpuAvailable); + public void setPrimaryStorageLimit(String primaryStorageLimit); public void setPrimaryStorageTotal(Long primaryStorageTotal); @@ -84,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); 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 72c1c66f5e8..b5b03873f3f 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 @@ -40,6 +40,10 @@ public class ResourceLimitResponse extends BaseResponse implements ControlledEnt @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.") private String resourceType; @@ -84,6 +88,10 @@ public class ResourceLimitResponse extends BaseResponse implements ControlledEnt 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(); 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 44325560534..26e4d19cdba 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 @@ -61,6 +61,10 @@ public class ResourceTagResponse extends BaseResponse implements ControlledViewE @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") private String customer; @@ -96,6 +100,11 @@ public class ResourceTagResponse extends BaseResponse implements ControlledViewE 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/RoleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RoleResponse.java index 1861028f0ed..92e3b46139f 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 @@ -36,6 +36,10 @@ public class RoleResponse extends BaseRoleResponse { @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 class RoleResponse extends BaseRoleResponse { 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/RouterHealthCheckResultResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultResponse.java index f98cf0acd5d..00f1e4e3bb0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultResponse.java @@ -34,7 +34,7 @@ public class RouterHealthCheckResultResponse extends BaseResponse { @Param(description = "the type of the health check - basic or advanced") private String checkType; - @SerializedName(ApiConstants.RESULT) + @SerializedName(ApiConstants.SUCCESS) @Param(description = "result of the health check") private boolean result; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SecurityGroupResponse.java index c96421b0a40..de486b5a374 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 @@ -63,6 +63,10 @@ public class SecurityGroupResponse extends BaseResponse implements ControlledVie @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) private Set ingressRules; @@ -126,6 +130,11 @@ public class SecurityGroupResponse extends BaseResponse implements ControlledVie 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/ServiceOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java index c7740c19214..4565a878b34 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java @@ -80,7 +80,7 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if the vm needs to be volatile, i.e., on every reboot of vm from API root disk is discarded and creates a new root disk") private Boolean isVolatile; - @SerializedName("storagetags") + @SerializedName(ApiConstants.STORAGE_TAGS) @Param(description = "the tags for the service offering") private String tags; @@ -234,6 +234,58 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if virtual machine root disk will be encrypted on storage", since = "4.18") private Boolean encryptRoot; + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the ID of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the name of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardName; + + @SerializedName(ApiConstants.VGPU_PROFILE_ID) + @Param(description = "the ID of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileId; + + @SerializedName(ApiConstants.VGPU_PROFILE_NAME) + @Param(description = "the name of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileName; + + @SerializedName(ApiConstants.VIDEORAM) + @Param(description = "the video RAM size in MB") + private Long videoRam; + + @SerializedName(ApiConstants.MAXHEADS) + @Param(description = "the maximum number of display heads") + private Long maxHeads; + + @SerializedName(ApiConstants.MAXRESOLUTIONX) + @Param(description = "the maximum X resolution") + private Long maxResolutionX; + + @SerializedName(ApiConstants.MAXRESOLUTIONY) + @Param(description = "the maximum Y resolution") + private Long maxResolutionY; + + @SerializedName(ApiConstants.GPU_COUNT) + @Param(description = "the count of GPUs to attach ", since = "4.21") + private Integer gpuCount; + + @SerializedName(ApiConstants.GPU_DISPLAY) + @Param(description = "whether GPU device is used for display or not ", since = "4.21") + private Boolean gpuDisplay; + + @SerializedName(ApiConstants.PURGE_RESOURCES) + @Param(description = "Whether to cleanup VM and its associated resource upon expunge", since = "4.20") + private Boolean purgeResources; + + @SerializedName(ApiConstants.INSTANCE_LEASE_DURATION) + @Param(description = "Instance lease duration (in days) for service offering", since = "4.21.0") + private Integer leaseDuration; + + @SerializedName(ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION) + @Param(description = "Action to be taken once lease is over", since = "4.21.0") + private String leaseExpiryAction; + public ServiceOfferingResponse() { } @@ -501,6 +553,22 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { this.cacheMode = cacheMode; } + public Integer getLeaseDuration() { + return leaseDuration; + } + + public void setLeaseDuration(Integer leaseDuration) { + this.leaseDuration = leaseDuration; + } + + public String getLeaseExpiryAction() { + return leaseExpiryAction; + } + + public void setLeaseExpiryAction(String leaseExpiryAction) { + this.leaseExpiryAction = leaseExpiryAction; + } + public String getVsphereStoragePolicy() { return vsphereStoragePolicy; } @@ -555,4 +623,88 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { } public void setEncryptRoot(Boolean encrypt) { this.encryptRoot = encrypt; } + + public String getVgpuProfileName() { + return vgpuProfileName; + } + + public void setVgpuProfileName(String vgpuProfileName) { + this.vgpuProfileName = vgpuProfileName; + } + + public Long getVideoRam() { + return videoRam; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } + + public String getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(String vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public void setGpuCardName(String gpuCardName) { + this.gpuCardName = gpuCardName; + } + + public String getGpuCardId() { + return gpuCardId; + } + + public void setGpuCardId(String gpuCardId) { + this.gpuCardId = gpuCardId; + } + + public Integer getGpuCount() { + return gpuCount; + } + + public void setGpuCount(Integer gpuCount) { + this.gpuCount = gpuCount; + } + + public Boolean getGpuDisplay() { + return gpuDisplay; + } + + public void setGpuDisplay(Boolean gpuDisplay) { + this.gpuDisplay = gpuDisplay; + } + + public void setPurgeResources(Boolean purgeResources) { + this.purgeResources = purgeResources; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SharedFSProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SharedFSProviderResponse.java new file mode 100644 index 00000000000..4d92945646f --- /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 00000000000..bac348fe36e --- /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 babc9bf4432..4ae140ec573 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 @@ -98,6 +98,10 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotation @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") private Date removed; @@ -193,4 +197,9 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotation this.domain = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + } 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 1f7509239d1..a9fd0f9703c 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 @@ -120,6 +120,10 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont @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") private Date created; @@ -241,6 +245,11 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont 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 cdd8e4f3d87..1e63ba896c7 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 @@ -66,6 +66,10 @@ public class Site2SiteVpnGatewayResponse extends BaseResponse implements Control @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") private Date removed; @@ -119,6 +123,10 @@ public class Site2SiteVpnGatewayResponse extends BaseResponse implements Control 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/SnapshotResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java index e160f64ebe9..9f7a7f42dec 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 @@ -47,6 +47,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements @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") private String projectId; @@ -71,6 +75,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements @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") private Date created; @@ -183,6 +191,11 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setSnapshotType(String snapshotType) { this.snapshotType = snapshotType; } @@ -199,6 +212,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements this.volumeType = volumeType; } + public void setVolumeState(String volumeState) { + this.volumeState = volumeState; + } + public void setCreated(Date created) { this.created = created; } 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 79c7f9040a1..9008fce2392 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java @@ -42,9 +42,17 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit @Param(description = "VPC the static route belongs to") private String vpcId; - @SerializedName(ApiConstants.GATEWAY_ID) + @SerializedName(ApiConstants.VPC_GATEWAY_ID) @Param(description = "VPC gateway the route is created for") - private String gatewayId; + private String vpcGatewayId; + + @SerializedName(ApiConstants.VPC_GATEWAY_IP) + @Param(description = "IP of VPC gateway the route is created for", since = "4.21.0") + private String vpcGatewayIp; + + @SerializedName(ApiConstants.NEXT_HOP) + @Param(description = "Next hop of the static route", since = "4.21.0") + private String nextHop; @SerializedName(ApiConstants.CIDR) @Param(description = "static route CIDR") @@ -70,6 +78,10 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit @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) private List tags; @@ -91,8 +103,16 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit this.vpcId = vpcId; } - public void setGatewayId(String gatewayId) { - this.gatewayId = gatewayId; + public void setVpcGatewayId(String vpcGatewayId) { + this.vpcGatewayId = vpcGatewayId; + } + + public void setVpcGatewayIp(String vpcGatewayIp) { + this.vpcGatewayIp = vpcGatewayIp; + } + + public void setNextHop(String nextHop) { + this.nextHop = nextHop; } public void setCidr(String cidr) { @@ -114,6 +134,10 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit 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/StorageAccessGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StorageAccessGroupResponse.java new file mode 100644 index 00000000000..a6324dd62a9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/StorageAccessGroupResponse.java @@ -0,0 +1,108 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import com.cloud.serializer.Param; + +public class StorageAccessGroupResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the storage access group") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the storage access group") + private String name; + + @SerializedName("hosts") + @Param(description = "List of Hosts in the Storage Access Group") + private ListResponse hostResponseList; + + @SerializedName("clusters") + @Param(description = "List of Clusters in the Storage Access Group") + private ListResponse clusterResponseList; + + @SerializedName("pods") + @Param(description = "List of Pods in the Storage Access Group") + private ListResponse podResponseList; + + @SerializedName("zones") + @Param(description = "List of Zones in the Storage Access Group") + private ListResponse zoneResponseList; + + @SerializedName("storagepools") + @Param(description = "List of Storage Pools in the Storage Access Group") + private ListResponse storagePoolResponseList; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ListResponse getHostResponseList() { + return hostResponseList; + } + + public void setHostResponseList(ListResponse hostResponseList) { + this.hostResponseList = hostResponseList; + } + + public ListResponse getClusterResponseList() { + return clusterResponseList; + } + + public void setClusterResponseList(ListResponse clusterResponseList) { + this.clusterResponseList = clusterResponseList; + } + + public ListResponse getPodResponseList() { + return podResponseList; + } + + public void setPodResponseList(ListResponse podResponseList) { + this.podResponseList = podResponseList; + } + + public ListResponse getZoneResponseList() { + return zoneResponseList; + } + + public void setZoneResponseList(ListResponse zoneResponseList) { + this.zoneResponseList = zoneResponseList; + } + + public ListResponse getStoragePoolResponseList() { + return storagePoolResponseList; + } + + public void setStoragePoolResponseList(ListResponse storagePoolResponseList) { + this.storagePoolResponseList = storagePoolResponseList; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/StoragePoolResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StoragePoolResponse.java index 183290ec9eb..abc674ff0f9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/StoragePoolResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/StoragePoolResponse.java @@ -97,10 +97,26 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { @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") 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; @@ -133,6 +149,14 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { @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; } @@ -300,6 +324,22 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { 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 +348,14 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { this.tags = tags; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + public Boolean getIsTagARule() { return isTagARule; } @@ -347,4 +395,40 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { 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/SystemVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java index 31a8b731491..7b1784fc767 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java @@ -38,14 +38,6 @@ public class SystemVmResponse extends BaseResponseWithAnnotations { @Param(description = "the system VM type") private String systemVmType; - @SerializedName("jobid") - @Param(description = "the job ID associated with the system VM. This is only displayed if the router listed is part of a currently running asynchronous job.") - private String jobId; - - @SerializedName("jobstatus") - @Param(description = "the job status associated with the system VM. This is only displayed if the router listed is part of a currently running asynchronous job.") - private Integer jobStatus; - @SerializedName("zoneid") @Param(description = "the Zone ID for the system VM") private String zoneId; @@ -186,6 +178,10 @@ public class SystemVmResponse extends BaseResponseWithAnnotations { @Param(description = "the name of the service offering of the system virtual machine.") private String serviceOfferingName; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU arch of the system VM", since = "4.20.1") + private String arch; + @Override public String getObjectId() { return this.getId(); @@ -490,4 +486,8 @@ public class SystemVmResponse extends BaseResponseWithAnnotations { public void setServiceOfferingName(String serviceOfferingName) { this.serviceOfferingName = serviceOfferingName; } + + public void setArch(String arch) { + this.arch = arch; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java index 3abd44941d9..a94dbd95a56 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java @@ -93,6 +93,8 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements @Param(description = "the name of the OS type for this template.") private String osTypeName; + private transient Long osTypeCategoryId; + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description = "the account id to which the template belongs") private String accountId; @@ -135,6 +137,10 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements @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") private String domainId; @@ -179,6 +185,10 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements @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") private int bits; @@ -200,6 +210,11 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements since = "4.15") private Boolean deployAsIs; + @SerializedName(ApiConstants.FOR_CKS) + @Param(description = "If true it indicates that the template can be used for CKS cluster deployments", + since = "4.21.0") + private Boolean forCks; + @SerializedName(ApiConstants.DEPLOY_AS_IS_DETAILS) @Param(description = "VMware only: additional key/value details tied with deploy-as-is template", since = "4.15") @@ -239,6 +254,12 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements @SerializedName(ApiConstants.USER_DATA_PARAMS) @Param(description="list of parameters which contains the list of keys or string parameters that are needed to be passed for any variables declared in userdata", since = "4.18.0") private String userDataParams; + @SerializedName(ApiConstants.EXTENSION_ID) @Param(description="The ID of extension linked to this template", since = "4.21.0") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) @Param(description="The name of extension linked to this template", since = "4.21.0") + private String extensionName; + public TemplateResponse() { tags = new LinkedHashSet<>(); } @@ -277,6 +298,14 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements this.osTypeName = osTypeName; } + public Long getOsTypeCategoryId() { + return osTypeCategoryId; + } + + public void setOsTypeCategoryId(Long osTypeCategoryId) { + this.osTypeCategoryId = osTypeCategoryId; + } + public void setId(String id) { this.id = id; } @@ -354,6 +383,11 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements 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 class TemplateResponse extends BaseResponseWithTagInformation implements this.deployAsIs = deployAsIs; } + public void setForCks(Boolean forCks) { + this.forCks = forCks; + } + public void setParentTemplateId(String parentTemplateId) { this.parentTemplateId = parentTemplateId; } @@ -511,4 +549,24 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements 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/UnmanageVMInstanceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UnmanageVMInstanceResponse.java index cec70f20cff..e9d45cb506a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UnmanageVMInstanceResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UnmanageVMInstanceResponse.java @@ -24,7 +24,7 @@ import org.apache.cloudstack.api.BaseResponse; public class UnmanageVMInstanceResponse extends BaseResponse { - @SerializedName(ApiConstants.RESULT) + @SerializedName(ApiConstants.SUCCESS) @Param(description = "result of the unmanage VM operation") private boolean success; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java index 7a26b178591..c1156f5f23a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java @@ -79,6 +79,14 @@ public class UnmanagedInstanceResponse extends BaseResponse { @Param(description = "the operating system of the virtual machine") private String operatingSystem; + @SerializedName(ApiConstants.BOOT_MODE) + @Param(description = "indicates the boot mode") + private String bootMode; + + @SerializedName(ApiConstants.BOOT_TYPE) + @Param(description = "indicates the boot type") + private String bootType; + @SerializedName(ApiConstants.DISK) @Param(description = "the list of disks associated with the virtual machine", responseObject = UnmanagedInstanceDiskResponse.class) private Set disks; @@ -211,4 +219,20 @@ public class UnmanagedInstanceResponse extends BaseResponse { public void addNic(NicResponse nic) { this.nics.add(nic); } + + public String getBootMode() { + return bootMode; + } + + public void setBootMode(String bootMode) { + this.bootMode = bootMode; + } + + public String getBootType() { + return bootType; + } + + public void setBootType(String bootType) { + this.bootType = bootType; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UsageRecordResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UsageRecordResponse.java index 7bcb1afd2d2..4aeded64287 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; @@ -51,6 +52,10 @@ public class UsageRecordResponse extends BaseResponseWithTagInformation implemen @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") private String zoneId; @@ -133,11 +138,11 @@ public class UsageRecordResponse extends BaseResponseWithTagInformation implemen @SerializedName(ApiConstants.START_DATE) @Param(description = "start date of the usage record") - private String startDate; + private Date startDate; @SerializedName(ApiConstants.END_DATE) @Param(description = "end date of the usage record") - private String endDate; + private Date endDate; @SerializedName("issourcenat") @Param(description = "True if the IPAddress is source NAT") @@ -160,7 +165,7 @@ public class UsageRecordResponse extends BaseResponseWithTagInformation implemen private String vpcId; public UsageRecordResponse() { - tags = new LinkedHashSet(); + tags = new LinkedHashSet<>(); } public void setTags(Set tags) { @@ -245,11 +250,11 @@ public class UsageRecordResponse extends BaseResponseWithTagInformation implemen 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 class UsageRecordResponse extends BaseResponseWithTagInformation implemen 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 83b97f00c15..5beef5ac556 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 @@ import com.cloud.serializer.Param; 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 class UsageTypeResponse extends BaseResponse { 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 e69094c8f80..ce344596aeb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserDataResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserDataResponse.java @@ -27,37 +27,41 @@ import org.apache.cloudstack.api.EntityReference; public class UserDataResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "ID of the ssh keypair") + @Param(description = "ID of the User Data") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "Name of the userdata") + @Param(description = "Name of the User Data") private String name; - @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the owner id of the userdata") + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the owner id of the User Data") private String accountId; - @SerializedName(ApiConstants.ACCOUNT) @Param(description="the owner of the userdata") + @SerializedName(ApiConstants.ACCOUNT) @Param(description="the owner of the User Data") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the userdata", since = "4.19.1") + @Param(description = "the project id of the User Data", since = "4.19.1") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the userdata", since = "4.19.1") + @Param(description = "the project name of the User Data", since = "4.19.1") private String projectName; - @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id of the userdata owner") + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id of the User Data owner") private String domainId; - @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the userdata owner") + @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the User Data owner") private String domain; - @SerializedName(ApiConstants.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() { @@ -143,4 +147,9 @@ public class UserDataResponse extends BaseResponseWithAnnotations implements Con 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 1a17f3b8698..5e4e6e1f3c8 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 @@ -67,7 +67,7 @@ public class UserResponse extends BaseResponse implements SetResourceIconRespons @Param(description = "the account type of the user") private Integer accountType; - @SerializedName("usersource") + @SerializedName(ApiConstants.USER_SOURCE) @Param(description = "the source type of the user in lowercase, such as native, ldap, saml2") private String userSource; @@ -128,6 +128,10 @@ public class UserResponse extends BaseResponse implements SetResourceIconRespons @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 class UserResponse extends BaseResponse implements SetResourceIconRespons 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/UserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java index 763265e109d..ca5bd09a9aa 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java @@ -31,6 +31,7 @@ import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponseWithTagInformation; import org.apache.cloudstack.api.EntityReference; +import org.apache.commons.collections.CollectionUtils; import com.cloud.network.router.VirtualRouter; import com.cloud.serializer.Param; @@ -81,6 +82,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "the name of the domain in which the virtual machine 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") private Date created; @@ -137,6 +142,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @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") private String templateDisplayText; @@ -173,6 +182,42 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "the name of the disk offering of the virtual machine. This parameter should not be used for retrieving disk offering details of DATA volumes. Use listVolumes API instead", since = "4.4") private String diskOfferingName; + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the ID of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the name of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardName; + + @SerializedName(ApiConstants.VGPU_PROFILE_ID) + @Param(description = "the ID of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileId; + + @SerializedName(ApiConstants.VGPU_PROFILE_NAME) + @Param(description = "the name of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileName; + + @SerializedName(ApiConstants.VIDEORAM) + @Param(description = "the video RAM size in MB") + private Long videoRam; + + @SerializedName(ApiConstants.MAXHEADS) + @Param(description = "the maximum number of display heads") + private Long maxHeads; + + @SerializedName(ApiConstants.MAXRESOLUTIONX) + @Param(description = "the maximum X resolution") + private Long maxResolutionX; + + @SerializedName(ApiConstants.MAXRESOLUTIONY) + @Param(description = "the maximum Y resolution") + private Long maxResolutionY; + + @SerializedName(ApiConstants.GPU_COUNT) + @Param(description = "the count of GPUs on the virtual machine", since = "4.21") + private Integer gpuCount; + @SerializedName(ApiConstants.BACKUP_OFFERING_ID) @Param(description = "the ID of the backup offering of the virtual machine", since = "4.14") private String backupOfferingId; @@ -269,6 +314,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @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") private String publicIpId; @@ -307,6 +356,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "true if vm contains XS/VMWare tools inorder to support dynamic scaling of VM 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; @@ -375,6 +428,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 +601,42 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co return diskOfferingName; } + public String getGpuCardId() { + return gpuCardId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public String getVgpuProfileId() { + return vgpuProfileId; + } + + public String getVgpuProfileName() { + return vgpuProfileName; + } + + public Long getVideoRam() { + return videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public Integer getGpuCount() { + return gpuCount; + } + public String getBackupOfferingId() { return backupOfferingId; } @@ -623,6 +732,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co return hypervisor; } + public String getIpAddress() { + return ipAddress; + } + public String getPublicIpId() { return publicIpId; } @@ -690,6 +803,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setCreated(Date created) { this.created = created; } @@ -802,6 +919,42 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co this.diskOfferingName = diskOfferingName; } + public void setGpuCardId(String gpuCardId) { + this.gpuCardId = gpuCardId; + } + + public void setGpuCardName(String gpuCardName) { + this.gpuCardName = gpuCardName; + } + + public void setVgpuProfileId(String vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public void setVgpuProfileName(String vgpuProfileName) { + this.vgpuProfileName = vgpuProfileName; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } + + public void setGpuCount(Integer gpuCount) { + this.gpuCount = gpuCount; + } + public void setBackupOfferingId(String backupOfferingId) { this.backupOfferingId = backupOfferingId; } @@ -859,6 +1012,13 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co 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) { @@ -963,6 +1123,14 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co isDynamicallyScalable = dynamicallyScalable; } + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } + public String getOsTypeId() { return osTypeId; } @@ -1076,6 +1244,14 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co this.templateType = templateType; } + public String getTemplateFormat() { + return templateFormat; + } + + public void setTemplateFormat(String templateFormat) { + this.templateFormat = templateFormat; + } + public List getVnfNics() { return vnfNics; } @@ -1105,4 +1281,49 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co } 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/VMSnapshotResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VMSnapshotResponse.java index 9b553ed0744..703f27b537c 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 @@ -108,6 +108,10 @@ public class VMSnapshotResponse extends BaseResponseWithTagInformation implement @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") private String hypervisor; @@ -261,6 +265,11 @@ public class VMSnapshotResponse extends BaseResponseWithTagInformation implement 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 1b739e56442..cf819491c2c 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 @@ -30,7 +30,7 @@ public class VMUserDataResponse extends BaseResponse { private String vmId; @SerializedName(ApiConstants.USER_DATA) - @Param(description = "Base 64 encoded VM user data") + @Param(description = "Base64 encoded VM 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 00000000000..382b391ef59 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/VgpuProfileResponse.java @@ -0,0 +1,135 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gpu.GpuCard; +import org.apache.cloudstack.gpu.VgpuProfile; + +@EntityReference(value = VgpuProfile.class) +public class VgpuProfileResponse extends GpuCardResponse { + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "the description of the vGPU profile") + private String description; + + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the ID of the GPU card associated with this vGPU profile") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the name of the vGPU profile") + private String gpuCardName; + + @SerializedName(ApiConstants.MAX_VGPU_PER_PHYSICAL_GPU) + @Param(description = "the maximum number of vGPUs per physical GPU") + private Long maxVgpuPerPgpu; + + @SerializedName(ApiConstants.VIDEORAM) + @Param(description = "the video RAM size in MB") + private Long videoRam; + + @SerializedName(ApiConstants.MAXHEADS) + @Param(description = "the maximum number of display heads") + private Long maxHeads; + + @SerializedName(ApiConstants.MAXRESOLUTIONX) + @Param(description = "the maximum X resolution") + private Long maxResolutionX; + + @SerializedName(ApiConstants.MAXRESOLUTIONY) + @Param(description = "the maximum Y resolution") + private Long maxResolutionY; + + public VgpuProfileResponse(VgpuProfile vgpuProfile, GpuCard gpuCard) { + super(gpuCard); + id = vgpuProfile.getUuid(); + name = vgpuProfile.getName(); + description = vgpuProfile.getDescription(); + gpuCardId = gpuCard.getUuid(); + gpuCardName = gpuCard.getName(); + maxVgpuPerPgpu = vgpuProfile.getMaxVgpuPerPgpu(); + videoRam = vgpuProfile.getVideoRam(); + maxHeads = vgpuProfile.getMaxHeads(); + maxResolutionX = vgpuProfile.getMaxResolutionX(); + maxResolutionY = vgpuProfile.getMaxResolutionY(); + setObjectName("vgpuprofile"); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public String getGpuCardId() { + return gpuCardId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getVideoRam() { + return videoRam; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VirtualMachineResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VirtualMachineResponse.java new file mode 100644 index 00000000000..7d676292b8a --- /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 c3b46710f6f..b9d18e23810 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 @@ -60,6 +60,10 @@ public class VirtualRouterProviderResponse extends BaseResponse implements Contr @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 class VirtualRouterProviderResponse extends BaseResponse implements Contr 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 aac6dd3c577..2be08bb7fcb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java @@ -55,6 +55,10 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit @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") private String podId; @@ -123,9 +127,9 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit @Param(description = "indicates whether VLAN IP range is dedicated to system vms or not") private Boolean forSystemVms; - @SerializedName(ApiConstants.FOR_NSX) - @Param(description = "indicates whether IP range is dedicated to NSX resources or not") - private Boolean forNsx; + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "indicates to which provider the IP range is dedicated to", since = "4.21.0") + private String provider; public void setId(String id) { this.id = id; @@ -166,6 +170,11 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setPodId(String podId) { this.podId = podId; } @@ -240,7 +249,7 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit this.ip6Cidr = ip6Cidr; } - public void setForNsx(Boolean forNsx) { - this.forNsx = forNsx; + public void setProvider(String provider) { + this.provider = provider; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VolumeForImportResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VolumeForImportResponse.java new file mode 100644 index 00000000000..803f154816a --- /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 0d502a6d7a7..209ca57c50d 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 @@ -93,7 +93,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co @Param(description = "display name of the virtual machine") private String virtualMachineDisplayName; - @SerializedName("vmstate") + @SerializedName(ApiConstants.VIRTUAL_MACHINE_STATE) @Param(description = "state of the virtual machine") private String virtualMachineState; @@ -145,6 +145,10 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co @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") private String storageType; @@ -211,7 +215,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co @SerializedName("destroyed") @Param(description = "the boolean state of whether the volume is destroyed or not") - private Boolean destroyed; + private boolean destroyed; @SerializedName(ApiConstants.SERVICE_OFFERING_ID) @Param(description = "ID of the service offering for root disk") @@ -227,7 +231,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co @SerializedName("isextractable") @Param(description = "true if the volume is extractable, false otherwise") - private Boolean extractable; + private boolean extractable; @SerializedName(ApiConstants.STATUS) @Param(description = "the status of the volume") @@ -235,7 +239,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co @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; + private boolean displayVolume; @SerializedName(ApiConstants.PATH) @Param(description = "the path of the volume") @@ -257,12 +261,16 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co @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) @@ -290,13 +298,17 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co 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") + @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") + @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; } @@ -314,11 +326,11 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co return this.getId(); } - public Boolean isDestroyed() { + public boolean isDestroyed() { return destroyed; } - public void setDestroyed(Boolean destroyed) { + public void setDestroyed(boolean destroyed) { this.destroyed = destroyed; } @@ -405,6 +417,11 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setStorageType(String storageType) { this.storageType = storageType; } @@ -517,7 +534,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co this.serviceOfferingDisplayText = serviceOfferingDisplayText; } - public void setExtractable(Boolean extractable) { + public void setExtractable(boolean extractable) { this.extractable = extractable; } @@ -535,7 +552,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co this.projectName = projectName; } - public void setDisplayVolume(Boolean displayVm) { + public void setDisplayVolume(boolean displayVm) { this.displayVolume = displayVm; } @@ -571,6 +588,14 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co return this.supportsStorageSnapshot; } + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } + public String getIsoId() { return isoId; } @@ -751,7 +776,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co return serviceOfferingDisplayText; } - public Boolean getExtractable() { + public boolean isExtractable() { return extractable; } @@ -759,7 +784,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co return status; } - public Boolean getDisplayVolume() { + public boolean isDisplayVolume() { return displayVolume; } @@ -842,4 +867,8 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co 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 ce00827f06d..b11764da7d9 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 @@ -70,9 +70,9 @@ public class VpcOfferingResponse extends BaseResponse { @Param(description = "true if vpc offering can be used by NSX networks only") private Boolean forNsx; - @SerializedName(ApiConstants.NSX_MODE) - @Param(description = "Mode in which the network will operate. This parameter is only relevant for NSX offerings") - private String nsxMode; + @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.") @@ -94,6 +94,14 @@ public class VpcOfferingResponse extends BaseResponse { @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; } @@ -150,8 +158,8 @@ public class VpcOfferingResponse extends BaseResponse { this.forNsx = forNsx; } - public void setNsxMode(String nsxMode) { - this.nsxMode = nsxMode; + public void setNetworkMode(String networkMode) { + this.networkMode = networkMode; } public String getZoneId() { @@ -177,4 +185,20 @@ public class VpcOfferingResponse extends BaseResponse { 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 610416d7b0e..56479506686 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; @@ -96,6 +97,10 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll @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) private List networks; @@ -156,6 +161,26 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll @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 class VpcResponse extends BaseResponseWithAnnotations implements Controll 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 class VpcResponse extends BaseResponseWithAnnotations implements Controll 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 class VpcResponse extends BaseResponseWithAnnotations implements Controll 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 d3e4d941678..3247f534133 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 @@ -48,6 +48,10 @@ public class VpnUsersResponse extends BaseResponse implements ControlledEntityRe @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") private String projectId; @@ -83,6 +87,11 @@ public class VpnUsersResponse extends BaseResponse implements ControlledEntityRe 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/ZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java index a898cd9d577..09e53dbb146 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java @@ -97,6 +97,14 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @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") private String allocationState; @@ -111,7 +119,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @SerializedName("capacity") @Param(description = "the capacity of the Zone", responseObject = CapacityResponse.class) - private List capacitites; + private List capacities; @SerializedName(ApiConstants.LOCAL_STORAGE_ENABLED) @Param(description = "true if local storage offering enabled, false otherwise") @@ -145,14 +153,40 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @Param(description = "the type of the zone - core or edge", since = "4.18.0") String type; + @Deprecated(since = "4.21.0") @SerializedName(ApiConstants.NSX_ENABLED) @Param(description = "true, if zone is NSX enabled", since = "4.20.0") private boolean nsxEnabled = false; + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "External network provider if any", since = "4.21.0") + private String provider = null; + + @SerializedName(ApiConstants.MULTI_ARCH) + @Param(description = "true, if zone contains clusters and hosts from different CPU architectures", since = "4.20") + private boolean multiArch; + + @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; } @@ -201,10 +235,18 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso 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; } @@ -217,15 +259,15 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso 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; } @@ -312,10 +354,6 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return networkType; } - public boolean isSecurityGroupsEnabled() { - return securityGroupsEnabled; - } - public String getAllocationState() { return allocationState; } @@ -328,12 +366,8 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return dhcpProvider; } - public List getCapacitites() { - return capacitites; - } - - public boolean isLocalStorageEnabled() { - return localStorageEnabled; + public List getCapacities() { + return capacities; } public Set getTags() { @@ -344,6 +378,46 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso 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; @@ -373,7 +447,35 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return type; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + public void setNsxEnabled(boolean nsxEnabled) { this.nsxEnabled = nsxEnabled; } + + 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 f369367957d..951af9180e7 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 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { private String id; private Date created; private String type; + private Long backupSize = 0L; + private Long dataSize = 0L; public RestorePoint(String id, Date created, String type) { this.id = id; @@ -69,6 +73,12 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { this.type = type; } + public RestorePoint(String id, Date created, String type, Long backupSize, Long dataSize) { + this(id, created, type); + this.backupSize = backupSize; + this.dataSize = dataSize; + } + public String getId() { return id; } @@ -92,6 +102,22 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { public void setType(String type) { this.type = type; } + + public Long getBackupSize() { + return backupSize; + } + + public void setBackupSize(Long backupSize) { + this.backupSize = backupSize; + } + + public Long getDataSize() { + return dataSize; + } + + public void setDataSize(Long dataSize) { + this.dataSize = dataSize; + } } class VolumeInfo { @@ -99,12 +125,20 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { private Volume.Type type; private Long size; private String path; + private Long deviceId; + private String diskOfferingId; + private Long minIops; + private Long maxIops; - public VolumeInfo(String uuid, String path, Volume.Type type, Long size) { + public VolumeInfo(String uuid, String path, Volume.Type type, Long size, Long deviceId, String diskOfferingId, Long minIops, Long maxIops) { this.uuid = uuid; this.type = type; this.size = size; this.path = path; + this.deviceId = deviceId; + this.diskOfferingId = diskOfferingId; + this.minIops = minIops; + this.maxIops = maxIops; } public String getUuid() { @@ -127,18 +161,42 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { return size; } + public Long getDeviceId() { + return deviceId; + } + + public String getDiskOfferingId() { + return diskOfferingId; + } + + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + @Override public String toString() { - return StringUtils.join(":", uuid, path, type, size); + return StringUtils.join(":", uuid, path, type, size, deviceId, diskOfferingId, minIops, maxIops); } } - long getVmId(); + Long getVmId(); + long getBackupOfferingId(); String getExternalId(); String getType(); 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 7b39804c738..c4b92fc9e05 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -18,18 +18,29 @@ package org.apache.cloudstack.backup; import java.util.List; +import java.util.Map; +import com.cloud.capacity.Capacity; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd; import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd; +import org.apache.cloudstack.api.command.user.backup.CreateBackupCmd; import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd; +import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; +import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.storage.Volume; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.component.PluggableService; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VmDiskInfo; /** * Backup and Recover Manager Interface @@ -56,6 +67,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 @@ -107,21 +174,22 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer * @param vmId * @return */ - BackupSchedule listBackupSchedule(Long vmId); + List listBackupSchedule(Long vmId); /** * 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 +201,15 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer */ boolean restoreBackup(final Long backupId); + Map getIpToNetworkMapFromBackup(Backup backup, boolean preserveIps, List networkIds); + + Boolean canCreateInstanceFromBackup(Long backupId); + + /** + * Restore a backup to a new Instance + */ + boolean restoreBackupToVM(Long backupId, Long vmId) throws ResourceUnavailableException; + /** * Restore a backed up volume and attach it to a VM */ @@ -146,5 +223,25 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer */ boolean deleteBackup(final Long backupId, final Boolean forced); + void validateBackupForZone(Long zoneId); + BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd); + + VmDiskInfo getRootDiskInfoFromBackup(Backup backup); + + List getDataDiskInfoListFromBackup(Backup backup); + + void checkVmDisksSizeAgainstBackup(List vmDiskInfoList, Backup backup); + + Map getBackupDetailsFromVM(VirtualMachine vm); + + String createVolumeInfoFromVolumes(List vmVolumes); + + String getBackupNameFromVM(VirtualMachine vm); + + BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails); + + Capacity getBackupStorageUsedStats(Long zoneId); + + void checkAndRemoveBackupOfferingBeforeExpunge(VirtualMachine vm); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java index 9c1b14ae60f..1eb36f89556 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.backup; import java.util.List; -import java.util.Map; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; @@ -49,22 +48,21 @@ public interface BackupProvider { /** * Assign a VM to a backup offering or policy - * @param vm - * @param backup - * @param policy - * @return + * @param vm the machine to back up + * @param backupOffering the SLA definition for the backup + * @return succeeded? */ boolean assignVMToBackupOffering(VirtualMachine vm, BackupOffering backupOffering); /** * Removes a VM from a backup offering or policy - * @param vm - * @return + * @param vm the machine to stop backing up + * @return succeeded? */ boolean removeVMFromBackupOffering(VirtualMachine vm); /** - * Whether the provide will delete backups on removal of VM from the offfering + * Whether the provider will delete backups on removal of VM from the offering * @return boolean result */ boolean willDeleteBackupsOnOfferingRemoval(); @@ -72,19 +70,23 @@ public interface BackupProvider { /** * Starts and creates an adhoc backup process * for a previously registered VM backup - * @param backup - * @return + * + * @param vm the machine to make a backup of + * @param quiesceVM instance will be quiesced for checkpointing for backup. Applicable only to NAS plugin. + * @return the result and {code}Backup{code} {code}Object{code} */ - 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); + boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid); + /** * Restore VM from backup */ @@ -93,20 +95,44 @@ 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); /** - * Returns backup metrics for a list of VMs in a zone - * @param zoneId - * @param vms - * @return + * Syncs backup metrics (backup size, protected size) from the plugin and stores it within the provider + * @param zoneId the zone for which to return metrics */ - Map getBackupMetrics(Long zoneId, List vms); + void syncBackupMetrics(Long zoneId); /** - * This method should reconcile and create backup entries for any backups created out-of-band - * @param vm - * @param metric + * Returns a list of Backup.RestorePoint + * @param vm the machine to get the restore points for */ - void syncBackups(VirtualMachine vm, Backup.Metric metric); + List listRestorePoints(VirtualMachine vm); + + /** + * Creates and returns an entry in the backups table by getting the information from restorePoint and vm. + * + * @param restorePoint the restore point to create a backup for + * @param vm The machine for which to create a backup + */ + Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm); + + /** + * Returns if the backup provider supports creating new instance from backup + */ + boolean supportsInstanceFromBackup(); + + /** + * Returns the backup storage usage (Used, Total) for a backup provider + * @param zoneId the zone for which to return metrics + * @return a pair of Used size and Total size for the backup storage + */ + Pair getBackupStorageStats(Long zoneId); + + /** + * Gets the backup storage usage (Used, Total) from the plugin and stores it in db + * @param zoneId the zone for which to return metrics + */ + void syncBackupStorageStats(Long zoneId); + } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java b/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java new file mode 100644 index 00000000000..be539a0eb04 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java @@ -0,0 +1,36 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. +package org.apache.cloudstack.backup; + +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 setUsedBytes(Long usedBytes); + Long getCapacityBytes(); + Long getUsedBytes(); + void setCapacityBytes(Long capacityBytes); + 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 00000000000..ae71053e400 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.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.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 java.util.List; + +public interface BackupRepositoryService { + BackupRepository addBackupRepository(AddBackupRepositoryCmd 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 d81dd731b1f..b5138d34de1 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java @@ -30,4 +30,7 @@ public interface BackupSchedule extends InternalIdentity { String getTimezone(); Date getScheduledTimestamp(); Long getAsyncJobId(); + Boolean getQuiesceVM(); + int getMaxBackups(); + String getUuid(); } 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 12a9d3d7b41..b0fb1ac73c2 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 15f7fcd8174..665f95842b0 100644 --- a/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java +++ b/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java @@ -21,6 +21,7 @@ package org.apache.cloudstack.cluster; import com.cloud.host.Host; import com.cloud.offering.ServiceOffering; +import com.cloud.org.Cluster; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.component.Adapter; @@ -55,8 +56,8 @@ 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; /** @@ -79,7 +80,7 @@ public interface ClusterDrsAlgorithm extends Adapter { * * @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; diff --git a/api/src/main/java/org/apache/cloudstack/command/ReconcileCommandService.java b/api/src/main/java/org/apache/cloudstack/command/ReconcileCommandService.java new file mode 100644 index 00000000000..89ab97990df --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/command/ReconcileCommandService.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.command; + + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.hypervisor.Hypervisor; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.Arrays; +import java.util.List; + +public interface ReconcileCommandService { + + ConfigKey ReconcileCommandsEnabled = new ConfigKey<>("Advanced", Boolean.class, + "reconcile.commands.enabled", "false", + "Indicates whether the background task to reconcile the commands is enabled or not", + false); + + ConfigKey ReconcileCommandsInterval = new ConfigKey<>("Advanced", Integer.class, + "reconcile.commands.interval", "60", + "Interval (in seconds) for the background task to reconcile the commands", + false); + ConfigKey ReconcileCommandsMaxAttempts = new ConfigKey<>("Advanced", Integer.class, + "reconcile.commands.max.attempts", "30", + "The maximum number of attempts to reconcile the commands", + true); + + ConfigKey ReconcileCommandsWorkers = new ConfigKey<>("Advanced", Integer.class, + "reconcile.commands.workers", "100", + "The Number of worker threads to reconcile the commands", + false); + + List SupportedHypervisorTypes = Arrays.asList(Hypervisor.HypervisorType.KVM); + + void persistReconcileCommands(Long hostId, Long requestSequence, Command[] cmd); + + boolean updateReconcileCommand(long requestSeq, Command command, Answer answer, Command.State newStateByManagement, Command.State newStateByAgent); + + void processCommand(Command pingCommand, Answer pingAnswer); + + void processAnswers(long requestSeq, Command[] commands, Answer[] answers); + + void updateReconcileCommandToInterruptedByManagementServerId(long managementServerId); + + void updateReconcileCommandToInterruptedByHostId(long hostId); + + boolean isReconcileResourceNeeded(long resourceId, ApiCommandResourceType resourceType); +} diff --git a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java index 5bd9699b201..23b571e7fae 100644 --- a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java +++ b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java @@ -44,7 +44,7 @@ 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); 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 00000000000..90d55cc5751 --- /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 00000000000..fc116cbb91e --- /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 00000000000..33ff70fcace --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.extension; + +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class CustomActionResultResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the action") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the action") + private String name; + + @SerializedName(ApiConstants.SUCCESS) + @Param(description = "Whether custom action succeed or not") + private Boolean success; + + @SerializedName(ApiConstants.RESULT) + @Param(description = "Result of the action execution") + private Map result; + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setSuccess(Boolean success) { + this.success = success; + } + + public Boolean getSuccess() { + return success; + } + + public void setResult(Map result) { + this.result = result; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/Extension.java b/api/src/main/java/org/apache/cloudstack/extension/Extension.java new file mode 100644 index 00000000000..3068612ed6f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/Extension.java @@ -0,0 +1,44 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.extension; + +import java.util.Date; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface Extension extends InternalIdentity, Identity { + enum Type { + Orchestrator + } + enum State { + Enabled, Disabled; + }; + String getName(); + String getDescription(); + Type getType(); + String getRelativePath(); + boolean isPathReady(); + boolean isUserDefined(); + State getState(); + Date getCreated(); + + static String getDirectoryName(String name) { + return name.replaceAll("[^a-zA-Z0-9._-]", "_").toLowerCase(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java new file mode 100644 index 00000000000..776b42f671b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java @@ -0,0 +1,386 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.extension; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.DateUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +public interface ExtensionCustomAction extends InternalIdentity, Identity { + enum ResourceType { + VirtualMachine(com.cloud.vm.VirtualMachine.class); + + private final Class clazz; + + ResourceType(Class clazz) { + this.clazz = clazz; + } + + public Class getAssociatedClass() { + return this.clazz; + } + } + + String getName(); + + String getDescription(); + + long getExtensionId(); + + ResourceType getResourceType(); + + Integer getAllowedRoleTypes(); + + String getSuccessMessage(); + + String getErrorMessage(); + + int getTimeout(); + + boolean isEnabled(); + + Date getCreated(); + + + class Parameter { + + public enum Type { + STRING(true), + NUMBER(true), + BOOLEAN(false), + DATE(false); + + private final boolean supportsOptions; + + Type(boolean supportsOptions) { + this.supportsOptions = supportsOptions; + } + + public boolean canSupportsOptions() { + return supportsOptions; + } + } + + public enum ValidationFormat { + // Universal default format + NONE(null), + + // String formats + UUID(Type.STRING), + EMAIL(Type.STRING), + PASSWORD(Type.STRING), + URL(Type.STRING), + + // Number formats + DECIMAL(Type.NUMBER); + + private final Type baseType; + + ValidationFormat(Type baseType) { + this.baseType = baseType; + } + + public Type getBaseType() { + return baseType; + } + } + + private static final Gson gson = new GsonBuilder() + .registerTypeAdapter(Parameter.class, new ParameterDeserializer()) + .setPrettyPrinting() + .create(); + + private final String name; + private final Type type; + private final ValidationFormat validationformat; + private final List valueoptions; + private final boolean required; + + public Parameter(String name, Type type, ValidationFormat validationformat, List valueoptions, boolean required) { + this.name = name; + this.type = type; + this.validationformat = validationformat; + this.valueoptions = valueoptions; + this.required = required; + } + + /** + * Parses a CSV string into a list of validated options. + */ + private static List parseValueOptions(String name, String csv, Type parsedType, ValidationFormat parsedFormat) { + if (StringUtils.isBlank(csv)) { + return null; + } + List values = Arrays.stream(csv.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + switch (parsedType) { + case STRING: + if (parsedFormat != null && parsedFormat != ValidationFormat.NONE) { + for (String value : values) { + if (!isValidStringValue(value, parsedFormat)) { + throw new InvalidParameterValueException(String.format("Invalid value options with validation format: %s for parameter: %s", parsedFormat.name(), name)); + } + } + } + return new ArrayList<>(values); + case NUMBER: + try { + return values.stream() + .map(v -> parseNumber(v, parsedFormat)) + .collect(Collectors.toList()); + } catch (NumberFormatException ignored) { + throw new InvalidParameterValueException(String.format("Invalid value options with validation format: %s for parameter: %s", parsedFormat.name(), name)); + } + default: + throw new InvalidParameterValueException(String.format("Options not supported for type: %s for parameter: %s", parsedType, name)); + } + } + + private static Object parseNumber(String value, ValidationFormat parsedFormat) { + if (parsedFormat == ValidationFormat.DECIMAL) { + return Float.parseFloat(value); + } + return Integer.parseInt(value); + } + + private static boolean isValidStringValue(String value, ValidationFormat validationFormat ) { + switch (validationFormat) { + case NONE: + return true; + case UUID: + try { + UUID.fromString(value); + return true; + } catch (Exception ignored) { + return false; + } + case EMAIL: + return value.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$"); + case PASSWORD: + return !value.trim().isEmpty(); + case URL: + try { + new java.net.URL(value); + return true; + } catch (Exception ignored) { + return false; + } + default: + return false; + } + } + + public static Parameter fromMap(Map map) throws InvalidParameterValueException { + final String name = map.get(ApiConstants.NAME); + final String typeStr = map.get(ApiConstants.TYPE); + final String validationFormatStr = map.get(ApiConstants.VALIDATION_FORMAT); + final String required = map.get(ApiConstants.REQUIRED); + final String valueOptionsStr = map.get(ApiConstants.VALUE_OPTIONS); + if (StringUtils.isBlank(name)) { + throw new InvalidParameterValueException("Invalid parameter specified with empty name"); + } + if (StringUtils.isBlank(typeStr)) { + throw new InvalidParameterValueException(String.format("No type specified for parameter: %s", name)); + } + Type parsedType = EnumUtils.getEnumIgnoreCase(Type.class, typeStr); + if (parsedType == null) { + throw new InvalidParameterValueException(String.format("Invalid type: %s specified for parameter: %s", + typeStr, name)); + } + ValidationFormat parsedFormat = StringUtils.isBlank(validationFormatStr) ? + ValidationFormat.NONE : EnumUtils.getEnumIgnoreCase(ValidationFormat.class, validationFormatStr); + if (parsedFormat == null || (!ValidationFormat.NONE.equals(parsedFormat) && + parsedFormat.getBaseType() != parsedType)) { + throw new InvalidParameterValueException( + String.format("Invalid validation format: %s specified for type: %s", + parsedFormat, parsedType.name())); + } + List valueOptions = parseValueOptions(name, valueOptionsStr, parsedType, parsedFormat); + return new Parameter(name, parsedType, parsedFormat, valueOptions, Boolean.parseBoolean(required)); + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public ValidationFormat getValidationFormat() { + return validationformat; + } + + public List getValueOptions() { + return valueoptions; + } + + public boolean isRequired() { + return required; + } + + @Override + public String toString() { + return String.format("Parameter %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, + ApiConstants.NAME, ApiConstants.TYPE, ApiConstants.REQUIRED)); + } + + public static String toJsonFromList(List parameters) { + return gson.toJson(parameters); + } + + public static List toListFromJson(String json) { + java.lang.reflect.Type listType = new TypeToken>() {}.getType(); + return gson.fromJson(json, listType); + } + + private void validateValueInOptions(Object value) { + if (CollectionUtils.isNotEmpty(valueoptions) && !valueoptions.contains(value)) { + throw new InvalidParameterValueException("Invalid value for parameter '" + name + "': " + value + + ". Valid options are: " + valueoptions.stream().map(Object::toString).collect(Collectors.joining(", "))); + } + } + + public Object validatedValue(String value) { + if (StringUtils.isBlank(value)) { + throw new InvalidParameterValueException("Empty value for parameter '" + name + "': " + value); + } + try { + switch (type) { + case BOOLEAN: + if (!Arrays.asList("true", "false").contains(value)) { + throw new IllegalArgumentException(); + } + return Boolean.parseBoolean(value); + case DATE: + return DateUtil.parseTZDateString(value); + case NUMBER: + Object obj = parseNumber(value, validationformat); + validateValueInOptions(obj); + return obj; + default: + if (!isValidStringValue(value, validationformat)) { + throw new IllegalArgumentException(); + } + validateValueInOptions(value); + return value; + } + } catch (Exception e) { + throw new InvalidParameterValueException("Invalid value for parameter '" + name + "': " + value); + } + } + + public static Map validateParameterValues(List parameterDefinitions, + Map suppliedValues) throws InvalidParameterValueException { + if (suppliedValues == null) { + suppliedValues = new HashMap<>(); + } + for (Parameter param : parameterDefinitions) { + String value = suppliedValues.get(param.getName()); + if (param.isRequired()) { + if (value == null || value.trim().isEmpty()) { + throw new InvalidParameterValueException("Missing or empty required parameter: " + param.getName()); + } + } + } + Map validatedParams = new HashMap<>(); + for (Map.Entry entry : suppliedValues.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + Parameter param = parameterDefinitions.stream() + .filter(p -> p.getName().equals(name)) + .findFirst() + .orElse(null); + if (param != null) { + validatedParams.put(name, param.validatedValue(value)); + } else { + validatedParams.put(name, value); + } + } + return validatedParams; + } + + static class ParameterDeserializer implements JsonDeserializer { + + @Override + public Parameter deserialize(JsonElement json, java.lang.reflect.Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject obj = json.getAsJsonObject(); + String name = obj.get(ApiConstants.NAME).getAsString(); + String typeStr = obj.get(ApiConstants.TYPE).getAsString(); + String validationFormatStr = obj.has(ApiConstants.VALIDATION_FORMAT) ? obj.get(ApiConstants.VALIDATION_FORMAT).getAsString() : null; + boolean required = obj.has(ApiConstants.REQUIRED) && obj.get(ApiConstants.REQUIRED).getAsBoolean(); + + Parameter.Type typeEnum = Parameter.Type.valueOf(typeStr); + Parameter.ValidationFormat validationFormatEnum = (validationFormatStr != null) + ? Parameter.ValidationFormat.valueOf(validationFormatStr) + : Parameter.ValidationFormat.NONE; + + List valueOptions = null; + if (obj.has(ApiConstants.VALUE_OPTIONS) && obj.get(ApiConstants.VALUE_OPTIONS).isJsonArray()) { + JsonArray valueOptionsArray = obj.getAsJsonArray(ApiConstants.VALUE_OPTIONS); + valueOptions = new ArrayList<>(); + for (JsonElement el : valueOptionsArray) { + switch (typeEnum) { + case STRING: + valueOptions.add(el.getAsString()); + break; + case NUMBER: + if (validationFormatEnum == Parameter.ValidationFormat.DECIMAL) { + valueOptions.add(el.getAsFloat()); + } else { + valueOptions.add(el.getAsInt()); + } + break; + default: + throw new JsonParseException("Unsupported type for value options"); + } + } + } + + return new Parameter(name, typeEnum, validationFormatEnum, valueOptions, required); + } + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java new file mode 100644 index 00000000000..f50f841ed74 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.extension; + +public interface ExtensionHelper { + Long getExtensionIdForCluster(long clusterId); + Extension getExtension(long id); + Extension getExtensionForCluster(long clusterId); +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java new file mode 100644 index 00000000000..40ebc19eb5e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java @@ -0,0 +1,34 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.extension; + +import java.util.Date; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface ExtensionResourceMap extends InternalIdentity, Identity { + enum ResourceType { + Cluster + } + + long getExtensionId(); + long getResourceId(); + ResourceType getResourceType(); + Date getCreated(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/GpuCard.java b/api/src/main/java/org/apache/cloudstack/gpu/GpuCard.java new file mode 100644 index 00000000000..2c02a0e30c2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/GpuCard.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gpu; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +/** + * GPU card interface representing a physical GPU card model + */ +public interface GpuCard extends InternalIdentity, Identity { + /** + * @return the UUID of the GPU card + */ + String getUuid(); + + /** + * @return the device ID of the GPU card + */ + String getDeviceId(); + + /** + * @return the device name of the GPU card + */ + String getDeviceName(); + + /** + * @return the name of the GPU card + */ + String getName(); + + /** + * @return the vendor name of the GPU card + */ + String getVendorName(); + + /** + * @return the vendor ID of the GPU card + */ + String getVendorId(); + + /** + * @return the date when the GPU card was created + */ + Date getCreated(); + + + /** + * @return the group name of the GPU card based on how the XenServer expects it. + */ + String getGroupName(); + +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/GpuDevice.java b/api/src/main/java/org/apache/cloudstack/gpu/GpuDevice.java new file mode 100644 index 00000000000..22adda46477 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/GpuDevice.java @@ -0,0 +1,42 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gpu; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +/** + * GPU device interface representing a physical GPU device + */ +public interface GpuDevice extends InternalIdentity, Identity { + + enum State { + Allocated, Free, Error, PartiallyAllocated, + } + + enum ManagedState { + Managed, Unmanaged, + } + + enum DeviceType { + PCI, MDEV, VGPUOnly, + } + + long getHostId(); + + State getState(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/GpuService.java b/api/src/main/java/org/apache/cloudstack/gpu/GpuService.java new file mode 100644 index 00000000000..1e7928a319f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/GpuService.java @@ -0,0 +1,157 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gpu; + +import com.cloud.agent.api.VgpuTypesInfo; +import com.cloud.agent.api.to.GPUDeviceTO; +import com.cloud.host.Host; +import com.cloud.utils.component.Manager; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.api.command.admin.gpu.CreateGpuCardCmd; +import org.apache.cloudstack.api.command.admin.gpu.CreateGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.CreateVgpuProfileCmd; +import org.apache.cloudstack.api.command.admin.gpu.DeleteGpuCardCmd; +import org.apache.cloudstack.api.command.admin.gpu.DeleteGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.DeleteVgpuProfileCmd; +import org.apache.cloudstack.api.command.admin.gpu.UnmanageGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.DiscoverGpuDevicesCmd; +import org.apache.cloudstack.api.command.admin.gpu.ManageGpuDeviceCmd; +import org.apache.cloudstack.api.command.user.gpu.ListGpuDevicesCmd; +import org.apache.cloudstack.api.command.admin.gpu.UpdateGpuCardCmd; +import org.apache.cloudstack.api.command.admin.gpu.UpdateGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.UpdateVgpuProfileCmd; +import org.apache.cloudstack.api.command.user.gpu.ListGpuCardsCmd; +import org.apache.cloudstack.api.command.user.gpu.ListVgpuProfilesCmd; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.HashMap; +import java.util.List; + +public interface GpuService extends Manager { + + ConfigKey GpuDetachOnStop = new ConfigKey<>(Boolean.class, "gpu.detach.on.stop", "Advanced", "false", + "Whether to detach GPU devices from VM on stop or keep them allocated", true, ConfigKey.Scope.Domain, null); + + GpuCard createGpuCard(CreateGpuCardCmd cmd); + + GpuCard updateGpuCard(UpdateGpuCardCmd cmd); + + boolean deleteGpuCard(DeleteGpuCardCmd cmd); + + VgpuProfileResponse createVgpuProfile(CreateVgpuProfileCmd cmd); + + VgpuProfileResponse updateVgpuProfile(UpdateVgpuProfileCmd cmd); + + boolean deleteVgpuProfile(DeleteVgpuProfileCmd cmd); + + ListResponse listGpuCards(ListGpuCardsCmd cmd); + + ListResponse listVgpuProfiles(ListVgpuProfilesCmd cmd); + + GpuDeviceResponse createGpuDevice(CreateGpuDeviceCmd cmd); + + GpuDeviceResponse updateGpuDevice(UpdateGpuDeviceCmd cmd); + + ListResponse listGpuDevices(ListGpuDevicesCmd cmd); + + boolean disableGpuDevice(UnmanageGpuDeviceCmd cmd); + + boolean enableGpuDevice(ManageGpuDeviceCmd cmd); + + /** + * Deallocate GPU devices for a VM on a host. + * + * @param vmId The ID of the VM to deallocate GPU devices for. + */ + void deallocateAllGpuDevicesForVm(long vmId); + + + /** + * Deallocate GPU devices for a VM on a host. + * + * @param vmId The ID of the VM to deallocate GPU devices for. + */ + void deallocateGpuDevicesForVmOnHost(long vmId, long hostId); + + /** + * Deallocate existing GPU devices for a VM on a host and allocate new GPU devices to the VM. + * + * @param vmId The ID of the VM to allocate GPU devices to. + * @param hostId The ID of the host to allocate GPU devices to. + * @param gpuDevices The list of GPU devices to allocate to the VM. + */ + void allocateGpuDevicesToVmOnHost(long vmId, long hostId, List gpuDevices); + + /** + * Discover GPU devices on a host by using the getGPUStatistics command and updating the GPU details for the host. + * + * @param cmd The command to discover GPU devices. + * @return The list of GPU devices. + */ + ListResponse discoverGpuDevices(DiscoverGpuDevicesCmd cmd); + + /** + * Check if GPU devices are available for a VM on a host by checking the number of available GPU devices for the + * vGPU profile. + * + * @param host The host to check GPU devices for. + * @param vmId The ID of the VM to check GPU devices for. + * @param vgpuProfile The vGPU profile to check GPU devices for. + * @param gpuCount The number of GPU devices to check for. + * @return True if GPU devices are available, false otherwise. + */ + boolean isGPUDeviceAvailable(Host host, Long vmId, VgpuProfile vgpuProfile, int gpuCount); + + /** + * Get GPU devices for a VM on a host by checking the number of available GPU devices for the vGPU profile. + * If the VM already has GPU devices assigned, deallocate them and allocate new GPU devices to the VM. + * The new GPU devices are allocated optimally to the VM. + * + * @param vm The VM to get GPU devices for. + * @param vgpuProfile The vGPU profile to get GPU devices for. + * @param gpuCount The number of GPU devices to get. + * @return The GPU devices. + */ + GPUDeviceTO getGPUDevice(VirtualMachine vm, long hostId, VgpuProfile vgpuProfile, int gpuCount); + + /** + * Gets the GPU group details from the GPU devices on a host. + * This fetches the GPU devices from the host and prepares the GPU group details for the host. + * The GPU group details are a map of GPU group name (Card's device name) to a map of vGPU profile name to + * VgpuTypesInfo. + * The VgpuTypesInfo contains the information about the GPU device. + * + * @param hostId The host ID to get GPU group details for. + * @return The GPU group details. + */ + HashMap> getGpuGroupDetailsFromGpuDevicesOnHost(long hostId); + + /** + * This method is used to add the GPU devices to the host when the host is discovered or when the GPU devices are + * updated. + * + * @param host The host to add the GPU devices to. + * @param newGpuDevicesInfo The list of GPU devices to add to the host. + */ + void addGpuDevicesToHost(Host host, List newGpuDevicesInfo); + + boolean deleteGpuDevices(DeleteGpuDeviceCmd deleteGpuDeviceCmd); +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/VgpuProfile.java b/api/src/main/java/org/apache/cloudstack/gpu/VgpuProfile.java new file mode 100644 index 00000000000..8cfac2a20de --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/VgpuProfile.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gpu; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +/** + * vGPU profile interface representing a virtualized GPU profile + */ +public interface VgpuProfile extends InternalIdentity, Identity { + /** + * @return the UUID of the vGPU profile + */ + String getUuid(); + + /** + * @return the name of the vGPU profile + */ + String getName(); + + /** + * @return the description of the vGPU profile + */ + String getDescription(); + + /** + * @return the date when the vGPU profile was created + */ + Date getCreated(); + + Long getCardId(); + + /** + * @return the maximum number of vGPUs per physical GPU + */ + Long getMaxVgpuPerPgpu(); + + /** + * @return the video RAM size in MB + */ + Long getVideoRam(); + + /** + * @return the maximum number of display heads + */ + Long getMaxHeads(); + + /** + * @return the maximum X resolution + */ + Long getMaxResolutionX(); + + /** + * @return the maximum Y resolution + */ + Long getMaxResolutionY(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java new file mode 100644 index 00000000000..0cca8bc2d7d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface GuiTheme extends InternalIdentity, Identity { + + String getName(); + + String getDescription(); + + String getCss(); + + String getJsonConfiguration(); + + Date getCreated(); + + Date getRemoved(); + + boolean getIsPublic(); + + void setId(Long id); + + void setUuid(String uuid); + + void setName(String name); + + void setDescription(String description); + + void setCss(String css); + + void setJsonConfiguration(String jsonConfiguration); + + void setCreated(Date created); + + void setRemoved(Date removed); + + void setIsPublic(boolean isPublic); + + boolean isRecursiveDomains(); + + void setRecursiveDomains(boolean recursiveDomains); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java new file mode 100644 index 00000000000..164535033d9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme; + +import org.apache.cloudstack.api.InternalIdentity; + +public interface GuiThemeDetails extends InternalIdentity { + + void setId(Long id); + + Long getGuiThemeId(); + + void setGuiThemeId(Long guiThemeId); + + String getType(); + + void setType(String type); + + String getValue(); + + void setValue(String value); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java new file mode 100644 index 00000000000..e54d53138ef --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java @@ -0,0 +1,47 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface GuiThemeJoin extends InternalIdentity, Identity { + + String getName(); + + String getDescription(); + + String getCss(); + + String getJsonConfiguration(); + + String getCommonNames(); + + String getDomains(); + + String getAccounts(); + + boolean isRecursiveDomains(); + + boolean getIsPublic(); + + Date getCreated(); + + Date getRemoved(); +} diff --git a/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 00000000000..8a066d3ffec --- /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 54a53f39578..7f81523dab7 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 @@ import org.apache.cloudstack.api.InternalIdentity; 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 00000000000..e1d7eca0a03 --- /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 00000000000..b0503314616 --- /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 00000000000..569ed22c164 --- /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 00000000000..221a550ad63 --- /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 isVpcVirtualRouterGateway(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/outofbandmanagement/OutOfBandManagementService.java b/api/src/main/java/org/apache/cloudstack/outofbandmanagement/OutOfBandManagementService.java index d670e4d3a88..4f6f1ad66c9 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 3299e7537a2..6a20c2fa248 100644 --- a/api/src/main/java/org/apache/cloudstack/query/QueryService.java +++ b/api/src/main/java/org/apache/cloudstack/query/QueryService.java @@ -16,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.router.ListRoutersCmd; import org.apache.cloudstack.api.command.admin.storage.ListImageStoresCmd; import org.apache.cloudstack.api.command.admin.storage.ListObjectStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.storage.ListSecondaryStagingStoresCmd; +import org.apache.cloudstack.api.command.admin.storage.ListStorageAccessGroupsCmd; import org.apache.cloudstack.api.command.admin.storage.ListStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.storage.ListStorageTagsCmd; import org.apache.cloudstack.api.command.admin.storage.heuristics.ListSecondaryStorageSelectorsCmd; @@ -52,6 +55,7 @@ import org.apache.cloudstack.api.command.user.snapshot.CopySnapshotCmd; 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.SecondaryStorageHeuristicsResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.StorageAccessGroupResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.StorageTagResponse; import org.apache.cloudstack.api.response.TemplateResponse; 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,15 +108,17 @@ import com.cloud.exception.PermissionDeniedException; */ public interface QueryService { + List RootAdminOnlyVmSettings = Arrays.asList(VmDetailConstants.GUEST_CPU_MODE, VmDetailConstants.GUEST_CPU_MODEL); + // Config keys ConfigKey AllowUserViewDestroyedVM = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.destroyed.vm", "false", "Determines whether users can view their destroyed or expunging vm ", true, ConfigKey.Scope.Account); - static final ConfigKey UserVMDeniedDetails = new ConfigKey<>(String.class, + ConfigKey UserVMDeniedDetails = new ConfigKey<>(String.class, "user.vm.denied.details", "Advanced", "rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag", "Determines whether users can view certain VM settings. When set to empty, default value used is: rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null); - static final ConfigKey UserVMReadOnlyDetails = new ConfigKey<>(String.class, + ConfigKey UserVMReadOnlyDetails = new ConfigKey<>(String.class, "user.vm.readonly.details", "Advanced", "dataDiskController, rootDiskController", "List of read-only VM settings/details as comma separated string", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null); @@ -118,14 +127,21 @@ public interface QueryService { "network offering, zones), we use the flag to determine if the entities should be sorted ascending (when flag is true) " + "or descending (when flag is false). Within the scope of the config all users see the same result.", true, ConfigKey.Scope.Global); - public static final ConfigKey AllowUserViewAllDomainAccounts = new ConfigKey<>("Advanced", Boolean.class, + ConfigKey AllowUserViewAllDomainAccounts = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.all.domain.accounts", "false", "Determines whether users can view all user accounts within the same domain", true, ConfigKey.Scope.Domain); - static final ConfigKey SharePublicTemplatesWithOtherDomains = new ConfigKey<>("Advanced", Boolean.class, "share.public.templates.with.other.domains", "true", + ConfigKey AllowUserViewAllDataCenters = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.all.zones", "true", + "Determines whether for instance a Resource Admin can view zones that are not dedicated to them.", true, ConfigKey.Scope.Domain); + + ConfigKey SharePublicTemplatesWithOtherDomains = new ConfigKey<>("Advanced", Boolean.class, "share.public.templates.with.other.domains", "true", "If false, templates of this domain will not show up in the list templates of other domains.", true, ConfigKey.Scope.Domain); - ListResponse searchForUsers(ListUsersCmd cmd) throws PermissionDeniedException; + ConfigKey ReturnVmStatsOnVmList = new ConfigKey<>("Advanced", Boolean.class, "list.vm.default.details.stats", "true", + "Determines whether VM stats should be returned when details are not explicitly specified in listVirtualMachines API request. When false, details default to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp]. When true, all details are returned including 'stats'.", true, ConfigKey.Scope.Global); + + + ListResponse searchForUsers(ResponseObject.ResponseView responseView, ListUsersCmd cmd) throws PermissionDeniedException; ListResponse searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException; @@ -137,6 +153,8 @@ public interface QueryService { ListResponse searchForUserVMs(ListVMsCmd cmd); + ListResponse listAffectedVmsForStorageScopeChange(ListAffectedVmsForStorageScopeChangeCmd cmd); + ListResponse searchForSecurityGroups(ListSecurityGroupsCmd cmd); ListResponse searchForRouters(ListRoutersCmd cmd); @@ -185,6 +203,8 @@ public interface QueryService { ListResponse searchForStorageTags(ListStorageTagsCmd cmd); + ListResponse searchForStorageAccessGroups(ListStorageAccessGroupsCmd cmd); + ListResponse searchForHostTags(ListHostTagsCmd cmd); ListResponse listManagementServers(ListMgmtsCmd cmd); diff --git a/api/src/main/java/org/apache/cloudstack/resource/ResourceCleanupService.java b/api/src/main/java/org/apache/cloudstack/resource/ResourceCleanupService.java new file mode 100644 index 00000000000..0d72edb0748 --- /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 cac5cc91b03..c281fa115fd 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 class DataStoreObjectResponse extends BaseResponse { 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 class DataStoreObjectResponse extends BaseResponse { 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 7e1361d1e71..e27ef308d7f 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.exception.ResourceAllocationException; 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 00000000000..bcba425abbf --- /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 00000000000..552dcf79f78 --- /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 00000000000..3966970f188 --- /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 00000000000..21184de27a2 --- /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/volume/VolumeImportUnmanageService.java b/api/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageService.java new file mode 100644 index 00000000000..5f69f3e46e7 --- /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 00000000000..1a8fd6ee273 --- /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 73962ba5875..00e8b431f8f 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 com.cloud.utils.Pair; 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 5e0f03ff583..5ad360a8026 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 fb4fe121cc7..d49120d4491 100644 --- a/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java +++ b/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java @@ -22,6 +22,8 @@ import org.apache.cloudstack.api.InternalIdentity; import com.cloud.configuration.Resource; +import java.util.Date; + /** * an interface defining an {code}AutoClosable{code} reservation object */ @@ -39,4 +41,6 @@ ResourceReservation extends InternalIdentity { 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 4dfcd0a7de1..b4bede24890 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,16 @@ 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; 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); } 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 23e0e371714..bba97dff71c 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 class UnmanagedInstanceTO { private String internalCSName; + private String path; + private PowerState powerState; private PowerState cloneSourcePowerState; @@ -57,6 +61,9 @@ public class UnmanagedInstanceTO { private String vncPassword; + private String bootType; + private String bootMode; + public String getName() { return name; } @@ -73,6 +80,14 @@ public class UnmanagedInstanceTO { this.internalCSName = internalCSName; } + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + public PowerState getPowerState() { return powerState; } @@ -177,6 +192,29 @@ public class UnmanagedInstanceTO { 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 class UnmanagedInstanceTO { 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 class UnmanagedInstanceTO { 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 53aece94964..b6233b9c270 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 00000000000..b6702695305 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/vm/lease/VMLeaseManager.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cloudstack.vm.lease; + +import com.cloud.utils.component.Manager; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.List; + +public interface VMLeaseManager extends Manager { + + int MAX_LEASE_DURATION_DAYS = 365_00; // 100 years + + enum ExpiryAction { + STOP, + DESTROY + } + + enum LeaseActionExecution { + PENDING, + DISABLED, + DONE, + CANCELLED + } + + ConfigKey InstanceLeaseEnabled = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Boolean.class, + "instance.lease.enabled", "false", "Indicates whether to enable the Instance lease," + + " will be applicable only on instances created after lease is enabled. Disabling the feature cancels lease on existing instances with lease." + + " Re-enabling feature will not cause lease expiry actions on grandfathered instances", + true, List.of(ConfigKey.Scope.Global)); + + ConfigKey InstanceLeaseSchedulerInterval = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "instance.lease.scheduler.interval", "3600", "VM Lease Scheduler interval in seconds", + false, List.of(ConfigKey.Scope.Global)); + + ConfigKey InstanceLeaseExpiryEventSchedulerInterval = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "instance.lease.eventscheduler.interval", "86400", "Lease expiry event Scheduler interval in seconds", + false, List.of(ConfigKey.Scope.Global)); + + ConfigKey InstanceLeaseExpiryEventDaysBefore = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "instance.lease.expiryevent.daysbefore", "7", "Indicates how many days in advance, expiry events will be created before expiry.", + true, List.of(ConfigKey.Scope.Global)); + + void onLeaseFeatureToggle(); +} diff --git a/api/src/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 5ea32464ee9..12d3c2361ac 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 8523daf1d21..b614e362950 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/to/LoadBalancerTOTest.java b/api/src/test/java/com/cloud/agent/api/to/LoadBalancerTOTest.java index b12c1b81d4a..e7ecbebae7b 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 class LoadBalancerTOTest { @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 00000000000..dfedf21864c --- /dev/null +++ b/api/src/test/java/com/cloud/cpu/CPUTest.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.cpu; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class CPUTest { + @Test + public void testCPUArchGetType() { + assertEquals("i686", CPU.CPUArch.x86.getType()); + assertEquals("x86_64", CPU.CPUArch.amd64.getType()); + assertEquals("aarch64", CPU.CPUArch.arm64.getType()); + } + + @Test + public void testCPUArchGetBits() { + assertEquals(32, CPU.CPUArch.x86.getBits()); + assertEquals(64, CPU.CPUArch.amd64.getBits()); + assertEquals(64, CPU.CPUArch.arm64.getBits()); + } + + @Test + public void testCPUArchFromTypeWithValidValues() { + assertEquals(CPU.CPUArch.x86, CPU.CPUArch.fromType("i686")); + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType("x86_64")); + assertEquals(CPU.CPUArch.arm64, CPU.CPUArch.fromType("aarch64")); + } + + @Test + public void testCPUArchFromTypeWithDefaultForBlankOrNull() { + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType("")); + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType(" ")); + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType(null)); + } + + @Test + public void testCPUArchFromTypeWithInvalidValue() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + CPU.CPUArch.fromType("unsupported"); + }); + assertTrue(exception.getMessage().contains("Unsupported arch type: unsupported")); + } + + @Test + public void testCPUArchGetTypesAsCSV() { + String expectedCSV = "i686,x86_64,aarch64"; + assertEquals(expectedCSV, CPU.CPUArch.getTypesAsCSV()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmdTest.java new file mode 100644 index 00000000000..603cda2040d --- /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 00000000000..2abcf736c5b --- /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 00000000000..7f49c61a693 --- /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 00000000000..1b80e5bff7f --- /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 00000000000..af53a539e67 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmdTest.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.cluster; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionHelper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.hypervisor.Hypervisor; + +@RunWith(MockitoJUnitRunner.class) +public class ListClustersCmdTest { + + ExtensionHelper extensionHelper; + ListClustersCmd listClustersCmd = new ListClustersCmd(); + + @Before + public void setUp() { + extensionHelper = mock(ExtensionHelper.class); + listClustersCmd.extensionHelper = extensionHelper; + } + + @Test + public void updateClustersExtensions_emptyList_noAction() { + listClustersCmd.updateClustersExtensions(Collections.emptyList()); + // No exception, nothing to verify + } + + @Test + public void updateClustersExtensions_nullList_noAction() { + listClustersCmd.updateClustersExtensions(null); + // No exception, nothing to verify + } + + @Test + public void updateClustersExtensions_withClusterResponses_setsExtension() { + ClusterResponse cluster1 = mock(ClusterResponse.class); + ClusterResponse cluster2 = mock(ClusterResponse.class); + when(cluster1.getInternalId()).thenReturn(1L); + when(cluster1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.External.name()); + when(cluster2.getInternalId()).thenReturn(2L); + when(cluster2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.External.name()); + Extension ext1 = mock(Extension.class); + when(ext1.getUuid()).thenReturn("a"); + Extension ext2 = mock(Extension.class); + when(ext2.getUuid()).thenReturn("b"); + when(extensionHelper.getExtensionIdForCluster(anyLong())).thenAnswer(invocation -> invocation.getArguments()[0]); + when(extensionHelper.getExtension(1L)).thenReturn(ext1); + when(extensionHelper.getExtension(2L)).thenReturn(ext2); + List clusters = Arrays.asList(cluster1, cluster2); + listClustersCmd.updateClustersExtensions(clusters); + verify(cluster1).setExtensionId("a"); + verify(cluster2).setExtensionId("b"); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmdTest.java index 3c9d4cb67ae..45f175e9a81 100644 --- 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 @@ -18,6 +18,7 @@ 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; @@ -71,7 +72,17 @@ public class ListDomainsCmdTest { cmd._resourceLimitService = resourceLimitService; ReflectionTestUtils.setField(cmd, "tag", "abc"); cmd.updateDomainResponse(List.of(Mockito.mock(DomainResponse.class))); - Mockito.verify(resourceLimitService, Mockito.times(1)).updateTaggedResourceLimitsAndCountsForDomains(Mockito.any(), Mockito.any()); + 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 00000000000..be21384109c --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmdTest.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CreateGpuCardCmdTest { + + @Test + public void getDeviceId() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getDeviceId()); + String deviceId = "0000:00:1f.6"; + ReflectionTestUtils.setField(cmd, "deviceId", deviceId); + assertEquals(deviceId, cmd.getDeviceId()); + } + + @Test + public void getDeviceName() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getDeviceName()); + String deviceName = "NVIDIA GeForce GTX 1080"; + ReflectionTestUtils.setField(cmd, "deviceName", deviceName); + assertEquals(deviceName, cmd.getDeviceName()); + } + + @Test + public void getName() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getName()); + String name = "Test GPU Card"; + ReflectionTestUtils.setField(cmd, "name", name); + assertEquals(name, cmd.getName()); + } + + @Test + public void getVendorName() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getVendorName()); + String vendorName = "NVIDIA"; + ReflectionTestUtils.setField(cmd, "vendorName", vendorName); + assertEquals(vendorName, cmd.getVendorName()); + } + + @Test + public void getVendorId() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getVendorId()); + String vendorId = "10de"; // NVIDIA vendor ID + ReflectionTestUtils.setField(cmd, "vendorId", vendorId); + assertEquals(vendorId, cmd.getVendorId()); + } + + @Test + public void getVideoRam() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getVideoRam()); + Long videoRam = 8192L; // 8 GB + ReflectionTestUtils.setField(cmd, "videoRam", videoRam); + assertEquals(videoRam, cmd.getVideoRam()); + } + + @Test + public void getEntityOwnerId() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmdTest.java new file mode 100644 index 00000000000..fd5c568d5bc --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmdTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.gpu.GpuDevice; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CreateGpuDeviceCmdTest { + + @Test + public void getHostId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getHostId()); + Long hostId = 1L; + ReflectionTestUtils.setField(cmd, "hostId", hostId); + assertEquals(hostId, cmd.getHostId()); + } + + @Test + public void getBusAddress() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getBusAddress()); + String busAddress = "0000:00:1f.6"; + ReflectionTestUtils.setField(cmd, "busAddress", busAddress); + assertEquals(busAddress, cmd.getBusAddress()); + } + + @Test + public void getGpuCardId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getGpuCardId()); + Long gpuCardId = 1L; + ReflectionTestUtils.setField(cmd, "gpuCardId", gpuCardId); + assertEquals(gpuCardId, cmd.getGpuCardId()); + } + + @Test + public void getVgpuProfileId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getVgpuProfileId()); + Long vgpuProfileId = 1L; + ReflectionTestUtils.setField(cmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cmd.getVgpuProfileId()); + } + + @Test + public void getType() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertEquals(GpuDevice.DeviceType.PCI, cmd.getType()); + String type = "MDEV"; + ReflectionTestUtils.setField(cmd, "type", type); + assertEquals(GpuDevice.DeviceType.MDEV, cmd.getType()); + } + + @Test + public void getParentGpuDeviceId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getParentGpuDeviceId()); + Long parentGpuDeviceId = 1L; + ReflectionTestUtils.setField(cmd, "parentGpuDeviceId", parentGpuDeviceId); + assertEquals(parentGpuDeviceId, cmd.getParentGpuDeviceId()); + } + + @Test + public void getNumaNode() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertEquals("-1", cmd.getNumaNode()); + String numaNode = "0"; + ReflectionTestUtils.setField(cmd, "numaNode", numaNode); + assertEquals(numaNode, cmd.getNumaNode()); + } + + @Test + public void getEntityOwnerId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmdTest.java new file mode 100644 index 00000000000..c71286bda65 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmdTest.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CreateVgpuProfileCmdTest { + + @Test + public void getName() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getName()); + String name = "Test VGPU Profile"; + ReflectionTestUtils.setField(cmd, "name", name); + assertEquals(name, cmd.getName()); + } + + @Test + public void getDescription() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getDescription()); + String description = "Test VGPU Profile Description"; + ReflectionTestUtils.setField(cmd, "description", description); + assertEquals(description, cmd.getDescription()); + } + + @Test + public void getCardId() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getCardId()); + Long cardId = 1L; + ReflectionTestUtils.setField(cmd, "cardId", cardId); + assertEquals(cardId, cmd.getCardId()); + } + + @Test + public void getMaxVgpuPerPgpu() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxVgpuPerPgpu()); + Long maxVgpuPerPgpu = 8L; + ReflectionTestUtils.setField(cmd, "maxVgpuPerPgpu", maxVgpuPerPgpu); + assertEquals(maxVgpuPerPgpu, cmd.getMaxVgpuPerPgpu()); + } + + @Test + public void getVideoRam() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getVideoRam()); + Long videoRam = 8192L; // 8 GB + ReflectionTestUtils.setField(cmd, "videoRam", videoRam); + assertEquals(videoRam, cmd.getVideoRam()); + } + + @Test + public void getMaxHeads() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxHeads()); + Long maxHeads = 2L; + ReflectionTestUtils.setField(cmd, "maxHeads", maxHeads); + assertEquals(maxHeads, cmd.getMaxHeads()); + } + + @Test + public void getMaxResolutionX() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionX()); + Long maxResolutionX = 1920L; // 1920 pixels + ReflectionTestUtils.setField(cmd, "maxResolutionX", maxResolutionX); + assertEquals(maxResolutionX, cmd.getMaxResolutionX()); + } + + @Test + public void getMaxResolutionY() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionY()); + Long maxResolutionY = 1080L; // 1080 pixels + ReflectionTestUtils.setField(cmd, "maxResolutionY", maxResolutionY); + assertEquals(maxResolutionY, cmd.getMaxResolutionY()); + } + + @Test + public void getEntityOwnerId() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmdTest.java new file mode 100644 index 00000000000..21df915b420 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmdTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteGpuCardCmdTest { + + @Test + public void getId() { + DeleteGpuCardCmd cmd = new DeleteGpuCardCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getEntityOwnerId() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmdTest.java new file mode 100644 index 00000000000..02b04dd307a --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmdTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteGpuDeviceCmdTest { + + @Test + public void getIds() { + DeleteGpuDeviceCmd cmd = new DeleteGpuDeviceCmd(); + assertNull(cmd.getIds()); + List ids = List.of(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "ids", ids); + assertEquals(ids, cmd.getIds()); + } + + @Test + public void getEntityOwnerId() { + DeleteGpuDeviceCmd cmd = new DeleteGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmdTest.java new file mode 100644 index 00000000000..ecd43810e65 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmdTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteVgpuProfileCmdTest { + + @Test + public void getId() { + DeleteVgpuProfileCmd cmd = new DeleteVgpuProfileCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getEntityOwnerId() { + DeleteVgpuProfileCmd cmd = new DeleteVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmdTest.java new file mode 100644 index 00000000000..8295e06e0d5 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmdTest.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DiscoverGpuDevicesCmdTest { + + @Test + public void getId() { + DiscoverGpuDevicesCmd cmd = new DiscoverGpuDevicesCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdminTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdminTest.java new file mode 100644 index 00000000000..200bce76933 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdminTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class ListGpuDevicesCmdByAdminTest { + + @Test + public void getId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getHostId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getHostId()); + Long hostId = 1L; + ReflectionTestUtils.setField(cmd, "hostId", hostId); + assertEquals(hostId, cmd.getHostId()); + } + + @Test + public void getGpuCardId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getGpuCardId()); + Long gpuCardId = 1L; + ReflectionTestUtils.setField(cmd, "gpuCardId", gpuCardId); + assertEquals(gpuCardId, cmd.getGpuCardId()); + } + + @Test + public void getVgpuProfileId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getVgpuProfileId()); + Long vgpuProfileId = 1L; + ReflectionTestUtils.setField(cmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cmd.getVgpuProfileId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmdTest.java new file mode 100644 index 00000000000..ee862409a93 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmdTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class ManageGpuDeviceCmdTest { + + @Test + public void getIds() { + ManageGpuDeviceCmd cmd = new ManageGpuDeviceCmd(); + assertNull(cmd.getIds()); + List ids = List.of(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "ids", ids); + assertEquals(ids, cmd.getIds()); + } + + @Test + public void getEntityOwnerId() { + ManageGpuDeviceCmd cmd = new ManageGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmdTest.java new file mode 100644 index 00000000000..63700d9e908 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmdTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UnmanageGpuDeviceCmdTest { + + @Test + public void getIds() { + UnmanageGpuDeviceCmd cmd = new UnmanageGpuDeviceCmd(); + assertNull(cmd.getIds()); + List ids = List.of(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "ids", ids); + assertEquals(ids, cmd.getIds()); + } + + @Test + public void getEntityOwnerId() { + UnmanageGpuDeviceCmd cmd = new UnmanageGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmdTest.java new file mode 100644 index 00000000000..ead7ab9d3d3 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmdTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UpdateGpuCardCmdTest { + + @Test + public void getId() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getDeviceName() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getDeviceName()); + String deviceName = "GPU-1234"; + ReflectionTestUtils.setField(cmd, "deviceName", deviceName); + assertEquals(deviceName, cmd.getDeviceName()); + } + + @Test + public void getName() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getName()); + String name = "Test GPU Card"; + ReflectionTestUtils.setField(cmd, "name", name); + assertEquals(name, cmd.getName()); + } + + @Test + public void getVendorName() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getVendorName()); + String vendorName = "NVIDIA"; + ReflectionTestUtils.setField(cmd, "vendorName", vendorName); + assertEquals(vendorName, cmd.getVendorName()); + } + + @Test + public void getEntityOwnerId() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmdTest.java new file mode 100644 index 00000000000..6ebec48aa00 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmdTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.gpu.GpuDevice; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UpdateGpuDeviceCmdTest { + + @Test + public void getId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getGpuCardId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getGpuCardId()); + Long gpuCardId = 1L; + ReflectionTestUtils.setField(cmd, "gpuCardId", gpuCardId); + assertEquals(gpuCardId, cmd.getGpuCardId()); + } + + @Test + public void getVgpuProfileId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getVgpuProfileId()); + Long vgpuProfileId = 1L; + ReflectionTestUtils.setField(cmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cmd.getVgpuProfileId()); + } + + @Test + public void getType() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getType()); + String type = "MDEV"; + ReflectionTestUtils.setField(cmd, "type", type); + assertEquals(GpuDevice.DeviceType.MDEV, cmd.getType()); + } + + @Test + public void getParentGpuDeviceId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getParentGpuDeviceId()); + Long parentGpuDeviceId = 1L; + ReflectionTestUtils.setField(cmd, "parentGpuDeviceId", parentGpuDeviceId); + assertEquals(parentGpuDeviceId, cmd.getParentGpuDeviceId()); + } + + @Test + public void getNumaNode() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getNumaNode()); + String numaNode = "0"; + ReflectionTestUtils.setField(cmd, "numaNode", numaNode); + assertEquals(numaNode, cmd.getNumaNode()); + } + + @Test + public void getEntityOwnerId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmdTest.java new file mode 100644 index 00000000000..95acd71096e --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmdTest.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UpdateVgpuProfileCmdTest { + + @Test + public void getId() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getProfileName() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getProfileName()); + String profileName = "Test VGPU Profile"; + ReflectionTestUtils.setField(cmd, "profileName", profileName); + assertEquals(profileName, cmd.getProfileName()); + } + + @Test + public void getDescription() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getDescription()); + String description = "Test VGPU Profile Description"; + ReflectionTestUtils.setField(cmd, "description", description); + assertEquals(description, cmd.getDescription()); + } + + @Test + public void getMaxVgpuPerPgpu() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxVgpuPerPgpu()); + Long maxVgpuPerPgpu = 8L; + ReflectionTestUtils.setField(cmd, "maxVgpuPerPgpu", maxVgpuPerPgpu); + assertEquals(maxVgpuPerPgpu, cmd.getMaxVgpuPerPgpu()); + } + + @Test + public void getVideoRam() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getVideoRam()); + Long videoRam = 8192L; // 8 GB + ReflectionTestUtils.setField(cmd, "videoRam", videoRam); + assertEquals(videoRam, cmd.getVideoRam()); + } + + @Test + public void getMaxHeads() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxHeads()); + Long maxHeads = 2L; + ReflectionTestUtils.setField(cmd, "maxHeads", maxHeads); + assertEquals(maxHeads, cmd.getMaxHeads()); + } + + @Test + public void getMaxResolutionX() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionX()); + Long maxResolutionX = 1920L; // Example resolution + ReflectionTestUtils.setField(cmd, "maxResolutionX", maxResolutionX); + assertEquals(maxResolutionX, cmd.getMaxResolutionX()); + } + + @Test + public void getMaxResolutionY() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionY()); + Long maxResolutionY = 1080L; // Example resolution + ReflectionTestUtils.setField(cmd, "maxResolutionY", maxResolutionY); + assertEquals(maxResolutionY, cmd.getMaxResolutionY()); + } + + @Test + public void getEntityOwnerId() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmdTest.java new file mode 100644 index 00000000000..32230056b6a --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmdTest.java @@ -0,0 +1,46 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class AddGuestOsCategoryCmdTest { + + @Test + public void testGetArch() { + AddGuestOsCategoryCmd cmd = new AddGuestOsCategoryCmd(); + Assert.assertNull(cmd.getName()); + String name = "Name"; + ReflectionTestUtils.setField(cmd, "name", name); + Assert.assertEquals(name, cmd.getName()); + } + + @Test + public void testIsFeatured() { + AddGuestOsCategoryCmd cmd = new AddGuestOsCategoryCmd(); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", false); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", true); + Assert.assertTrue(cmd.isFeatured()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmdTest.java new file mode 100644 index 00000000000..3f6a943b3fe --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmdTest.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateGuestOsCategoryCmdTest { + + @Test + public void testGetArch() { + UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd(); + Assert.assertNull(cmd.getName()); + String name = "Name"; + ReflectionTestUtils.setField(cmd, "name", name); + Assert.assertEquals(name, cmd.getName()); + } + + @Test + public void testIsFeatured() { + UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd(); + Assert.assertNull(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", false); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", true); + Assert.assertTrue(cmd.isFeatured()); + } + + @Test + public void testGetSortKey() { + UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd(); + Assert.assertNull(cmd.getSortKey()); + Integer sortKey = 100; + ReflectionTestUtils.setField(cmd, "sortKey", sortKey); + Assert.assertEquals(sortKey, cmd.getSortKey()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmdTest.java new file mode 100644 index 00000000000..f03a791776e --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmdTest.java @@ -0,0 +1,37 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateGuestOsCmdTest { + + + @Test + public void testGetZoneId() { + UpdateGuestOsCmd cmd = new UpdateGuestOsCmd(); + Assert.assertNull(cmd.getOsCategoryId()); + Long osCategoryId = 100L; + ReflectionTestUtils.setField(cmd, "osCategoryId", osCategoryId); + Assert.assertEquals(osCategoryId, cmd.getOsCategoryId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java new file mode 100644 index 00000000000..e1393e31699 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.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.network; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class CreateIpv4SubnetForGuestNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testCreateIpv4SubnetForGuestNetworkCmd() { + Long parentId = 1L; + 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); + + Assert.assertEquals(parentId, cmd.getParentId()); + Assert.assertEquals(subnet, cmd.getSubnet()); + Assert.assertEquals(cidrSize, cmd.getCidrSize()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_IP4_GUEST_SUBNET_CREATE, cmd.getEventType()); + Assert.assertEquals(String.format("Creating guest IPv4 subnet %s in zone subnet=%s", subnet, parentId), cmd.getEventDescription()); + + 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 00000000000..51c1eb986c4 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmdTest.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.DataCenterIpv4SubnetResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class CreateIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testCreateIpv4SubnetForZoneCmd() { + Long zoneId = 1L; + 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); + + 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, zoneId), 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 00000000000..7db77098b23 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmdTest.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.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; + +@RunWith(MockitoJUnitRunner.class) +public class DedicateIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDedicateIpv4SubnetForZoneCmd() { + Long id = 1L; + 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); + + 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 IPv4 subnet %s", id), 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 00000000000..a4af5ddf748 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmdTest.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.network; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.SuccessResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteIpv4SubnetForGuestNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDeleteIpv4SubnetForGuestNetworkCmd() { + Long id = 1L; + + DeleteIpv4SubnetForGuestNetworkCmd cmd = new DeleteIpv4SubnetForGuestNetworkCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_IP4_GUEST_SUBNET_DELETE, cmd.getEventType()); + Assert.assertEquals(String.format("Deleting guest IPv4 subnet %s", id), cmd.getEventDescription()); + + 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 00000000000..7af173f09d9 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmdTest.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.network; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.SuccessResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDeleteIpv4SubnetForZoneCmd() { + Long id = 1L; + + DeleteIpv4SubnetForZoneCmd cmd = new DeleteIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_DELETE, cmd.getEventType()); + Assert.assertEquals(String.format("Deleting zone IPv4 subnet %s", id), cmd.getEventDescription()); + + 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 00000000000..cbfe34f774a --- /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 00000000000..2c7a246f70f --- /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 00000000000..9ce9a4f9464 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmdTest.java @@ -0,0 +1,62 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class ReleaseDedicatedIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testReleaseDedicatedIpv4SubnetForZoneCmd() { + Long id = 1L; + + ReleaseDedicatedIpv4SubnetForZoneCmd cmd = new ReleaseDedicatedIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_RELEASE, cmd.getEventType()); + Assert.assertEquals(String.format("Releasing a dedicated zone IPv4 subnet %s", id), cmd.getEventDescription()); + + 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 00000000000..cdb9cce22d8 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmdTest.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.command.admin.network; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testUpdateIpv4SubnetForZoneCmd() { + Long id = 1L; + 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); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(subnet, cmd.getSubnet()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_UPDATE, cmd.getEventType()); + Assert.assertEquals(String.format("Updating zone IPv4 subnet %s", id), cmd.getEventDescription()); + + 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 00000000000..28ddad17afe --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmdTest.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.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.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 ChangeBgpPeersForNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testChangeBgpPeersForNetworkCmd() { + Long networkId = 10L; + 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); + + Assert.assertEquals(networkId, cmd.getNetworkId()); + Assert.assertEquals(bgpPeerIds, cmd.getBgpPeerIds()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_NETWORK_BGP_PEER_UPDATE, cmd.getEventType()); + Assert.assertEquals(String.format("Changing Bgp Peers for network %s", networkId), cmd.getEventDescription()); + + 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 00000000000..96eb1f020de --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmdTest.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.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.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 ChangeBgpPeersForVpcCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testChangeBgpPeersForVpcCmd() { + Long VpcId = 10L; + 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); + + Assert.assertEquals(VpcId, cmd.getVpcId()); + Assert.assertEquals(bgpPeerIds, cmd.getBgpPeerIds()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_VPC_BGP_PEER_UPDATE, cmd.getEventType()); + Assert.assertEquals(String.format("Changing Bgp Peers for VPC %s", VpcId), cmd.getEventDescription()); + + 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 00000000000..0d802bf3619 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmdTest.java @@ -0,0 +1,85 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network.bgp; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.BgpPeerResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class CreateBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testCreateBgpPeerCmd() { + Long zoneId = 1L; + 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); + + 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=%s", peerAsNumber, zoneId), 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 00000000000..f3ae007da28 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmdTest.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.bgp; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.response.BgpPeerResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class DedicateBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDedicateBgpPeerCmd() { + Long id = 1L; + 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); + + 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 %s", id), 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 00000000000..5e747188fda --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmdTest.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.network.bgp; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.SuccessResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDeleteBgpPeerCmd() { + Long id = 1L; + + DeleteBgpPeerCmd cmd = new DeleteBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_BGP_PEER_DELETE, cmd.getEventType()); + Assert.assertEquals(String.format("Deleting Bgp Peer %s", id), cmd.getEventDescription()); + + 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 00000000000..cb2027951ad --- /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 00000000000..8c55c4a7347 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmdTest.java @@ -0,0 +1,62 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network.bgp; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.response.BgpPeerResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class ReleaseDedicatedBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testReleaseDedicatedBgpPeerCmd() { + Long id = 1L; + + ReleaseDedicatedBgpPeerCmd cmd = new ReleaseDedicatedBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_BGP_PEER_RELEASE, cmd.getEventType()); + Assert.assertEquals(String.format("Releasing a dedicated Bgp Peer %s", id), cmd.getEventDescription()); + + 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 00000000000..003944c6147 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmdTest.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.admin.network.bgp; + +import com.cloud.event.EventTypes; + +import org.apache.cloudstack.api.response.BgpPeerResponse; +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; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testUpdateBgpPeerCmd() { + Long id = 1L; + 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); + + 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 %s", id), 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/CreateServiceOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java index f69e8cea4f3..bc7f65b0756 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.api.command.admin.offering; +import com.cloud.exception.InvalidParameterValueException; +import org.apache.cloudstack.vm.lease.VMLeaseManager; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,4 +39,62 @@ public class CreateServiceOfferingCmdTest { 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 00000000000..1bb2be041e1 --- /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/PurgeExpungedResourcesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmdTest.java new file mode 100644 index 00000000000..c8ca1c2afe3 --- /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 a69a7a858ce..c7aeb8ba99b 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java @@ -61,6 +61,8 @@ public class AddObjectStoragePoolCmdTest { String provider = "Simulator"; + Long size = 10L; + Map details; private AutoCloseable closeable; @@ -74,6 +76,7 @@ public class AddObjectStoragePoolCmdTest { ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "url", url); ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "providerName", provider); ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "details", details); + ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "size", size); addObjectStoragePoolCmdSpy._storageService = storageService; addObjectStoragePoolCmdSpy._responseGenerator = responseGenerator; } @@ -87,12 +90,12 @@ public class AddObjectStoragePoolCmdTest { @Test public void testAddObjectStore() throws DiscoveryException { Mockito.doReturn(objectStore).when(storageService).discoverObjectStore(Mockito.anyString(), - Mockito.anyString(), Mockito.anyString(), any()); + Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), any()); ObjectStoreResponse objectStoreResponse = new ObjectStoreResponse(); Mockito.doReturn(objectStoreResponse).when(responseGenerator).createObjectStoreResponse(any()); addObjectStoragePoolCmdSpy.execute(); Mockito.verify(storageService, Mockito.times(1)) - .discoverObjectStore(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); + .discoverObjectStore(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java index 98435bf6e38..ad95ce10bd6 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java @@ -20,14 +20,11 @@ package org.apache.cloudstack.api.command.admin.storage; import com.cloud.utils.Pair; import org.apache.cloudstack.api.response.ExtractResponse; import org.apache.cloudstack.storage.browser.StorageBrowser; -import org.junit.After; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; @@ -48,18 +45,6 @@ public class DownloadImageStoreObjectCmdTest { @Spy private DownloadImageStoreObjectCmd cmd; - private AutoCloseable closeable; - - @Before - public void setUp() { - closeable = MockitoAnnotations.openMocks(this); - } - - @After - public void tearDown() throws Exception { - closeable.close(); - } - @Test public void testExecute() throws Exception { ReflectionTestUtils.setField(cmd, "storeId", 1L); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java index 61a3c8fb9e6..4fec90217fd 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java @@ -68,6 +68,12 @@ public class MigrateVirtualMachineWithVolumeCmdTest { @Mock Host hostMock; + @Mock + private Object job; + + @Mock + private Object _responseObject; + @Spy @InjectMocks MigrateVirtualMachineWithVolumeCmd cmdSpy; diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmdTest.java new file mode 100644 index 00000000000..a7c41b9271b --- /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.finalyzeAccountId(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 00000000000..959940d2a91 --- /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 00000000000..ba7e351a8a8 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmdTest.java @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.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.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 UnmanageVolumeCmdTest { + + VolumeImportUnmanageService volumeImportService = Mockito.spy(VolumeImportUnmanageService.class); + ResponseGenerator responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testUnmanageVolumeCmd() { + long accountId = 2L; + Long volumeId = 3L; + 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); + + Assert.assertEquals(volumeId, cmd.getVolumeId()); + Assert.assertEquals(accountId, cmd.getEntityOwnerId()); + Assert.assertEquals(volumeId, cmd.getApiResourceId()); + Assert.assertEquals(ApiCommandResourceType.Volume, cmd.getApiResourceType()); + Assert.assertEquals(EventTypes.EVENT_VOLUME_UNMANAGE, cmd.getEventType()); + Assert.assertEquals("Unmanaging Volume with ID " + volumeId, cmd.getEventDescription()); + + 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 00000000000..aaa65e10ff4 --- /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/test/CreateRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java index 4b9d4fd8974..72ce9593364 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 class CreateRoleCmdTest { 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 class CreateRoleCmdTest { 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/ImportRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java index 6299c1ed8e2..d2597e5162f 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 org.springframework.test.util.ReflectionTestUtils; 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 class ImportRoleCmdTest { 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/UpdateRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java index 84b91525742..9a1dae9a480 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 class UpdateRoleCmdTest extends TestCase{ 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/user/account/ListAccountsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmdTest.java index 896a7a6c826..a1ba9270345 100644 --- 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 @@ -18,6 +18,7 @@ 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; @@ -58,7 +59,7 @@ public class ListAccountsCmdTest { } @Test - public void testUpdateDomainResponseNoDomains() { + public void testUpdateAccountResponseNoAccounts() { ListAccountsCmd cmd = new ListAccountsCmd(); cmd._resourceLimitService = resourceLimitService; cmd.updateAccountResponse(null); @@ -66,11 +67,21 @@ public class ListAccountsCmdTest { } @Test - public void testUpdateDomainResponseWithDomains() { + 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/bgp/ListASNumbersCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmdTest.java new file mode 100644 index 00000000000..9d7f4ef7cf1 --- /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/firewall/CreateFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmdTest.java new file mode 100644 index 00000000000..c905974b2be --- /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 00000000000..54e726eadbe --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmdTest.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.user.gpu; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + + +public class ListGpuCardsCmdTest { + + @Test + public void getId() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + Assert.assertEquals(id, cmd.getId()); + } + + @Test + public void getVendorName() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getVendorName()); + String vendorName = "vendor name"; + ReflectionTestUtils.setField(cmd, "vendorName", vendorName); + Assert.assertEquals(vendorName, cmd.getVendorName()); + } + + @Test + public void getVendorId() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getVendorId()); + String vendorId = "vendor id"; + ReflectionTestUtils.setField(cmd, "vendorId", vendorId); + Assert.assertEquals(vendorId, cmd.getVendorId()); + } + + @Test + public void getDeviceId() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getDeviceId()); + String deviceId = "device id"; + ReflectionTestUtils.setField(cmd, "deviceId", deviceId); + Assert.assertEquals(deviceId, cmd.getDeviceId()); + } + + @Test + public void getDeviceName() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getDeviceName()); + String deviceName = "device name"; + ReflectionTestUtils.setField(cmd, "deviceName", deviceName); + Assert.assertEquals(deviceName, cmd.getDeviceName()); + } + + @Test + public void getActiveOnly() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertFalse(cmd.getActiveOnly()); + Boolean activeOnly = true; + ReflectionTestUtils.setField(cmd, "activeOnly", activeOnly); + Assert.assertEquals(activeOnly, cmd.getActiveOnly()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmdTest.java new file mode 100644 index 00000000000..e1a65ee0ece --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmdTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.user.gpu; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +public class ListGpuDevicesCmdTest { + + @Test + public void getVmId() { + ListGpuDevicesCmd cmd = new ListGpuDevicesCmd(); + Assert.assertNull(cmd.getVmId()); + Long vmId = 1L; + ReflectionTestUtils.setField(cmd, "vmId", vmId); + Assert.assertEquals(vmId, cmd.getVmId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmdTest.java new file mode 100644 index 00000000000..7616abd1f8d --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmdTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.user.gpu; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +public class ListVgpuProfilesCmdTest { + + @Test + public void getId() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + Assert.assertEquals(id, cmd.getId()); + } + + @Test + public void getName() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertNull(cmd.getName()); + String name = "Test VGPU Profile"; + ReflectionTestUtils.setField(cmd, "name", name); + Assert.assertEquals(name, cmd.getName()); + } + + @Test + public void getCardId() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertNull(cmd.getCardId()); + Long cardId = 1L; + ReflectionTestUtils.setField(cmd, "cardId", cardId); + Assert.assertEquals(cardId, cmd.getCardId()); + } + + @Test + public void getActiveOnly() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertFalse(cmd.getActiveOnly()); + Boolean activeOnly = true; + ReflectionTestUtils.setField(cmd, "activeOnly", activeOnly); + Assert.assertEquals(activeOnly, cmd.getActiveOnly()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmdTest.java new file mode 100644 index 00000000000..f417dc5f876 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmdTest.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.cpu.CPU; + +@RunWith(MockitoJUnitRunner.class) +public class ListGuestOsCategoriesCmdTest { + + @Test + public void testIsFeatured() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", false); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", true); + Assert.assertTrue(cmd.isFeatured()); + } + + @Test + public void testIsIso() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.isIso()); + ReflectionTestUtils.setField(cmd, "iso", false); + Assert.assertFalse(cmd.isIso()); + ReflectionTestUtils.setField(cmd, "iso", true); + Assert.assertTrue(cmd.isIso()); + } + + @Test + public void testIsVnf() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.isVnf()); + ReflectionTestUtils.setField(cmd, "vnf", false); + Assert.assertFalse(cmd.isVnf()); + ReflectionTestUtils.setField(cmd, "vnf", true); + Assert.assertTrue(cmd.isVnf()); + } + + @Test + public void testGetZoneId() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.getZoneId()); + Long zoneId = 100L; + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + Assert.assertEquals(zoneId, cmd.getZoneId()); + } + + @Test + public void testGetArch() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.getArch()); + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + ReflectionTestUtils.setField(cmd, "arch", arch.getType()); + Assert.assertEquals(arch, cmd.getArch()); + } + + @Test + public void testIsShowIcon() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertFalse(cmd.isShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", false); + Assert.assertFalse(cmd.isShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", true); + Assert.assertTrue(cmd.isShowIcon()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java index 415ee01ba16..c42a7882c54 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java @@ -41,7 +41,10 @@ public class UpdateNetworkCmdTest { NetworkService networkService; @Mock public EntityManager _entityMgr; - private ResponseGenerator responseGenerator; + @Mock + private ResponseGenerator _responseGenerator; + @Mock + private Object job; @InjectMocks UpdateNetworkCmd cmd = new UpdateNetworkCmd(); @@ -176,15 +179,13 @@ public class UpdateNetworkCmdTest { ReflectionTestUtils.setField(cmd, "id", networkId); ReflectionTestUtils.setField(cmd, "publicMtu", publicmtu); Network network = Mockito.mock(Network.class); - responseGenerator = Mockito.mock(ResponseGenerator.class); NetworkResponse response = Mockito.mock(NetworkResponse.class); response.setPublicMtu(publicmtu); Mockito.when(networkService.getNetwork(networkId)).thenReturn(network); Mockito.when(networkService.updateGuestNetwork(cmd)).thenReturn(network); - cmd._responseGenerator = responseGenerator; - Mockito.when(responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Restricted, network)).thenReturn(response); + Mockito.when(_responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Restricted, network)).thenReturn(response); cmd.execute(); - Mockito.verify(responseGenerator).createNetworkResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.any(Network.class)); + Mockito.verify(_responseGenerator).createNetworkResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.any(Network.class)); NetworkResponse actualResponse = (NetworkResponse) cmd.getResponseObject(); Assert.assertEquals(response, actualResponse); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmdTest.java new file mode 100644 index 00000000000..11c41f4c92d --- /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 00000000000..2b55d4c6a58 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmdTest.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.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.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; + +@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; + 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_DELETE, cmd.getEventType()); + assertEquals(String.format("Deleting ipv4 routing firewall rule ID=%s", id), 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 00000000000..53ac45917cb --- /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 00000000000..dd0319df696 --- /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/userdata/ListUserDataCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java index 3f47a078445..8b7db292462 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java @@ -68,7 +68,7 @@ public class ListUserDataCmdTest { Pair, Integer> result = new Pair, Integer>(userDataList, 1); UserDataResponse userDataResponse = Mockito.mock(UserDataResponse.class); - Mockito.when(_mgr.listUserDatas(cmd)).thenReturn(result); + Mockito.when(_mgr.listUserDatas(cmd, false)).thenReturn(result); Mockito.when(_responseGenerator.createUserDataResponse(userData)).thenReturn(userDataResponse); cmd.execute(); @@ -82,7 +82,7 @@ public class ListUserDataCmdTest { List userDataList = new ArrayList(); Pair, Integer> result = new Pair, Integer>(userDataList, 0); - Mockito.when(_mgr.listUserDatas(cmd)).thenReturn(result); + Mockito.when(_mgr.listUserDatas(cmd, false)).thenReturn(result); cmd.execute(); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmdTest.java index c9eb672c9e9..99bc9d2b3fb 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.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.security.InvalidParameterException; - public class CreateVMScheduleCmdTest { @Mock public VMScheduleManager vmScheduleManager; @@ -70,11 +68,11 @@ public class CreateVMScheduleCmdTest { /** * 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 class CreateVMScheduleCmdTest { /** * 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 9b4decc83aa..1f764a84365 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.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.security.InvalidParameterException; - public class DeleteVMScheduleCmdTest { @Mock public VMScheduleManager vmScheduleManager; @@ -89,11 +87,11 @@ public class DeleteVMScheduleCmdTest { /** * 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 class DeleteVMScheduleCmdTest { /** * 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 00000000000..f7e3e38d9c3 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmdTest.java @@ -0,0 +1,483 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiConstants.BootMode; +import org.apache.cloudstack.api.ApiConstants.BootType; +import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.NetworkService; +import com.cloud.utils.db.EntityManager; +import com.cloud.vm.VmDetailConstants; +import com.cloud.network.Network; +import com.cloud.template.VirtualMachineTemplate; +import com.cloud.offering.DiskOffering; +import com.cloud.network.Network.IpAddresses; +import com.cloud.vm.VmDiskInfo; + +@RunWith(MockitoJUnitRunner.class) +public class DeployVMCmdTest { + + @Spy + private DeployVMCmd cmd = new DeployVMCmd(); + + @Test + public void testGetBootType_ValidUEFI() { + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + BootType result = cmd.getBootType(); + + assertEquals(BootType.UEFI, result); + } + + @Test + public void testGetBootTypeValidBIOS() { + ReflectionTestUtils.setField(cmd, "bootType", "BIOS"); + + BootType result = cmd.getBootType(); + + assertEquals(BootType.BIOS, result); + } + + @Test + public void testGetBootTypeInvalidValue() { + ReflectionTestUtils.setField(cmd, "bootType", "INVALID"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getBootType(); + }); + assertTrue(thrownException.getMessage().contains("Invalid bootType INVALID")); + } + + @Test + public void testGetBootTypeNullValue() { + ReflectionTestUtils.setField(cmd, "bootType", null); + + BootType result = cmd.getBootType(); + + assertNull(result); + } + + @Test + public void testGetBootModeValidSecure() { + ReflectionTestUtils.setField(cmd, "bootMode", "SECURE"); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + BootMode result = cmd.getBootMode(); + + assertEquals(BootMode.SECURE, result); + } + + @Test + public void testGetBootModeValidLegacy() { + ReflectionTestUtils.setField(cmd, "bootMode", "LEGACY"); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + BootMode result = cmd.getBootMode(); + + assertEquals(BootMode.LEGACY, result); + } + + @Test + public void testGetBootModeInvalidValue() { + ReflectionTestUtils.setField(cmd, "bootMode", "INVALID"); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getBootMode(); + }); + assertTrue(thrownException.getMessage().contains("Invalid bootmode: INVALID specified for VM: null. Valid values are: [LEGACY, SECURE]")); + } + + @Test + public void testGetBootModeUEFIWithoutBootMode() { + ReflectionTestUtils.setField(cmd, "bootMode", null); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getBootMode(); + }); + assertTrue(thrownException.getMessage().contains("bootmode must be specified for the VM with boot type: UEFI. Valid values are: [LEGACY, SECURE]")); + } + + @Test + public void testGetDetails() { + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + ReflectionTestUtils.setField(cmd, "bootMode", "SECURE"); + ReflectionTestUtils.setField(cmd, "rootdisksize", 100L); + ReflectionTestUtils.setField(cmd, "ioDriverPolicy", "native"); + ReflectionTestUtils.setField(cmd, "iothreadsEnabled", true); + ReflectionTestUtils.setField(cmd, "nicMultiqueueNumber", null); + ReflectionTestUtils.setField(cmd, "nicPackedVirtQueues", null); + ReflectionTestUtils.setField(cmd, "details", new HashMap<>()); + + Map result = cmd.getDetails(); + + assertEquals("SECURE", result.get("UEFI")); + assertEquals("100", result.get(VmDetailConstants.ROOT_DISK_SIZE)); + assertEquals("native", result.get(VmDetailConstants.IO_POLICY)); + assertEquals("true", result.get(VmDetailConstants.IOTHREADS)); + } + + @Test + public void testGetLeaseExpiryActionValidStop() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", "STOP"); + + VMLeaseManager.ExpiryAction result = cmd.getLeaseExpiryAction(); + + assertEquals(VMLeaseManager.ExpiryAction.STOP, result); + } + + @Test + public void testGetLeaseExpiryActionValidDestroy() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", "DESTROY"); + + VMLeaseManager.ExpiryAction result = cmd.getLeaseExpiryAction(); + + assertEquals(VMLeaseManager.ExpiryAction.DESTROY, result); + } + + @Test + public void testGetLeaseExpiryActionInvalidValue() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", "INVALID"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getLeaseExpiryAction(); + }); + assertTrue(thrownException.getMessage().contains("Invalid value configured for leaseexpiryaction")); + } + + @Test + public void testGetLeaseExpiryActionNullValue() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", null); + + VMLeaseManager.ExpiryAction result = cmd.getLeaseExpiryAction(); + + assertNull(result); + } + + @Test + public void testGetIoDriverPolicyValidThrottle() { + ReflectionTestUtils.setField(cmd, "ioDriverPolicy", "native"); + + IoDriverPolicy result = cmd.getIoDriverPolicy(); + + assertEquals(IoDriverPolicy.valueOf("NATIVE"), result); + } + + @Test + public void testGetIoDriverPolicyInvalidValue() { + ReflectionTestUtils.setField(cmd, "ioDriverPolicy", "INVALID"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIoDriverPolicy(); + }); + assertTrue(thrownException.getMessage().contains("Invalid io policy INVALID")); + } + + @Test + public void testGetNetworkIds() { + List networkIds = Arrays.asList(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "networkIds", networkIds); + ReflectionTestUtils.setField(cmd, "vAppNetworks", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", null); + + List result = cmd.getNetworkIds(); + + assertEquals(networkIds, result); + } + + @Test + public void testGetNetworkIdsVAppNetworks() { + Map vAppNetworks = new HashMap<>(); + vAppNetworks.put("network1", new HashMap()); + ReflectionTestUtils.setField(cmd, "vAppNetworks", vAppNetworks); + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + ReflectionTestUtils.setField(cmd, "ip6Address", null); + + List result = cmd.getNetworkIds(); + + assertTrue(result.isEmpty()); + } + + @Test + public void testGetNetworkIdsVAppNetworksAndNetworkIds() { + Map vAppNetworks = new HashMap<>(); + vAppNetworks.put("network1", new HashMap()); + ReflectionTestUtils.setField(cmd, "vAppNetworks", vAppNetworks); + ReflectionTestUtils.setField(cmd, "networkIds", Arrays.asList(1L, 2L)); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getNetworkIds(); + }); + assertTrue(thrownException.getMessage().contains("nicnetworklist can't be specified along with networkids")); + } + + @Test + public void testGetNetworkIdsIpToNetworkListAndNetworkIds() { + Map ipToNetworkList = new HashMap<>(); + ipToNetworkList.put("0", new HashMap()); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", ipToNetworkList); + ReflectionTestUtils.setField(cmd, "networkIds", Arrays.asList(1L, 2L)); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getNetworkIds(); + }); + assertTrue(thrownException.getMessage().contains("ipToNetworkMap can't be specified along with networkIds or ipAddress")); + } + + @Test + public void testGetIpToNetworkMap_WithNetworkIds() { + ReflectionTestUtils.setField(cmd, "networkIds", Arrays.asList(1L, 2L)); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", new HashMap<>()); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIpToNetworkMap(); + }); + assertTrue(thrownException.getMessage().contains("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter")); + } + + @Test + public void testGetIpToNetworkMap_WithIpAddress() { + ReflectionTestUtils.setField(cmd, "ipAddress", "192.168.1.1"); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", new HashMap<>()); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIpToNetworkMap(); + }); + assertTrue(thrownException.getMessage().contains("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter")); + } + + @Test + public void testGetIpToNetworkMap_WithEmptyIpToNetworkList() { + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", new HashMap<>()); + + Map result = cmd.getIpToNetworkMap(); + + assertNull(result); + } + + @Test + public void testGetIpToNetworkMap_WithNullIpToNetworkList() { + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", null); + + Map result = cmd.getIpToNetworkMap(); + + assertNull(result); + } + + @Test + public void testGetDataDiskInfoList() { + Map dataDisksDetails = new HashMap<>(); + Map dataDisk = new HashMap<>(); + dataDisk.put(ApiConstants.DISK_OFFERING_ID, "offering-uuid"); + dataDisk.put(ApiConstants.DEVICE_ID, "0"); + dataDisk.put(ApiConstants.MIN_IOPS, "1000"); + dataDisk.put(ApiConstants.MAX_IOPS, "2000"); + dataDisksDetails.put("0", dataDisk); + + ReflectionTestUtils.setField(cmd, "dataDisksDetails", dataDisksDetails); + + EntityManager entityMgr = mock(EntityManager.class); + ReflectionTestUtils.setField(cmd, "_entityMgr", entityMgr); + DiskOffering diskOffering = mock(DiskOffering.class); + when(diskOffering.getDiskSize()).thenReturn(1024 * 1024 * 1024L); + when(diskOffering.isCustomizedIops()).thenReturn(true); + when(entityMgr.findByUuid(DiskOffering.class, "offering-uuid")).thenReturn(diskOffering); + + List result = cmd.getDataDiskInfoList(); + + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(diskOffering, result.get(0).getDiskOffering()); + assertEquals(1L, result.get(0).getSize().longValue()); + assertEquals(1000L, result.get(0).getMinIops().longValue()); + assertEquals(2000L, result.get(0).getMaxIops().longValue()); + } + + @Test + public void testGetIpAddressesFromIpMap() { + Map ipToNetworkList = new HashMap<>(); + Map ipMap = new HashMap<>(); + ipMap.put("ip", "192.168.1.100"); + ipMap.put("mac", "00:11:22:33:44:55"); + ipMap.put("networkid", "1"); + ipToNetworkList.put("0", ipMap); + + ReflectionTestUtils.setField(cmd, "ipToNetworkList", ipToNetworkList); + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + + Network mockNetwork = mock(Network.class); + NetworkService networkServiceMock = mock(NetworkService.class); + ReflectionTestUtils.setField(cmd, "_networkService", networkServiceMock); + + Map result = cmd.getIpToNetworkMap(); + + assertNotNull(result); + assertTrue(result.containsKey(1L)); + assertEquals(result.get(1L).getIp4Address(), "192.168.1.100"); + assertEquals(result.get(1L).getMacAddress(), "00:11:22:33:44:55"); + } + + @Test + public void testGetIpAddressesFromIpMapInvalidMac() { + Map ipToNetworkList = new HashMap<>(); + Map ipMap = new HashMap<>(); + ipMap.put("ip", "192.168.1.100"); + ipMap.put("mac", "invalid-mac"); + ipMap.put("networkid", "1"); + ipToNetworkList.put("0", ipMap); + + ReflectionTestUtils.setField(cmd, "ipToNetworkList", ipToNetworkList); + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + + Network mockNetwork = mock(Network.class); + NetworkService networkServiceMock = mock(NetworkService.class); + ReflectionTestUtils.setField(cmd, "_networkService", networkServiceMock); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIpToNetworkMap(); + }); + assertTrue(thrownException.getMessage().contains("Mac address is not valid")); + } + + @Test + public void testGetDhcpOptionsMap() { + Map dhcpOptionsNetworkList = new HashMap<>(); + Map dhcpOptions = new HashMap<>(); + dhcpOptions.put("networkid", "network-1"); + dhcpOptions.put("dhcp:114", "url-value"); + dhcpOptions.put("dhcp:66", "www.test.com"); + dhcpOptionsNetworkList.put("0", dhcpOptions); + + ReflectionTestUtils.setField(cmd, "dhcpOptionsNetworkList", dhcpOptionsNetworkList); + + Map> result = cmd.getDhcpOptionsMap(); + + assertNotNull(result); + assertTrue(result.containsKey("network-1")); + Map networkOptions = result.get("network-1"); + assertEquals("url-value", networkOptions.get(114)); + assertEquals("www.test.com", networkOptions.get(66)); + } + + @Test + public void testGetDhcpOptionsMap_WithMissingNetworkId() { + Map dhcpOptionsNetworkList = new HashMap<>(); + Map dhcpOptions = new HashMap<>(); + dhcpOptions.put("dhcp:114", "url-value"); + dhcpOptionsNetworkList.put("0", dhcpOptions); + + ReflectionTestUtils.setField(cmd, "dhcpOptionsNetworkList", dhcpOptionsNetworkList); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, () -> { + cmd.getDhcpOptionsMap(); + }); + assertTrue(thrownException.getMessage().contains("No networkid specified when providing extra dhcp options")); + } + + @Test + public void testGetDataDiskTemplateToDiskOfferingMap() { + ReflectionTestUtils.setField(cmd, "diskOfferingId", null); + + Map dataDiskTemplateToDiskOfferingList = new HashMap<>(); + Map dataDiskTemplate = new HashMap<>(); + dataDiskTemplate.put("datadisktemplateid", "template-uuid"); + dataDiskTemplate.put("diskofferingid", "offering-uuid"); + dataDiskTemplateToDiskOfferingList.put("0", dataDiskTemplate); + + ReflectionTestUtils.setField(cmd, "dataDiskTemplateToDiskOfferingList", dataDiskTemplateToDiskOfferingList); + + VirtualMachineTemplate mockTemplate = mock(VirtualMachineTemplate.class); + when(mockTemplate.getId()).thenReturn(1L); + + DiskOffering mockOffering = mock(DiskOffering.class); + + EntityManager entityMgr = mock(EntityManager.class); + ReflectionTestUtils.setField(cmd, "_entityMgr", entityMgr); + when(entityMgr.findByUuid(VirtualMachineTemplate.class, "template-uuid")).thenReturn(mockTemplate); + when(entityMgr.findByUuid(DiskOffering.class, "offering-uuid")).thenReturn(mockOffering); + + Map result = cmd.getDataDiskTemplateToDiskOfferingMap(); + + assertNotNull(result); + assertEquals(mockOffering, result.get(1L)); + } + + @Test + public void testGetDataDiskTemplateToDiskOfferingMapWithDiskOfferingId() { + ReflectionTestUtils.setField(cmd, "diskOfferingId", 1L); + ReflectionTestUtils.setField(cmd, "dataDiskTemplateToDiskOfferingList", new HashMap<>()); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getDataDiskTemplateToDiskOfferingMap(); + }); + assertTrue(thrownException.getMessage().contains("diskofferingid parameter can't be specified along with datadisktemplatetodiskofferinglist parameter")); + } + + @Test + public void testGetDataDiskTemplateToDiskOfferingMapInvalidTemplateId() { + ReflectionTestUtils.setField(cmd, "diskOfferingId", null); + + Map dataDiskTemplateToDiskOfferingList = new HashMap<>(); + Map dataDiskTemplate = new HashMap<>(); + dataDiskTemplate.put("datadisktemplateid", "invalid-template"); + dataDiskTemplate.put("diskofferingid", "offering-uuid"); + dataDiskTemplateToDiskOfferingList.put("0", dataDiskTemplate); + + ReflectionTestUtils.setField(cmd, "dataDiskTemplateToDiskOfferingList", dataDiskTemplateToDiskOfferingList); + + EntityManager entityMgr = mock(EntityManager.class); + ReflectionTestUtils.setField(cmd, "_entityMgr", entityMgr); + when(entityMgr.findByUuid(VirtualMachineTemplate.class, "invalid-template")).thenReturn(null); + when(entityMgr.findById(VirtualMachineTemplate.class, "invalid-template")).thenReturn(null); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getDataDiskTemplateToDiskOfferingMap(); + }); + assertTrue(thrownException.getMessage().contains("Unable to translate and find entity with datadisktemplateid")); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java index f9a1d945f03..f5434de3581 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.Mock; 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 class ListVMScheduleCmdTest { /** * 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 00000000000..48b41a47fff --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmdTest.java @@ -0,0 +1,223 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.anyList; +import static org.mockito.Mockito.anySet; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceIconManager; +import com.cloud.server.ResourceTag; +import com.cloud.storage.GuestOS; +import com.cloud.utils.db.EntityManager; + +public class ListVMsCmdTest { + + EntityManager _entityMgr; + ResourceIconManager resourceIconManager; + ResponseGenerator _responseGenerator; + + ListVMsCmd cmd; + + @Before + public void setup() { + _entityMgr = mock(EntityManager.class); + resourceIconManager = mock(ResourceIconManager.class); + _responseGenerator = mock(ResponseGenerator.class); + cmd = spy(ListVMsCmd.class); + cmd._entityMgr = _entityMgr; + cmd.resourceIconManager = resourceIconManager; + cmd._responseGenerator = _responseGenerator; + } + + @Test + public void testUpdateVMResponse_withMixedIcons() { + String vm1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getId()).thenReturn(vm1Uuid); + String vm2Uuid = UUID.randomUUID().toString(); + UserVmResponse vm2 = mock(UserVmResponse.class); + when(vm2.getId()).thenReturn(vm2Uuid); + List responses = Arrays.asList(vm1, vm2); + ResourceIcon icon1 = mock(ResourceIcon.class); + ResourceIcon icon2 = mock(ResourceIcon.class); + Map initialIcons = new HashMap<>(); + initialIcons.put(vm1Uuid, icon1); + when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.UserVm, Set.of(vm1Uuid, vm2Uuid))) + .thenReturn(initialIcons); + Map fallbackIcons = Map.of(vm2Uuid, icon2); + doReturn(fallbackIcons).when(cmd).getResourceIconsForUsingTemplateIso(anyList()); + ResourceIconResponse iconResponse1 = new ResourceIconResponse(); + ResourceIconResponse iconResponse2 = new ResourceIconResponse(); + when(_responseGenerator.createResourceIconResponse(icon1)).thenReturn(iconResponse1); + when(_responseGenerator.createResourceIconResponse(icon2)).thenReturn(iconResponse2); + cmd.updateVMResponse(responses); + verify(vm1).setResourceIconResponse(iconResponse1); + verify(vm2).setResourceIconResponse(iconResponse2); + } + + @Test + public void testUpdateVMResponse_withEmptyList() { + cmd.updateVMResponse(Collections.emptyList()); + verify(resourceIconManager, never()).getByResourceTypeAndIds(Mockito.any(), Mockito.anyCollection()); + } + + @Test + public void testGetResourceIconsForUsingTemplateIso_withValidData() { + String vm1Uuid = UUID.randomUUID().toString(); + String template1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getId()).thenReturn(vm1Uuid); + when(vm1.getTemplateId()).thenReturn(template1Uuid); + when(vm1.getIsoId()).thenReturn(null); + String vm2Uuid = UUID.randomUUID().toString(); + String iso2Uuid = UUID.randomUUID().toString(); + UserVmResponse vm2 = mock(UserVmResponse.class); + when(vm2.getId()).thenReturn(vm2Uuid); + when(vm2.getTemplateId()).thenReturn(null); + when(vm2.getIsoId()).thenReturn(iso2Uuid); + List responses = Arrays.asList(vm1, vm2); + Map templateIcons = new HashMap<>(); + templateIcons.put(template1Uuid, mock(ResourceIcon.class)); + Map isoIcons = new HashMap<>(); + isoIcons.put(iso2Uuid, mock(ResourceIcon.class)); + when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.Template, Set.of(template1Uuid))) + .thenReturn(templateIcons); + when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.ISO, Set.of(iso2Uuid))) + .thenReturn(isoIcons); + doReturn(Collections.emptyMap()).when(cmd).getResourceIconsUsingOsCategory(anyList()); + Map result = cmd.getResourceIconsForUsingTemplateIso(responses); + assertEquals(2, result.size()); + assertTrue(result.containsKey(vm1Uuid)); + assertTrue(result.containsKey(vm2Uuid)); + assertEquals(templateIcons.get(template1Uuid), result.get(vm1Uuid)); + assertEquals(isoIcons.get(iso2Uuid), result.get(vm2Uuid)); + } + + @Test + public void testGetResourceIconsForUsingTemplateIso_withMissingIcons() { + String vm1Uuid = UUID.randomUUID().toString(); + String template1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getId()).thenReturn(vm1Uuid); + when(vm1.getTemplateId()).thenReturn(template1Uuid); + when(vm1.getIsoId()).thenReturn(null); + List responses = List.of(vm1); + when(resourceIconManager.getByResourceTypeAndUuids(eq(ResourceTag.ResourceObjectType.Template), anySet())) + .thenReturn(Collections.emptyMap()); + when(resourceIconManager.getByResourceTypeAndUuids(eq(ResourceTag.ResourceObjectType.ISO), anySet())) + .thenReturn(Collections.emptyMap()); + Map fallbackIcons = Map.of(vm1Uuid, mock(ResourceIcon.class)); + doReturn(fallbackIcons).when(cmd).getResourceIconsUsingOsCategory(anyList()); + Map result = cmd.getResourceIconsForUsingTemplateIso(responses); + assertEquals(1, result.size()); + assertEquals(fallbackIcons.get("vm1"), result.get("vm1")); + } + + @Test + public void testGetResourceIconsUsingOsCategory_withValidData() { + String vm1Uuid = UUID.randomUUID().toString(); + String os1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getGuestOsId()).thenReturn(os1Uuid); + when(vm1.getId()).thenReturn(vm1Uuid); + String vm2Uuid = UUID.randomUUID().toString(); + String os2Uuid = UUID.randomUUID().toString(); + UserVmResponse vm2 = mock(UserVmResponse.class); + when(vm2.getGuestOsId()).thenReturn(os2Uuid); + when(vm2.getId()).thenReturn(vm2Uuid); + List responses = Arrays.asList(vm1, vm2); + GuestOS guestOS1 = mock(GuestOS.class); + when(guestOS1.getUuid()).thenReturn(os1Uuid); + when(guestOS1.getCategoryId()).thenReturn(10L); + GuestOS guestOS2 = mock(GuestOS.class); + when(guestOS2.getUuid()).thenReturn(os2Uuid); + when(guestOS2.getCategoryId()).thenReturn(20L); + when(_entityMgr.listByUuids(eq(GuestOS.class), anySet())) + .thenReturn(Arrays.asList(guestOS1, guestOS2)); + ResourceIcon icon1 = mock(ResourceIcon.class); + ResourceIcon icon2 = mock(ResourceIcon.class); + Map categoryIcons = new HashMap<>(); + categoryIcons.put(10L, icon1); + categoryIcons.put(20L, icon2); + when(resourceIconManager.getByResourceTypeAndIds(eq(ResourceTag.ResourceObjectType.GuestOsCategory), anySet())) + .thenReturn(categoryIcons); + Map result = cmd.getResourceIconsUsingOsCategory(responses); + assertEquals(2, result.size()); + assertEquals(icon1, result.get(vm1Uuid)); + assertEquals(icon2, result.get(vm2Uuid)); + } + + @Test + public void testGetResourceIconsUsingOsCategory_missingGuestOS() { + String vm1Uuid = UUID.randomUUID().toString(); + String os1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getGuestOsId()).thenReturn(vm1Uuid); + when(vm1.getId()).thenReturn(os1Uuid); + List responses = Collections.singletonList(vm1); + when(_entityMgr.listByUuids(eq(GuestOS.class), anySet())) + .thenReturn(Collections.emptyList()); + Map result = cmd.getResourceIconsUsingOsCategory(responses); + assertTrue(result.isEmpty()); + } + + @Test + public void testGetResourceIconsUsingOsCategory_missingIcon() { + UserVmResponse vm1 = mock(UserVmResponse.class); + String vmUuid = UUID.randomUUID().toString(); + String osUuid = UUID.randomUUID().toString(); + when(vm1.getGuestOsId()).thenReturn(osUuid); + when(vm1.getId()).thenReturn(vmUuid); + List responses = Collections.singletonList(vm1); + GuestOS guestOS1 = mock(GuestOS.class); + when(guestOS1.getCategoryId()).thenReturn(10L); + when(guestOS1.getUuid()).thenReturn(osUuid); + when(_entityMgr.listByUuids(eq(GuestOS.class), anySet())) + .thenReturn(Collections.singletonList(guestOS1)); + when(resourceIconManager.getByResourceTypeAndIds(eq(ResourceTag.ResourceObjectType.GuestOsCategory), anySet())) + .thenReturn(Collections.emptyMap()); + Map result = cmd.getResourceIconsUsingOsCategory(responses); + assertTrue(result.containsKey(vmUuid)); + assertNull(result.get(vmUuid)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java index 5ce133382f3..2c6c485f25b 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.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.security.InvalidParameterException; - public class UpdateVMScheduleCmdTest { @Mock public VMScheduleManager vmScheduleManager; @@ -71,11 +69,11 @@ public class UpdateVMScheduleCmdTest { /** * 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 class UpdateVMScheduleCmdTest { /** * 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/vpc/CreateVPCCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java index 79f27fd6687..18c03f8f4bb 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java @@ -25,7 +25,6 @@ import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcService; import com.cloud.user.AccountService; import com.cloud.utils.db.EntityManager; -import junit.framework.TestCase; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.response.VpcResponse; @@ -39,7 +38,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; @RunWith(MockitoJUnitRunner.class) -public class CreateVPCCmdTest extends TestCase { +public class CreateVPCCmdTest { @Mock public VpcService _vpcService; @@ -47,9 +46,13 @@ public class CreateVPCCmdTest extends TestCase { public EntityManager _entityMgr; @Mock public AccountService _accountService; - private ResponseGenerator responseGenerator; + @Mock + private ResponseGenerator _responseGenerator; + @Mock + private Object job; + @InjectMocks - CreateVPCCmd cmd = new CreateVPCCmd(); + CreateVPCCmd cmd; @Test public void testGetAccountName() { @@ -86,6 +89,20 @@ public class CreateVPCCmdTest extends TestCase { 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 class CreateVPCCmdTest extends TestCase { @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 00000000000..50248383b4f --- /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 00000000000..9515984134e --- /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 00000000000..7c82eb84368 --- /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 00000000000..add9544de7d --- /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 523b3de9e3c..04e3ad7df60 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 org.junit.Test; 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 final class HostResponseTest extends TestCase { 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 final class HostResponseTest extends TestCase { 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 00000000000..717668d054e --- /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 00000000000..6fb5141e7a9 --- /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/VolumeForImportResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/VolumeForImportResponseTest.java new file mode 100644 index 00000000000..7d0538836bf --- /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/extension/ExtensionCustomActionTest.java b/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java new file mode 100644 index 00000000000..ae4314aa11a --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java @@ -0,0 +1,484 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.extension; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.junit.Test; + +import com.cloud.exception.InvalidParameterValueException; + +public class ExtensionCustomActionTest { + + @Test + public void testResourceType() { + ExtensionCustomAction.ResourceType vmType = ExtensionCustomAction.ResourceType.VirtualMachine; + assertEquals(com.cloud.vm.VirtualMachine.class, vmType.getAssociatedClass()); + } + + @Test + public void testParameterTypeSupportsOptions() { + assertTrue(ExtensionCustomAction.Parameter.Type.STRING.canSupportsOptions()); + assertTrue(ExtensionCustomAction.Parameter.Type.NUMBER.canSupportsOptions()); + assertFalse(ExtensionCustomAction.Parameter.Type.BOOLEAN.canSupportsOptions()); + assertFalse(ExtensionCustomAction.Parameter.Type.DATE.canSupportsOptions()); + } + + @Test + public void testValidationFormatBaseType() { + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.UUID.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.PASSWORD.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.URL.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL.getBaseType()); + assertNull(ExtensionCustomAction.Parameter.ValidationFormat.NONE.getBaseType()); + } + + @Test + public void testParameterFromMapValid() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "EMAIL"); + map.put(ApiConstants.REQUIRED, "true"); + map.put(ApiConstants.VALUE_OPTIONS, "test@example.com,another@test.com"); + + ExtensionCustomAction.Parameter param = ExtensionCustomAction.Parameter.fromMap(map); + + assertEquals("testParam", param.getName()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, param.getType()); + assertEquals(ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, param.getValidationFormat()); + assertTrue(param.isRequired()); + assertEquals(2, param.getValueOptions().size()); + assertTrue(param.getValueOptions().contains("test@example.com")); + assertTrue(param.getValueOptions().contains("another@test.com")); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapEmptyName() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, ""); + map.put(ApiConstants.TYPE, "STRING"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapNoType() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidType() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "INVALID_TYPE"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidValidationFormat() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "INVALID_FORMAT"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapMismatchedTypeAndFormat() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "DECIMAL"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test + public void testParameterFromMapWithNumberOptions() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "NUMBER"); + map.put(ApiConstants.VALIDATION_FORMAT, "DECIMAL"); + map.put(ApiConstants.VALUE_OPTIONS, "1.5,2.7,3.0"); + + ExtensionCustomAction.Parameter param = ExtensionCustomAction.Parameter.fromMap(map); + + assertEquals(ExtensionCustomAction.Parameter.Type.NUMBER, param.getType()); + assertEquals(ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL, param.getValidationFormat()); + assertEquals(3, param.getValueOptions().size()); + assertTrue(param.getValueOptions().contains(1.5f)); + assertTrue(param.getValueOptions().contains(2.7f)); + assertTrue(param.getValueOptions().contains(3.0f)); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidNumberOptions() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "NUMBER"); + map.put(ApiConstants.VALUE_OPTIONS, "1.5,invalid,3.0"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidEmailOptions() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "EMAIL"); + map.put(ApiConstants.VALUE_OPTIONS, "valid@email.com,invalid-email"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test + public void testValidatedValueString() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, + null, + false + ); + + Object result = param.validatedValue("test@example.com"); + assertEquals("test@example.com", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidEmail() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, + null, + false + ); + + param.validatedValue("invalid-email"); + } + + @Test + public void testValidatedValueUUID() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.UUID, + null, + false + ); + + String validUUID = "550e8400-e29b-41d4-a716-446655440000"; + Object result = param.validatedValue(validUUID); + assertEquals(validUUID, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidUUID() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.UUID, + null, + false + ); + + param.validatedValue("invalid-uuid"); + } + + @Test + public void testValidatedValueURL() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.URL, + null, + false + ); + + Object result = param.validatedValue("https://example.com"); + assertEquals("https://example.com", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidURL() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.URL, + null, + false + ); + + param.validatedValue("not-a-url"); + } + + @Test + public void testValidatedValuePassword() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.PASSWORD, + null, + false + ); + + Object result = param.validatedValue("mypassword"); + assertEquals("mypassword", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueEmptyPassword() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.PASSWORD, + null, + false + ); + + param.validatedValue(" "); + } + + @Test + public void testValidatedValueNumber() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + Object result = param.validatedValue("42"); + assertEquals(42, result); + } + + @Test + public void testValidatedValueDecimal() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL, + null, + false + ); + + Object result = param.validatedValue("3.14"); + assertEquals(3.14f, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidNumber() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + param.validatedValue("not-a-number"); + } + + @Test + public void testValidatedValueBoolean() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.BOOLEAN, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + Object result = param.validatedValue("true"); + assertEquals(true, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidBoolean() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.BOOLEAN, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + Object result = param.validatedValue("maybe"); + } + + @Test + public void testValidatedValueWithOptions() { + List options = Arrays.asList("option1", "option2", "option3"); + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + options, + false + ); + + Object result = param.validatedValue("option2"); + assertEquals("option2", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueNotInOptions() { + List options = Arrays.asList("option1", "option2", "option3"); + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + options, + false + ); + + param.validatedValue("option4"); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueEmpty() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + param.validatedValue(""); + } + + @Test + public void testValidateParameterValues() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("required1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true), + new ExtensionCustomAction.Parameter("required2", ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true), + new ExtensionCustomAction.Parameter("optional", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, false) + ); + + Map suppliedValues = new HashMap<>(); + suppliedValues.put("required1", "value1"); + suppliedValues.put("required2", "42"); + suppliedValues.put("optional", "optionalValue"); + + Map result = ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, suppliedValues); + + assertEquals("value1", result.get("required1")); + assertEquals(42, result.get("required2")); + assertEquals("optionalValue", result.get("optional")); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateParameterValuesMissingRequired() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("required1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true) + ); + + Map suppliedValues = new HashMap<>(); + + ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, suppliedValues); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateParameterValuesEmptyRequired() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("required1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true) + ); + + Map suppliedValues = new HashMap<>(); + suppliedValues.put("required1", " "); + + ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, suppliedValues); + } + + @Test + public void testValidateParameterValuesNullSupplied() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("optional", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, false) + ); + + Map result = ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, null); + assertTrue(result.isEmpty()); + } + + @Test + public void testJsonSerializationDeserialization() { + List originalParams = Arrays.asList( + new ExtensionCustomAction.Parameter("param1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, Arrays.asList("test@example.com"), true), + new ExtensionCustomAction.Parameter("param2", ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL, Arrays.asList(1.5f, 2.7f), false) + ); + + String json = ExtensionCustomAction.Parameter.toJsonFromList(originalParams); + List deserializedParams = ExtensionCustomAction.Parameter.toListFromJson(json); + + assertEquals(originalParams.size(), deserializedParams.size()); + assertEquals(originalParams.get(0).getName(), deserializedParams.get(0).getName()); + assertEquals(originalParams.get(0).getType(), deserializedParams.get(0).getType()); + assertEquals(originalParams.get(0).getValidationFormat(), deserializedParams.get(0).getValidationFormat()); + assertEquals(originalParams.get(0).isRequired(), deserializedParams.get(0).isRequired()); + assertEquals(originalParams.get(0).getValueOptions(), deserializedParams.get(0).getValueOptions()); + } + + @Test + public void testToString() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, + null, + true + ); + + String result = param.toString(); + assertTrue(result.contains("testParam")); + assertTrue(result.contains("STRING")); + assertTrue(result.contains("true")); + } +} diff --git a/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 00000000000..2d1f8868ffc --- /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 00000000000..59de3b8ac4e --- /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 3d9a4597060..8c3812eb7d2 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 70e727b40b2..84c87ae2e44 100755 --- a/client/bindir/cloud-setup-management.in +++ b/client/bindir/cloud-setup-management.in @@ -16,13 +16,27 @@ # specific language governing permissions and limitations # under the License. +import os import sys +# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- +# ---- We do this so cloud_utils can be looked up in the following order: +# ---- 1) Sources directory +# ---- 2) waf configured PYTHONDIR +# ---- 3) System Python path +for pythonpath in ( + "@PYTHONDIR@", + os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), + ): + if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) +# ---- End snippet of code ---- + from cloudutils.syscfg import sysConfigFactory from cloudutils.utilities import initLoging, UnknownSystemException from cloudutils.cloudException import CloudRuntimeException, CloudInternalException from cloudutils.globalEnv import globalEnv from cloudutils.serviceConfigServer import cloudManagementConfig from optparse import OptionParser + if __name__ == '__main__': initLoging("@MSLOGDIR@/setupManagement.log") glbEnv = globalEnv() diff --git a/client/bindir/cloud-update-xenserver-licenses.in b/client/bindir/cloud-update-xenserver-licenses.in index 9ce1898dc81..2be3a083f97 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 @@ -46,7 +46,7 @@ cfg = "@MSCONF@/db.properties" #---------------------- 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.""" @@ -88,7 +88,7 @@ def parseuserpwfromhosts(hosts): return creds class XenServerConfigurator(Thread): - + def __init__(self,host,user,password,keyfiledata): Thread.__init__(self) self.host = host @@ -99,7 +99,7 @@ class XenServerConfigurator(Thread): self.stdout = "" self.stderr = "" self.state = 'initialized' - + def run(self): try: self.state = 'running' @@ -120,18 +120,18 @@ class XenServerConfigurator(Thread): 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() @@ -162,7 +162,7 @@ for host,(user,password) in list(creds.items()): for c in configurators: c.start() - + for c in configurators: print(c.host + "...", end=' ') c.join() diff --git a/client/conf/db.properties.in b/client/conf/db.properties.in index 8f31aff17e6..0f7d2706a42 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 dbcf8c6198b..26da171269d 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 57d81c81217..fd75c9d3ea0 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,6 @@ 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@ diff --git a/client/pom.xml b/client/pom.xml index a73aed7d032..81e2b780934 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -25,24 +25,9 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.21.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 @@ -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 @@ -1102,11 +1097,26 @@ 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/ServerDaemon.java b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java index d2e4483835e..c6fd2ff24dc 100644 --- a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java +++ b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java @@ -82,6 +82,8 @@ public class ServerDaemon implements Daemon { 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; //////////////////////////////////////////////////////// /////////////// Server Configuration /////////////////// @@ -94,6 +96,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; @@ -141,6 +144,7 @@ public class ServerDaemon implements Daemon { 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)))); } catch (final IOException e) { logger.warn("Failed to read configuration from server.properties file", e); } finally { @@ -192,6 +196,7 @@ public class ServerDaemon implements Daemon { // 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); @@ -264,6 +269,7 @@ public class ServerDaemon implements Daemon { 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(); @@ -366,4 +372,8 @@ public class ServerDaemon implements Daemon { public void setMaxFormContentSize(int maxFormContentSize) { this.maxFormContentSize = maxFormContentSize; } + + public void setMaxFormKeys(int maxFormKeys) { + this.maxFormKeys = maxFormKeys; + } } diff --git a/client/src/main/resources/META-INF/cloudstack/webApplicationContext.xml b/client/src/main/resources/META-INF/cloudstack/webApplicationContext.xml index 3b3c6dbe35c..e1c46268618 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/core/pom.xml b/core/pom.xml index 83cdee8cf4f..25ac46518b3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.21.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 00000000000..a02ac92927a --- /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 00000000000..fc066e5c589 --- /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 982e10cf2eb..94239f2900e 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 class CheckOnHostCommand extends Command { } 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 b299c602dde..351702a048c 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java @@ -25,7 +25,6 @@ import java.util.Map; public class CheckS2SVpnConnectionsAnswer extends Answer { Map ipToConnected; Map ipToDetail; - String details; protected CheckS2SVpnConnectionsAnswer() { ipToConnected = new HashMap(); diff --git a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java index dd136d8642f..07b7e102df9 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java @@ -17,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 b4036bebf3a..bd44b35c895 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 @@ package com.cloud.agent.api; 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 00000000000..a4d73a8b164 --- /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 683d4afd5b2..ac6f15ec4c3 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 class ConsoleAccessAuthenticationCommand extends AgentControlCommand { } 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 class ConsoleAccessAuthenticationCommand extends AgentControlCommand { 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 829888570a6..8092ab9b43f 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java @@ -16,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 63234b04480..f938d0ac55d 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java @@ -20,24 +20,27 @@ import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.RemoteInstanceTO; import com.cloud.hypervisor.Hypervisor; -import java.util.List; - public class ConvertInstanceCommand extends Command { private RemoteInstanceTO sourceInstance; private Hypervisor.HypervisorType destinationHypervisorType; - private List destinationStoragePools; private DataStoreTO conversionTemporaryLocation; + private String templateDirOnConversionLocation; + private boolean checkConversionSupport; + private boolean exportOvfToConversionLocation; + private int threadsCountToExportOvf = 0; public ConvertInstanceCommand() { } - public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, - List destinationStoragePools, DataStoreTO conversionTemporaryLocation) { + public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, DataStoreTO conversionTemporaryLocation, + String templateDirOnConversionLocation, boolean checkConversionSupport, boolean exportOvfToConversionLocation) { this.sourceInstance = sourceInstance; this.destinationHypervisorType = destinationHypervisorType; - this.destinationStoragePools = destinationStoragePools; this.conversionTemporaryLocation = conversionTemporaryLocation; + this.templateDirOnConversionLocation = templateDirOnConversionLocation; + this.checkConversionSupport = checkConversionSupport; + this.exportOvfToConversionLocation = exportOvfToConversionLocation; } public RemoteInstanceTO getSourceInstance() { @@ -48,14 +51,30 @@ public class ConvertInstanceCommand extends Command { return destinationHypervisorType; } - public List getDestinationStoragePools() { - return destinationStoragePools; - } - 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; + } + @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 00000000000..c19a061f736 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotAnswer.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.agent.api; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public class ConvertSnapshotAnswer extends Answer { + + private SnapshotObjectTO snapshotObjectTO; + + public ConvertSnapshotAnswer(SnapshotObjectTO snapshotObjectTO) { + super(null); + this.snapshotObjectTO = snapshotObjectTO; + } + + public SnapshotObjectTO getSnapshotObjectTO() { + return snapshotObjectTO; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ConvertSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotCommand.java new file mode 100644 index 00000000000..e456d4b6b12 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotCommand.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.agent.api; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public class ConvertSnapshotCommand extends Command { + + public static final String TEMP_SNAPSHOT_NAME = "_temp"; + + SnapshotObjectTO snapshotObjectTO; + + public SnapshotObjectTO getSnapshotObjectTO() { + return snapshotObjectTO; + } + + public ConvertSnapshotCommand(SnapshotObjectTO snapshotObjectTO) { + this.snapshotObjectTO = snapshotObjectTO; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java index f6d7cab4596..4aec0b26581 100644 --- a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java @@ -17,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 class CopyRemoteVolumeAnswer extends Answer { return size; } + public Map getVolumeDetails() { + return volumeDetails; + } + public String getString() { return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]"; } diff --git a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeCommand.java b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeCommand.java index 82bc4d7cb45..798336b0e72 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 @@ package com.cloud.agent.api; 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/GetGPUStatsAnswer.java b/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java index 8b3cd44e207..5bf70ed086f 100644 --- a/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java @@ -19,7 +19,9 @@ package com.cloud.agent.api; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import com.cloud.agent.api.LogLevel.Log4jLevel; @@ -27,6 +29,7 @@ import com.cloud.agent.api.LogLevel.Log4jLevel; public class GetGPUStatsAnswer extends Answer { private HashMap> groupDetails; + private List gpuDevices = new ArrayList<>(); public GetGPUStatsAnswer(final GetGPUStatsCommand cmd, final HashMap> groupDetails) { super(cmd); @@ -37,7 +40,21 @@ public class GetGPUStatsAnswer extends Answer { super(cmd, success, details); } + public GetGPUStatsAnswer(final GetGPUStatsCommand cmd, final List gpuDevices) { + super(cmd); + this.gpuDevices = gpuDevices; + } + + public HashMap> getGroupDetails() { return groupDetails; } + + public List getGpuDevices() { + return gpuDevices; + } + + public void setGpuDevices(List gpuDevices) { + this.gpuDevices = gpuDevices; + } } diff --git a/core/src/main/java/com/cloud/agent/api/GetRemoteVmsAnswer.java b/core/src/main/java/com/cloud/agent/api/GetRemoteVmsAnswer.java index 8cd072f1da1..c4e590591d0 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 org.apache.cloudstack.vm.UnmanagedInstanceTO; 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 5c71d12dbd0..5b6b9bdd360 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 26e7b749586..79753661066 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; } - public GetStorageStatsAnswer(GetStorageStatsCommand cmd, long capacity, long used) { + @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.capacity = capacity; - this.used = used; + this.capacityBytes = capacityBytes; + this.usedBytes = usedBytes; + } + + public GetStorageStatsAnswer(GetStorageStatsCommand cmd, long capacityBytes, long usedBytes, Long capacityIops, Long usedIops) { + super(cmd, true, null); + 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 771d472be2a..950930ec614 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 java.util.HashMap; 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 2cd80aebea1..c0b8987e152 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 @@ import org.apache.commons.collections.CollectionUtils; * 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 a9c7413b9f0..4efc0781f91 100644 --- a/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java +++ b/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java @@ -24,11 +24,13 @@ public class GetVmIpAddressCommand extends Command { String vmName; String vmNetworkCidr; boolean windows = false; + String macAddress; - public GetVmIpAddressCommand(String vmName, String vmNetworkCidr, boolean windows) { + public GetVmIpAddressCommand(String vmName, String vmNetworkCidr, boolean windows, String macAddress) { this.vmName = vmName; this.windows = windows; this.vmNetworkCidr = vmNetworkCidr; + this.macAddress = macAddress; } @Override @@ -47,4 +49,8 @@ public class GetVmIpAddressCommand extends Command { public String getVmNetworkCidr() { return vmNetworkCidr; } + + public String getMacAddress() { + return macAddress; + } } diff --git a/core/src/main/java/com/cloud/agent/api/GetVolumeStatAnswer.java b/core/src/main/java/com/cloud/agent/api/GetVolumeStatAnswer.java new file mode 100644 index 00000000000..8352c97c108 --- /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 00000000000..1be3d6a0419 --- /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 00000000000..3c46994499d --- /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 00000000000..6bc3356eb67 --- /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 00000000000..16942bb05d4 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/HandleCksIsoCommand.java @@ -0,0 +1,34 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.agent.api; + +import com.cloud.agent.api.routing.NetworkElementCommand; + +public class HandleCksIsoCommand extends NetworkElementCommand { + + private boolean mountCksIso; + + public HandleCksIsoCommand(boolean mountCksIso) { + this.mountCksIso = mountCksIso; + } + + public boolean isMountCksIso() { + return mountCksIso; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceAnswer.java b/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceAnswer.java new file mode 100644 index 00000000000..2a8f8704e3f --- /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 00000000000..9d50e852ced --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceCommand.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.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; + + public ImportConvertedInstanceCommand() { + } + + public ImportConvertedInstanceCommand(RemoteInstanceTO sourceInstance, + List destinationStoragePools, + DataStoreTO conversionTemporaryLocation, String temporaryConvertUuid) { + this.sourceInstance = sourceInstance; + this.destinationStoragePools = destinationStoragePools; + this.conversionTemporaryLocation = conversionTemporaryLocation; + this.temporaryConvertUuid = temporaryConvertUuid; + } + + public RemoteInstanceTO getSourceInstance() { + return sourceInstance; + } + + public List getDestinationStoragePools() { + return destinationStoragePools; + } + + public DataStoreTO getConversionTemporaryLocation() { + return conversionTemporaryLocation; + } + + public String getTemporaryConvertUuid() { + return temporaryConvertUuid; + } + + @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 00000000000..33d32c7f6cc --- /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 00000000000..9471a68669f --- /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 3acdb9c351b..5ac4e9ae445 100644 --- a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java +++ b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java @@ -29,14 +29,14 @@ import com.cloud.agent.api.to.VirtualMachineTO; public class MigrateCommand extends Command { private String vmName; - private String destIp; + private String destinationIp; private Map migrateStorage; private boolean migrateStorageManaged; private boolean migrateNonSharedInc; private boolean autoConvergence; private String hostGuid; - private boolean isWindows; - private VirtualMachineTO vmTO; + private boolean windows; + private VirtualMachineTO virtualMachine; private boolean executeInSequence = false; private List migrateDiskInfoList = new ArrayList<>(); private Map dpdkInterfaceMapping = new HashMap<>(); @@ -64,11 +64,11 @@ public class MigrateCommand extends Command { protected MigrateCommand() { } - public MigrateCommand(String vmName, String destIp, boolean isWindows, VirtualMachineTO vmTO, boolean executeInSequence) { + public MigrateCommand(String vmName, String destinationIp, boolean windows, VirtualMachineTO virtualMachine, boolean executeInSequence) { this.vmName = vmName; - this.destIp = destIp; - this.isWindows = isWindows; - this.vmTO = vmTO; + this.destinationIp = destinationIp; + this.windows = windows; + this.virtualMachine = virtualMachine; this.executeInSequence = executeInSequence; } @@ -105,15 +105,15 @@ public class MigrateCommand extends Command { } public boolean isWindows() { - return isWindows; + return windows; } public VirtualMachineTO getVirtualMachine() { - return vmTO; + return virtualMachine; } public String getDestinationIp() { - return destIp; + return destinationIp; } public String getVmName() { @@ -233,4 +233,9 @@ public class MigrateCommand extends Command { this.isSourceDiskOnStorageFileSystem = isDiskOnFileSystemStorage; } } + + @Override + public boolean isReconcile() { + return true; + } } diff --git a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java index 552ffb85aaf..a3401d6faed 100644 --- a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java @@ -46,6 +46,10 @@ public class ModifyStoragePoolAnswer extends Answer { templateInfo = tInfo; } + public ModifyStoragePoolAnswer(ModifyStoragePoolCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + public void setPoolInfo(StoragePoolInfo poolInfo) { this.poolInfo = poolInfo; } diff --git a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java index ad05fe1d615..06940266b53 100644 --- a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java @@ -46,6 +46,10 @@ public class ModifyStoragePoolCommand extends Command { this.details = details; } + public ModifyStoragePoolCommand(boolean add, StoragePool pool, Map details) { + this(add, pool, LOCAL_PATH_PREFIX + File.separator + UUID.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())); } diff --git a/core/src/main/java/com/cloud/agent/api/PingAnswer.java b/core/src/main/java/com/cloud/agent/api/PingAnswer.java index 6353b121583..22eb4443f8a 100644 --- a/core/src/main/java/com/cloud/agent/api/PingAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/PingAnswer.java @@ -19,18 +19,25 @@ package com.cloud.agent.api; +import java.util.ArrayList; +import java.util.List; + public class PingAnswer extends Answer { private PingCommand _command = null; private boolean sendStartup = false; + private List avoidMsList; + + private List reconcileCommands = new ArrayList<>(); protected PingAnswer() { } - public PingAnswer(PingCommand cmd, boolean sendStartup) { + public PingAnswer(PingCommand cmd, List avoidMsList, boolean sendStartup) { super(cmd); _command = cmd; this.sendStartup = sendStartup; + this.avoidMsList = avoidMsList; } public PingCommand getCommand() { @@ -44,4 +51,20 @@ public class PingAnswer extends Answer { public void setSendStartup(boolean sendStartup) { this.sendStartup = sendStartup; } + + public List getReconcileCommands() { + return reconcileCommands; + } + + public void setReconcileCommands(List reconcileCommands) { + this.reconcileCommands = reconcileCommands; + } + + public void addReconcileCommand(String reconcileCommand) { + this.reconcileCommands.add(reconcileCommand); + } + + public List getAvoidMsList() { + return avoidMsList; + } } diff --git a/core/src/main/java/com/cloud/agent/api/PingCommand.java b/core/src/main/java/com/cloud/agent/api/PingCommand.java index 4192fc2e747..4ae64da89c3 100644 --- a/core/src/main/java/com/cloud/agent/api/PingCommand.java +++ b/core/src/main/java/com/cloud/agent/api/PingCommand.java @@ -20,11 +20,14 @@ package com.cloud.agent.api; import com.cloud.host.Host; +import org.apache.cloudstack.command.CommandInfo; public class PingCommand extends Command { Host.Type hostType; long hostId; boolean outOfBand; + @LogLevel(LogLevel.Log4jLevel.Trace) + private CommandInfo[] commandInfos = new CommandInfo[] {}; protected PingCommand() { } @@ -78,4 +81,12 @@ public class PingCommand extends Command { result = 31 * result + (int) (hostId ^ (hostId >>> 32)); return result; } + + public CommandInfo[] getCommandInfos() { + return commandInfos; + } + + public void setCommandInfos(CommandInfo[] commandInfos) { + this.commandInfos = commandInfos; + } } diff --git a/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningAnswer.java b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningAnswer.java new file mode 100644 index 00000000000..b94d18c537e --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningAnswer.java @@ -0,0 +1,51 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.agent.api; + +import java.util.Map; + +import com.cloud.agent.api.to.VirtualMachineTO; + +public class PrepareExternalProvisioningAnswer extends Answer { + + Map serverDetails; + VirtualMachineTO virtualMachineTO; + + public PrepareExternalProvisioningAnswer() { + super(); + } + + public PrepareExternalProvisioningAnswer(PrepareExternalProvisioningCommand cmd, Map externalDetails, VirtualMachineTO virtualMachineTO, String details) { + super(cmd, true, details); + this.serverDetails = externalDetails; + this.virtualMachineTO = virtualMachineTO; + } + + public PrepareExternalProvisioningAnswer(PrepareExternalProvisioningCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + public Map getServerDetails() { + return serverDetails; + } + + public VirtualMachineTO getVirtualMachineTO() { + return virtualMachineTO; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningCommand.java b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningCommand.java new file mode 100644 index 00000000000..44f57607eba --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningCommand.java @@ -0,0 +1,39 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.agent.api; + +import com.cloud.agent.api.to.VirtualMachineTO; + +public class PrepareExternalProvisioningCommand extends Command { + + VirtualMachineTO virtualMachineTO; + + public PrepareExternalProvisioningCommand(VirtualMachineTO vmTO) { + this.virtualMachineTO = vmTO; + } + + public VirtualMachineTO getVirtualMachineTO() { + return virtualMachineTO; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/PrepareStorageClientAnswer.java b/core/src/main/java/com/cloud/agent/api/PrepareStorageClientAnswer.java new file mode 100644 index 00000000000..85afb925646 --- /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 00000000000..8dea9c11c53 --- /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 637e4f54da0..49768297ad5 100644 --- a/core/src/main/java/com/cloud/agent/api/ReadyCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ReadyCommand.java @@ -19,6 +19,8 @@ package com.cloud.agent.api; +import com.cloud.host.Host; + import java.util.List; public class ReadyCommand extends Command { @@ -30,19 +32,25 @@ public class ReadyCommand extends Command { 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(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 class ReadyCommand extends Command { return hostId; } + public String getHostUuid() { + return hostUuid; + } + + public String getHostName() { + return hostName; + } + public List getMsHostList() { return msHostList; } @@ -75,6 +91,14 @@ public class ReadyCommand extends Command { this.msHostList = msHostList; } + public List getAvoidMsHostList() { + return avoidMsHostList; + } + + public void setAvoidMsHostList(List msHostList) { + this.avoidMsHostList = avoidMsHostList; + } + public String getLbAlgorithm() { return lbAlgorithm; } @@ -94,4 +118,12 @@ public class ReadyCommand extends Command { 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 00000000000..154b611be98 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RecreateCheckpointsCommand.java @@ -0,0 +1,49 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public class RecreateCheckpointsCommand extends Command { + + private List volumes; + + private String vmName; + + public RecreateCheckpointsCommand(List volumes, String vmName) { + this.volumes = volumes; + this.vmName = vmName; + } + + public List getDisks() { + return volumes; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RemoveBitmapCommand.java b/core/src/main/java/com/cloud/agent/api/RemoveBitmapCommand.java new file mode 100644 index 00000000000..90b62e3f17b --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RemoveBitmapCommand.java @@ -0,0 +1,46 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.agent.api; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public class RemoveBitmapCommand extends Command { + + private SnapshotObjectTO snapshotObjectTO; + + private boolean isVmRunning; + + public RemoveBitmapCommand(SnapshotObjectTO snapshotObjectTO, boolean isVmRunning) { + this.snapshotObjectTO = snapshotObjectTO; + this.isVmRunning = isVmRunning; + } + + @Override + public boolean executeInSequence() { + return true; + } + + public SnapshotObjectTO getSnapshotObjectTO() { + return snapshotObjectTO; + } + + public boolean isVmRunning() { + return isVmRunning; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RunCustomActionAnswer.java b/core/src/main/java/com/cloud/agent/api/RunCustomActionAnswer.java new file mode 100644 index 00000000000..1deb789c995 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RunCustomActionAnswer.java @@ -0,0 +1,32 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +public class RunCustomActionAnswer extends Answer { + + public RunCustomActionAnswer(RunCustomActionCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java b/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java new file mode 100644 index 00000000000..36489ad4fa5 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java @@ -0,0 +1,59 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +import java.util.Map; + +public class RunCustomActionCommand extends Command { + + String actionName; + Long vmId; + Map parameters; + + public RunCustomActionCommand(String actionName) { + this.actionName = actionName; + this.setWait(5); + } + + public String getActionName() { + return actionName; + } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java b/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java index 06583f2d0d3..fef0ceed6b8 100644 --- a/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java +++ b/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java @@ -36,6 +36,7 @@ public class SetupGuestNetworkCommand extends NetworkElementCommand { String routerIpv6Gateway = null; String routerIpv6Cidr = null; boolean isVrGuestGateway = false; + long networkId; public NicTO getNic() { return nic; @@ -123,4 +124,12 @@ public class SetupGuestNetworkCommand extends NetworkElementCommand { public void setVrGuestGateway(boolean vrGuestGateway) { isVrGuestGateway = vrGuestGateway; } + + public long getNetworkId() { + return networkId; + } + + public void setNetworkId(long networkId) { + this.networkId = networkId; + } } diff --git a/core/src/main/java/com/cloud/agent/api/StartupAnswer.java b/core/src/main/java/com/cloud/agent/api/StartupAnswer.java index 71652269b66..c619ce75ace 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 @@ package com.cloud.agent.api; 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 class StartupAnswer extends Answer { 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 5f2c00d0be6..7a18ba2dccc 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 class StartupCommand extends Command { 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 b4f9d20df5e..068196aabe5 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 class StartupRoutingCommand extends StartupCommand { 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 class StartupRoutingCommand extends StartupCommand { this.groupDetails = groupDetails; } + public List getGpuDevices() { + return gpuDevices; + } + + public void setGpuDevices(List gpuDevices) { + this.gpuDevices = gpuDevices; + } + public boolean getSupportsClonedVolumes() { return supportsClonedVolumes; } @@ -197,4 +211,12 @@ public class StartupRoutingCommand extends StartupCommand { 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 3923a35bd0a..d07ffa2e31f 100644 --- a/core/src/main/java/com/cloud/agent/api/StopCommand.java +++ b/core/src/main/java/com/cloud/agent/api/StopCommand.java @@ -19,14 +19,14 @@ package com.cloud.agent.api; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import com.cloud.agent.api.to.DpdkTO; import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.vm.VirtualMachine; -import java.util.ArrayList; -import java.util.Map; -import java.util.List; - public class StopCommand extends RebootCommand { private boolean isProxy = false; private String urlPort = null; @@ -37,6 +37,7 @@ public class StopCommand extends RebootCommand { boolean forceStop = false; private Map dpdkInterfaceMapping; Map vlanToPersistenceMap; + boolean expungeVM = false; public Map getDpdkInterfaceMapping() { return dpdkInterfaceMapping; @@ -138,4 +139,12 @@ public class StopCommand extends RebootCommand { public void setVlanToPersistenceMap(Map vlanToPersistenceMap) { this.vlanToPersistenceMap = vlanToPersistenceMap; } + + public boolean isExpungeVM() { + return expungeVM; + } + + public void setExpungeVM(boolean expungeVM) { + this.expungeVM = expungeVM; + } } diff --git a/core/src/main/java/com/cloud/agent/api/TransferAgentCommand.java b/core/src/main/java/com/cloud/agent/api/TransferAgentCommand.java index ab74d9bcf85..9c6b3b5fc59 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 class TransferAgentCommand extends Command { 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 class TransferAgentCommand extends Command { return currentOwner; } + public boolean isConnectionTransfer() { + return isConnectionTransfer; + } + @Override public boolean executeInSequence() { return false; 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 00000000000..1280293db0d --- /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 00000000000..b98ce6fa345 --- /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 00000000000..9645b300db5 --- /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 00000000000..36371a196c8 --- /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/CreateDiskOnlyVmSnapshotAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java new file mode 100644 index 00000000000..4d61249c7cb --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.utils.Pair; + +import java.util.Map; + +public class CreateDiskOnlyVmSnapshotAnswer extends Answer { + + protected Map> mapVolumeToSnapshotSizeAndNewVolumePath; + + public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map> mapVolumeToSnapshotSizeAndNewVolumePath) { + super(command, success, details); + this.mapVolumeToSnapshotSizeAndNewVolumePath = mapVolumeToSnapshotSizeAndNewVolumePath; + } + + public Map> getMapVolumeToSnapshotSizeAndNewVolumePath() { + return mapVolumeToSnapshotSizeAndNewVolumePath; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java new file mode 100644 index 00000000000..952bf0c971d --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + + +import com.cloud.agent.api.VMSnapshotBaseCommand; +import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public class CreateDiskOnlyVmSnapshotCommand extends VMSnapshotBaseCommand { + + protected VirtualMachine.State vmState; + + public CreateDiskOnlyVmSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType, VirtualMachine.State vmState) { + super(vmName, snapshot, volumeTOs, guestOSType); + this.vmState = vmState; + } + + public VirtualMachine.State getVmState() { + return vmState; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CreateEntityDownloadURLCommand.java b/core/src/main/java/com/cloud/agent/api/storage/CreateEntityDownloadURLCommand.java index c84eceb0ca2..33403580b6f 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 @@ import com.cloud.agent.api.to.DataTO; 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 class CreateEntityDownloadURLCommand extends AbstractDownloadCommand { 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 class CreateEntityDownloadURLCommand extends AbstractDownloadCommand { 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 00000000000..bf7bdd59736 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/DeleteDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Command; + +import com.cloud.agent.api.to.DataTO; + + +import java.util.List; + +public class DeleteDiskOnlyVmSnapshotCommand extends Command { + + List snapshots; + + public DeleteDiskOnlyVmSnapshotCommand(List snapshots) { + this.snapshots = snapshots; + } + + public List getSnapshots() { + return snapshots; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java new file mode 100644 index 00000000000..b6396c24d10 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Command; +import com.cloud.vm.VirtualMachine; + +import java.util.List; + +public class MergeDiskOnlyVmSnapshotCommand extends Command { + + private List snapshotMergeTreeToList; + private VirtualMachine.State vmState; + private String vmName; + + public MergeDiskOnlyVmSnapshotCommand(List snapshotMergeTreeToList, VirtualMachine.State vmState, String vmName) { + this.snapshotMergeTreeToList = snapshotMergeTreeToList; + this.vmState = vmState; + this.vmName = vmName; + } + + public List getSnapshotMergeTreeToList() { + return snapshotMergeTreeToList; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return false; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java b/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java index 9fed0f913e1..70375c30a1b 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java +++ b/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java @@ -145,4 +145,9 @@ public class MigrateVolumeCommand extends Command { } public String getChainInfo() { return chainInfo; } + + @Override + public boolean isReconcile() { + return true; + } } diff --git a/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotAnswer.java new file mode 100644 index 00000000000..2ecf587d59d --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotAnswer.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public class RevertDiskOnlyVmSnapshotAnswer extends Answer { + List volumeObjectTos; + + public RevertDiskOnlyVmSnapshotAnswer(Command cmd, List volumeObjectTos) { + super(cmd, true, null); + this.volumeObjectTos = volumeObjectTos; + } + + public List getVolumeObjectTos() { + return volumeObjectTos; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java new file mode 100644 index 00000000000..72bb92bcb10 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +import java.util.List; + +public class RevertDiskOnlyVmSnapshotCommand extends Command { + + private List snapshotObjectTos; + private String vmName; + + public RevertDiskOnlyVmSnapshotCommand(List snapshotObjectTos, String vmName) { + super(); + this.snapshotObjectTos = snapshotObjectTos; + this.vmName = vmName; + } + + public List getSnapshotObjectTos() { + return snapshotObjectTos; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return false; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java b/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java new file mode 100644 index 00000000000..78f23105e19 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.to.DataTO; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; + +import java.util.List; + +public class SnapshotMergeTreeTO { + DataTO parent; + DataTO child; + List grandChildren; + + public SnapshotMergeTreeTO(DataTO parent, DataTO child, List grandChildren) { + this.parent = parent; + this.child = child; + this.grandChildren = grandChildren; + } + + public DataTO getParent() { + return parent; + } + + public DataTO getChild() { + return child; + } + + public List getGrandChildren() { + return grandChildren; + } + + public void addGrandChild(DataTO grandChild) { + grandChildren.add(grandChild); + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this); + } +} diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java index ebe5e9a7ec9..7bfbf786e9b 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java @@ -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 3c86b3a0dcc..bd632632ae8 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -34,6 +34,8 @@ import java.util.concurrent.locks.ReentrantLock; import javax.naming.ConfigurationException; +import com.cloud.agent.api.HandleCksIsoCommand; +import org.apache.cloudstack.agent.routing.ManageServiceCommand; import com.cloud.agent.api.routing.UpdateNetworkCommand; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.network.router.VirtualRouter; @@ -144,6 +146,14 @@ public class VirtualRoutingResource { 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 class VirtualRoutingResource { } } + protected Answer execute(final HandleCksIsoCommand cmd) { + String routerIp = getRouterSshControlIp(cmd); + logger.info("Attempting to mount CKS ISO on Virtual Router"); + ExecutionResult result = _vrDeployer.executeInVR(routerIp, VRScripts.CKS_ISO_MOUNT_SERVE, String.valueOf(cmd.isMountCksIso())); + return new Answer(cmd, result.isSuccess(), result.getDetails()); + } + private Answer execute(final SetupKeyStoreCommand cmd) { final String args = String.format("/usr/local/cloud/systemvm/conf/agent.properties " + "/usr/local/cloud/systemvm/conf/%s " + @@ -271,6 +288,20 @@ public class VirtualRoutingResource { 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 46dd801bebf..83dfa2a62ca 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.LoadBalancerConfigCommand; 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/SetBgpPeersConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItem.java new file mode 100644 index 00000000000..68f4275bb6b --- /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 1a6824ceb7f..57c96ec4bc9 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java @@ -76,6 +76,7 @@ public class SetGuestNetworkConfigItem extends AbstractConfigItemFacade { guestNetwork.setRouterIp6Gateway(command.getRouterIpv6Gateway()); guestNetwork.setRouterIp6Cidr(command.getRouterIpv6Cidr()); guestNetwork.setVrGuestGateway(command.isVrGuestGateway()); + guestNetwork.setNetworkId(command.getNetworkId()); return generateConfigItems(guestNetwork); } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetPortForwardingRulesConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetPortForwardingRulesConfigItem.java index c9d0e74e2e4..4daef64ed8a 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 class SetPortForwardingRulesConfigItem extends AbstractConfigItemFacade { 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 00000000000..54a1ab2e091 --- /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 ade80d71384..e370b764f22 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 cf3e43d1c01..1dc1cc855c4 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 class ForwardingRule { 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 a416b4bc5e4..6bf36d62bf9 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java @@ -38,6 +38,7 @@ public class GuestNetwork extends ConfigBase { private String routerIp6Gateway; private String routerIp6Cidr; private boolean isVrGuestGateway; + long networkId; private Integer mtu; @@ -211,4 +212,12 @@ public class GuestNetwork extends ConfigBase { public void setVrGuestGateway(boolean vrGuestGateway) { isVrGuestGateway = vrGuestGateway; } + + public long getNetworkId() { + return networkId; + } + + public void setNetworkId(long networkId) { + this.networkId = networkId; + } } diff --git a/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java b/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java index 7229f0903b7..eabcc82040d 100644 --- a/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java +++ b/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java @@ -75,13 +75,17 @@ public class ArrayTypeAdaptor implements JsonDeserializer, JsonSerialize try { clazz = Class.forName(name); } catch (ClassNotFoundException e) { - throw new CloudRuntimeException("can't find " + name); + throw new JsonParseException("can't find " + name); } T cmd = (T)_gson.fromJson(entry.getValue(), clazz); cmds.add(cmd); } - Class type = ((Class)typeOfT).getComponentType(); - T[] ts = (T[])Array.newInstance(type, cmds.size()); - return cmds.toArray(ts); + try { + Class type = Class.forName(typeOfT.getTypeName().replace("[]", "")); + T[] ts = (T[])Array.newInstance(type, cmds.size()); + return cmds.toArray(ts); + } catch (ClassNotFoundException e) { + throw new CloudRuntimeException("can't find " + typeOfT.getTypeName()); + } } } diff --git a/core/src/main/java/com/cloud/network/HAProxyConfigurator.java b/core/src/main/java/com/cloud/network/HAProxyConfigurator.java index 9d07fc95c2f..e4b0a7ffff4 100644 --- a/core/src/main/java/com/cloud/network/HAProxyConfigurator.java +++ b/core/src/main/java/com/cloud/network/HAProxyConfigurator.java @@ -485,7 +485,7 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { sb.append("\tbind ").append(publicIP).append(":").append(publicPort); result.add(sb.toString()); sb = new StringBuilder(); - sb.append("\t").append("balance ").append(algorithm); + sb.append("\t").append("balance ").append(algorithm.toLowerCase()); result.add(sb.toString()); int i = 0; diff --git a/core/src/main/java/com/cloud/resource/CommandWrapper.java b/core/src/main/java/com/cloud/resource/CommandWrapper.java index a839234117b..72d1348dfe7 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 1602a78d9a4..23d200942a2 100644 --- a/core/src/main/java/com/cloud/resource/ServerResource.java +++ b/core/src/main/java/com/cloud/resource/ServerResource.java @@ -22,6 +22,7 @@ package com.cloud.resource; import com.cloud.agent.IAgentControl; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingAnswer; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.host.Host; @@ -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 2d2cecf2618..7de98c08b7e 100644 --- a/core/src/main/java/com/cloud/serializer/GsonHelper.java +++ b/core/src/main/java/com/cloud/serializer/GsonHelper.java @@ -62,7 +62,7 @@ public class GsonHelper { LOGGER.info("Default Builder inited."); } - static Gson setDefaultGsonConfig(GsonBuilder builder) { + public static Gson setDefaultGsonConfig(GsonBuilder builder) { builder.setVersion(1.5); InterfaceTypeAdaptor dsAdaptor = new InterfaceTypeAdaptor(); builder.registerTypeAdapter(DataStoreTO.class, dsAdaptor); diff --git a/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java b/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java index 7d8225462ca..58885210120 100644 --- a/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java +++ b/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java @@ -142,7 +142,7 @@ public class StorageSubsystemCommandHandlerBase implements StorageSubsystemComma } return new CreateObjectAnswer("not supported type"); } catch (Exception e) { - logger.debug("Failed to create object: " + data.getObjectType() + ": " + e.toString()); + logger.error("Failed to create object [{}] due to [{}].", data.getObjectType(), e.getMessage(), e); return new CreateObjectAnswer(e.toString()); } } diff --git a/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java b/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java index 9b126846827..4c056f256cf 100755 --- a/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java @@ -27,6 +27,7 @@ import java.io.InputStream; import java.io.RandomAccessFile; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.Date; import java.util.List; @@ -80,6 +81,18 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te private ResourceType resourceType = ResourceType.TEMPLATE; private final HttpMethodRetryHandler myretryhandler; private boolean followRedirects = false; + private boolean isChunkedTransfer; + + protected static final List CUSTOM_HEADERS_FOR_CHUNKED_TRANSFER_SIZE = Arrays.asList( + "x-goog-stored-content-length", + "x-goog-meta-size", + "x-amz-meta-size", + "x-amz-meta-content-length", + "x-object-meta-size", + "x-original-content-length", + "x-oss-meta-content-length", + "x-file-size"); + private static final long MIN_FORMAT_VERIFICATION_SIZE = 1024 * 1024; public HttpTemplateDownloader(StorageLayer storageLayer, String downloadUrl, String toDir, DownloadCompleteCallback callback, long maxTemplateSizeInBytes, String user, String password, Proxy proxy, ResourceType resourceType) { @@ -205,13 +218,11 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te RandomAccessFile out = new RandomAccessFile(file, "rw"); ) { out.seek(localFileSize); - - logger.info("Starting download from " + downloadUrl + " to " + toFile + " remoteSize=" + toHumanReadableSize(remoteSize) + " , max size=" + toHumanReadableSize(maxTemplateSizeInBytes)); - - if (copyBytes(file, in, out)) return 0; - + logger.info("Starting download from {} to {} remoteSize={} , max size={}",downloadUrl, toFile, + toHumanReadableSize(remoteSize), toHumanReadableSize(maxTemplateSizeInBytes)); + boolean eof = copyBytes(file, in, out); Date finish = new Date(); - checkDowloadCompletion(); + checkDownloadCompletion(eof); downloadTime += finish.getTime() - start.getTime(); } finally { /* in.close() and out.close() */ } return totalBytes; @@ -237,28 +248,32 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te } private boolean copyBytes(File file, InputStream in, RandomAccessFile out) throws IOException { - int bytes; - byte[] block = new byte[CHUNK_SIZE]; + byte[] buffer = new byte[CHUNK_SIZE]; long offset = 0; - boolean done = false; VerifyFormat verifyFormat = new VerifyFormat(file); status = Status.IN_PROGRESS; - while (!done && status != Status.ABORTED && offset <= remoteSize) { - if ((bytes = in.read(block, 0, CHUNK_SIZE)) > -1) { - offset = writeBlock(bytes, out, block, offset); - if (!ResourceType.SNAPSHOT.equals(resourceType) && - !verifyFormat.isVerifiedFormat() && - (offset >= 1048576 || offset >= remoteSize)) { //let's check format after we get 1MB or full file - verifyFormat.invoke(); - } - } else { - done = true; + while (status != Status.ABORTED) { + int bytesRead = in.read(buffer, 0, CHUNK_SIZE); + if (bytesRead == -1) { + logger.debug("Reached EOF on input stream"); + break; + } + offset = writeBlock(bytesRead, out, buffer, offset); + if (!ResourceType.SNAPSHOT.equals(resourceType) + && !verifyFormat.isVerifiedFormat() + && (offset >= MIN_FORMAT_VERIFICATION_SIZE || offset >= remoteSize)) { + verifyFormat.invoke(); + } + if (offset >= remoteSize) { + logger.debug("Reached expected remote size limit: {} bytes", remoteSize); + break; } } out.getFD().sync(); - return false; + return !Status.ABORTED.equals(status); } + private long writeBlock(int bytes, RandomAccessFile out, byte[] block, long offset) throws IOException { out.write(block, 0, bytes); offset += bytes; @@ -267,11 +282,13 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te return offset; } - private void checkDowloadCompletion() { + private void checkDownloadCompletion(boolean eof) { String downloaded = "(incomplete download)"; - if (totalBytes >= remoteSize) { + if (eof && ((totalBytes >= remoteSize) || (isChunkedTransfer && remoteSize == maxTemplateSizeInBytes))) { status = Status.DOWNLOAD_FINISHED; - downloaded = "(download complete remote=" + toHumanReadableSize(remoteSize) + " bytes)"; + downloaded = "(download complete remote=" + + (remoteSize == maxTemplateSizeInBytes ? toHumanReadableSize(remoteSize) : "unknown") + + " bytes)"; } errorString = "Downloaded " + toHumanReadableSize(totalBytes) + " bytes " + downloaded; } @@ -293,18 +310,42 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te } } + protected long getRemoteSizeForChunkedTransfer() { + for (String headerKey : CUSTOM_HEADERS_FOR_CHUNKED_TRANSFER_SIZE) { + Header header = request.getResponseHeader(headerKey); + if (header == null) { + continue; + } + try { + return Long.parseLong(header.getValue()); + } catch (NumberFormatException ignored) {} + } + Header contentRangeHeader = request.getResponseHeader("Content-Range"); + if (contentRangeHeader != null) { + String contentRange = contentRangeHeader.getValue(); + if (contentRange != null && contentRange.contains("/")) { + String totalSize = contentRange.substring(contentRange.indexOf('/') + 1).trim(); + return Long.parseLong(totalSize); + } + } + return 0; + } + private boolean tryAndGetRemoteSize() { Header contentLengthHeader = request.getResponseHeader("content-length"); - boolean chunked = false; + isChunkedTransfer = false; long reportedRemoteSize = 0; if (contentLengthHeader == null) { Header chunkedHeader = request.getResponseHeader("Transfer-Encoding"); - if (chunkedHeader == null || !"chunked".equalsIgnoreCase(chunkedHeader.getValue())) { + if (chunkedHeader != null && "chunked".equalsIgnoreCase(chunkedHeader.getValue())) { + isChunkedTransfer = true; + reportedRemoteSize = getRemoteSizeForChunkedTransfer(); + logger.debug("{} is using chunked transfer encoding, possible remote size: {}", downloadUrl, + reportedRemoteSize); + } else { status = Status.UNRECOVERABLE_ERROR; errorString = " Failed to receive length of download "; return false; - } else if ("chunked".equalsIgnoreCase(chunkedHeader.getValue())) { - chunked = true; } } else { reportedRemoteSize = Long.parseLong(contentLengthHeader.getValue()); @@ -316,9 +357,11 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te return false; } } - if (remoteSize == 0) { remoteSize = reportedRemoteSize; + if (remoteSize != 0) { + logger.debug("Remote size for {} found to be {}", downloadUrl, toHumanReadableSize(remoteSize)); + } } return true; } diff --git a/core/src/main/java/com/cloud/storage/template/TemplateConstants.java b/core/src/main/java/com/cloud/storage/template/TemplateConstants.java index d6622bed73e..349997da1b7 100644 --- a/core/src/main/java/com/cloud/storage/template/TemplateConstants.java +++ b/core/src/main/java/com/cloud/storage/template/TemplateConstants.java @@ -24,7 +24,7 @@ public final class TemplateConstants { public static final String DEFAULT_SNAPSHOT_ROOT_DIR = "snapshots"; public static final String DEFAULT_VOLUME_ROOT_DIR = "volumes"; public static final String DEFAULT_TMPLT_FIRST_LEVEL_DIR = "tmpl/"; - + public static final String DEFAULT_CHECKPOINT_ROOT_DIR = "checkpoints"; public static final String DEFAULT_SYSTEM_VM_TEMPLATE_PATH = "template/tmpl/1/"; public static final int DEFAULT_TMPLT_COPY_PORT = 80; diff --git a/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java b/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java index 50cf956c9e7..864a3e22eb3 100644 --- a/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java +++ b/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java @@ -26,20 +26,28 @@ import com.cloud.agent.api.Command; public class SetupMSListCommand extends Command { private List msList; + private List avoidMsList; private String lbAlgorithm; private Long lbCheckInterval; + private Boolean triggerHostLb; - public SetupMSListCommand(final List msList, final String lbAlgorithm, final Long lbCheckInterval) { + public SetupMSListCommand(final List msList, final List avoidMsList, final String lbAlgorithm, final Long lbCheckInterval, final Boolean triggerHostLb) { super(); this.msList = msList; + this.avoidMsList = avoidMsList; this.lbAlgorithm = lbAlgorithm; this.lbCheckInterval = lbCheckInterval; + this.triggerHostLb = triggerHostLb; } public List getMsList() { return msList; } + public List getAvoidMsList() { + return avoidMsList; + } + public String getLbAlgorithm() { return lbAlgorithm; } @@ -48,9 +56,12 @@ public class SetupMSListCommand extends Command { return lbCheckInterval; } + public boolean getTriggerHostLb() { + return triggerHostLb; + } + @Override public boolean executeInSequence() { return false; } - } diff --git a/core/src/main/java/org/apache/cloudstack/agent/routing/ManageServiceCommand.java b/core/src/main/java/org/apache/cloudstack/agent/routing/ManageServiceCommand.java new file mode 100644 index 00000000000..c83a5b69574 --- /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 00000000000..ffc67b628a7 --- /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 00000000000..eabf6877ba6 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/BackupStorageStatsAnswer.java @@ -0,0 +1,50 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.backup; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class BackupStorageStatsAnswer extends Answer { + private Long totalSize; + private Long usedSize; + + public BackupStorageStatsAnswer(final Command command, final boolean success, final String details) { + super(command, success, details); + this.totalSize = 0L; + this.usedSize = 0L; + } + + public Long getTotalSize() { + return totalSize; + } + + public void setTotalSize(Long totalSize) { + this.totalSize = totalSize; + } + + public Long getUsedSize() { + return usedSize; + } + + public void setUsedSize(Long usedSize) { + this.usedSize = usedSize; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/DeleteBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/DeleteBackupCommand.java new file mode 100644 index 00000000000..16c611af998 --- /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 00000000000..1ceeac17e52 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/GetBackupStorageStatsCommand.java @@ -0,0 +1,66 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.backup; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; + +public class GetBackupStorageStatsCommand extends Command { + private String backupRepoType; + private String backupRepoAddress; + @LogLevel(LogLevel.Log4jLevel.Off) + private String mountOptions; + + public GetBackupStorageStatsCommand(String backupRepoType, String backupRepoAddress, String mountOptions) { + super(); + this.backupRepoType = backupRepoType; + this.backupRepoAddress = backupRepoAddress; + this.mountOptions = mountOptions; + } + + public String getBackupRepoType() { + return backupRepoType; + } + + public void setBackupRepoType(String backupRepoType) { + this.backupRepoType = backupRepoType; + } + + public String getBackupRepoAddress() { + return backupRepoAddress; + } + + public void setBackupRepoAddress(String backupRepoAddress) { + this.backupRepoAddress = backupRepoAddress; + } + + public String getMountOptions() { + return mountOptions == null ? "" : mountOptions; + } + + public void setMountOptions(String mountOptions) { + this.mountOptions = mountOptions; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java new file mode 100644 index 00000000000..f447fbe3d00 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.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.backup; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; +import com.cloud.vm.VirtualMachine; + +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 restoreVolumePaths; + private String diskType; + private Boolean vmExists; + private String restoreVolumeUUID; + private VirtualMachine.State vmState; + + 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 getRestoreVolumePaths() { + return restoreVolumePaths; + } + + public void setRestoreVolumePaths(List restoreVolumePaths) { + this.restoreVolumePaths = restoreVolumePaths; + } + + 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 String getRestoreVolumeUUID() { + return restoreVolumeUUID; + } + + public void setRestoreVolumeUUID(String restoreVolumeUUID) { + this.restoreVolumeUUID = restoreVolumeUUID; + } + + 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; + } +} 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 00000000000..ecebd57a178 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.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.backup; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; + +import java.util.List; + +public class TakeBackupCommand extends Command { + private String vmName; + private String backupPath; + private String backupRepoType; + private String backupRepoAddress; + 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 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 00000000000..b9bb702e345 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/CommandInfo.java @@ -0,0 +1,124 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.command; + +import com.cloud.agent.api.Command; +import com.cloud.serializer.GsonHelper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.Date; + +public class CommandInfo { + public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSSZ"; + public static final Gson GSON = GsonHelper.setDefaultGsonConfig(new GsonBuilder().setDateFormat(DATE_FORMAT)); + + long requestSeq; + Command.State state; + Date startTime; + Date updateTime; + String commandName; + String command; + int timeout; + String answerName; + String answer; + + public CommandInfo() { + } + + public CommandInfo(long requestSeq, Command command, Command.State state) { + this.requestSeq = requestSeq; + this.state = state; + this.startTime = this.updateTime = new Date(); + this.commandName = command.getClass().getName(); + this.command = GSON.toJson(command); + this.timeout = command.getWait(); + } + + public long getRequestSeq() { + return requestSeq; + } + + public void setRequestSeq(long requestSeq) { + this.requestSeq = requestSeq; + } + + public Command.State getState() { + return state; + } + + public void setState(Command.State state) { + this.state = state; + this.updateTime = new Date(); + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getCommandName() { + return commandName; + } + + public void setCommandName(String commandName) { + this.commandName = commandName; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public String getAnswerName() { + return answerName; + } + + public void setAnswerName(String answerName) { + this.answerName = answerName; + } + + public String getAnswer() { + return answer; + } + + public void setAnswer(String answer) { + this.answer = answer; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileAnswer.java new file mode 100644 index 00000000000..e8d27e1fa71 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileAnswer.java @@ -0,0 +1,45 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import com.cloud.agent.api.Answer; +import org.apache.cloudstack.api.ApiCommandResourceType; + +public class ReconcileAnswer extends Answer { + + ApiCommandResourceType resourceType; + Long resourceId; + + public ApiCommandResourceType getResourceType() { + return resourceType; + } + + public void setResourceType(ApiCommandResourceType resourceType) { + this.resourceType = resourceType; + } + + public Long getResourceId() { + return resourceId; + } + + public void setResourceId(Long resourceId) { + this.resourceId = resourceId; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommand.java new file mode 100644 index 00000000000..262aefb30ac --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommand.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command; + +import com.cloud.agent.api.Command; + +public class ReconcileCommand extends Command { + + @Override + public boolean executeInSequence() { + return false; + } + + @Override + public int getWait() { + return 30; // timeout is 30 seconds + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCommandUtils.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommandUtils.java new file mode 100644 index 00000000000..8acc02a730f --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommandUtils.java @@ -0,0 +1,192 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.command; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.JsonSyntaxException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; + +public class ReconcileCommandUtils { + + protected static final Logger LOGGER = LogManager.getLogger(ReconcileCommandUtils.class.getName()); + + public static void createLogFileForCommand(final String logPath, final Command cmd) { + updateLogFileForCommand(logPath, cmd, Command.State.CREATED); + } + + public static void updateLogFileForCommand(final String logPath, final Command cmd, final Command.State state) { + if (cmd.isReconcile()) { + String logFileName = getLogFileNameForCommand(logPath, cmd); + LOGGER.debug(String.format("Updating log file %s with %s state", logFileName, state)); + File logFile = new File(logFileName); + CommandInfo commandInfo = null; + if (logFile.exists()) { + commandInfo = readLogFileForCommand(logFileName); + logFile.delete(); + } + if (commandInfo == null) { + commandInfo = new CommandInfo(cmd.getRequestSequence(), cmd, state); + } else { + commandInfo.setState(state); + } + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logFile)); + writer.write(CommandInfo.GSON.toJson(commandInfo)); + writer.close(); + } catch (IOException e) { + LOGGER.error(String.format("Failed to write log file %s", logFile)); + } + } + } + + public static void updateLogFileForCommand(final String logFullPath, final Command.State state) { + File logFile = new File(logFullPath); + LOGGER.debug(String.format("Updating log file %s with %s state", logFile.getName(), state)); + if (!logFile.exists()) { + return; + } + CommandInfo commandInfo = readLogFileForCommand(logFullPath); + if (commandInfo != null) { + commandInfo.setState(state); + } + logFile.delete(); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logFile)); + writer.write(CommandInfo.GSON.toJson(commandInfo)); + writer.close(); + } catch (IOException e) { + LOGGER.error(String.format("Failed to write log file %s", logFile)); + } + } + + public static void deleteLogFileForCommand(final String logPath, final Command cmd) { + if (cmd.isReconcile()) { + File logFile = new File(getLogFileNameForCommand(logPath, cmd)); + LOGGER.debug(String.format("Removing log file %s", logFile.getName())); + if (logFile.exists()) { + logFile.delete(); + } + } + } + + public static void deleteLogFile(final String logFullPath) { + File logFile = new File(logFullPath); + LOGGER.debug(String.format("Removing log file %s ", logFile.getName())); + if (logFile.exists()) { + logFile.delete(); + } + } + + public static String getLogFileNameForCommand(final String logPath, final Command cmd) { + return String.format("%s/%s-%s.json", logPath, cmd.getRequestSequence(), cmd); + } + + public static CommandInfo readLogFileForCommand(final String logFullPath) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleDateFormat df = new SimpleDateFormat(CommandInfo.DATE_FORMAT); + objectMapper.setDateFormat(df); + return objectMapper.readValue(new File(logFullPath), CommandInfo.class); + } catch (IOException e) { + LOGGER.error(String.format("Failed to read log file %s: %s", logFullPath, e.getMessage())); + return null; + } + } + + public static Command parseCommandInfo(final CommandInfo commandInfo) { + if (commandInfo.getCommandName() == null || commandInfo.getCommand() == null) { + return null; + } + return parseCommandInfo(commandInfo.getCommandName(), commandInfo.getCommand()); + } + + public static Command parseCommandInfo(final String commandName, final String commandInfo) { + Object parsedObject = null; + try { + Class commandClazz = Class.forName(commandName); + parsedObject = CommandInfo.GSON.fromJson(commandInfo, commandClazz); + } catch (ClassNotFoundException | JsonSyntaxException e) { + LOGGER.error(String.format("Failed to parse command from CommandInfo %s due to %s", commandInfo, e.getMessage())); + } + if (parsedObject != null) { + return (Command) parsedObject; + } + return null; + } + + public static Answer parseAnswerFromCommandInfo(final CommandInfo commandInfo) { + if (commandInfo.getAnswerName() == null || commandInfo.getAnswer() == null) { + return null; + } + return parseAnswerFromAnswerInfo(commandInfo.getAnswerName(), commandInfo.getAnswer()); + } + + public static Answer parseAnswerFromAnswerInfo(final String answerName, final String answerInfo) { + Object parsedObject = null; + try { + Class commandClazz = Class.forName(answerName); + parsedObject = CommandInfo.GSON.fromJson(answerInfo, commandClazz); + } catch (ClassNotFoundException | JsonSyntaxException e) { + LOGGER.error(String.format("Failed to parse answer from answerInfo %s due to %s", answerInfo, e.getMessage())); + } + if (parsedObject != null) { + return (Answer) parsedObject; + } + return null; + } + + public static void updateLogFileWithAnswerForCommand(final String logPath, final Command cmd, final Answer answer) { + if (cmd.isReconcile()) { + String logFileName = getLogFileNameForCommand(logPath, cmd); + LOGGER.debug(String.format("Updating log file %s with answer %s", logFileName, answer)); + File logFile = new File(logFileName); + if (!logFile.exists()) { + return; + } + CommandInfo commandInfo = readLogFileForCommand(logFile.getAbsolutePath()); + if (commandInfo == null) { + return; + } + if (Command.State.STARTED.equals(commandInfo.getState())) { + if (answer.getResult()) { + commandInfo.setState(Command.State.COMPLETED); + } else { + commandInfo.setState(Command.State.FAILED); + } + } + commandInfo.setAnswerName(answer.toString()); + commandInfo.setAnswer(CommandInfo.GSON.toJson(answer)); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logFile)); + writer.write(CommandInfo.GSON.toJson(commandInfo)); + writer.close(); + } catch (IOException e) { + LOGGER.error(String.format("Failed to write log file %s", logFile)); + } + } + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyAnswer.java new file mode 100644 index 00000000000..82a24fa7fa5 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyAnswer.java @@ -0,0 +1,56 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +public class ReconcileCopyAnswer extends ReconcileVolumeAnswer { + + boolean isSkipped = false; + String reason; + + public ReconcileCopyAnswer(boolean isSkipped, String reason) { + super(); + this.isSkipped = isSkipped; + this.reason = reason; + } + + public ReconcileCopyAnswer(boolean isSkipped, boolean result, String reason) { + super(); + this.isSkipped = isSkipped; + this.result = result; + this.reason = reason; + } + + public ReconcileCopyAnswer(VolumeOnStorageTO volumeOnSource, VolumeOnStorageTO volumeOnDestination) { + this.isSkipped = false; + this.result = true; + this.volumeOnSource = volumeOnSource; + this.volumeOnDestination = volumeOnDestination; + } + + public boolean isSkipped() { + return isSkipped; + } + + public String getReason() { + return reason; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyCommand.java new file mode 100644 index 00000000000..36a678833d0 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyCommand.java @@ -0,0 +1,53 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command; + +import com.cloud.agent.api.to.DataTO; + +import java.util.Map; + +public class ReconcileCopyCommand extends ReconcileCommand { + + DataTO srcData; + DataTO destData; + Map option; // details of source volume + Map option2; // details of destination volume + + public ReconcileCopyCommand(DataTO srcData, DataTO destData, Map option, Map option2) { + this.srcData = srcData; + this.destData = destData; + this.option = option; + this.option2 = option2; + } + + public DataTO getSrcData() { + return srcData; + } + + public DataTO getDestData() { + return destData; + } + + public Map getOption() { + return option; + } + + public Map getOption2() { + return option2; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateAnswer.java new file mode 100644 index 00000000000..6313267c7e4 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateAnswer.java @@ -0,0 +1,68 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import com.cloud.vm.VirtualMachine; + +import java.util.List; + +public class ReconcileMigrateAnswer extends ReconcileAnswer { + + Long hostId; + String vmName; + VirtualMachine.State vmState; + List vmDisks; + + public ReconcileMigrateAnswer() { + } + + public ReconcileMigrateAnswer(String vmName, VirtualMachine.State vmState) { + this.vmName = vmName; + this.vmState = vmState; + } + + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + public String getVmName() { + return vmName; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public void setVmState(VirtualMachine.State vmState) { + this.vmState = vmState; + } + + public List getVmDisks() { + return vmDisks; + } + + public void setVmDisks(List vmDisks) { + this.vmDisks = vmDisks; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateCommand.java new file mode 100644 index 00000000000..50e1c68a65f --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateCommand.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command; + +public class ReconcileMigrateCommand extends ReconcileCommand { + + String vmName; + + public ReconcileMigrateCommand(String vmName) { + this.vmName = vmName; + } + + public String getVmName() { + return vmName; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeAnswer.java new file mode 100644 index 00000000000..ebbd913f971 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeAnswer.java @@ -0,0 +1,50 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import java.util.List; + +public class ReconcileMigrateVolumeAnswer extends ReconcileVolumeAnswer { + + String vmName; + List vmDiskPaths; + + public ReconcileMigrateVolumeAnswer(VolumeOnStorageTO volumeOnSource, VolumeOnStorageTO volumeOnDestination) { + super(volumeOnSource, volumeOnDestination); + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public List getVmDiskPaths() { + return vmDiskPaths; + } + + public void setVmDiskPaths(List vmDiskPaths) { + this.vmDiskPaths = vmDiskPaths; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeCommand.java new file mode 100644 index 00000000000..e3e75249406 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeCommand.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command; + +import com.cloud.agent.api.to.DataTO; + +public class ReconcileMigrateVolumeCommand extends ReconcileCommand { + + DataTO srcData; + DataTO destData; + String vmName; + + public ReconcileMigrateVolumeCommand(DataTO srcData, DataTO destData) { + this.srcData = srcData; + this.destData = destData; + } + + public DataTO getSrcData() { + return srcData; + } + + public DataTO getDestData() { + return destData; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileVolumeAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileVolumeAnswer.java new file mode 100644 index 00000000000..da10bf23aee --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileVolumeAnswer.java @@ -0,0 +1,46 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +public class ReconcileVolumeAnswer extends ReconcileAnswer { + + // (1) null object: volume is not available. For example the source is secondary storage + // (2) otherwise, if volumeOnSource.getPath() is null, the volume cannot be found on primary storage pool + VolumeOnStorageTO volumeOnSource; + VolumeOnStorageTO volumeOnDestination; + + public ReconcileVolumeAnswer() { + } + + public ReconcileVolumeAnswer(VolumeOnStorageTO volumeOnSource, VolumeOnStorageTO volumeOnDestination) { + this.volumeOnSource = volumeOnSource; + this.volumeOnDestination = volumeOnDestination; + } + + public VolumeOnStorageTO getVolumeOnSource() { + return volumeOnSource; + } + + public VolumeOnStorageTO getVolumeOnDestination() { + return volumeOnDestination; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java b/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java index b8a25a11b5c..b84718e4f93 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java @@ -39,9 +39,7 @@ import java.util.Map; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import org.apache.cloudstack.utils.security.SSLUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.io.IOUtils; @@ -55,6 +53,7 @@ import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import com.cloud.utils.Pair; @@ -98,8 +97,8 @@ public class HttpsDirectTemplateDownloader extends DirectTemplateDownloaderImpl req = new HttpGet(downloadUrl); setFollowRedirects(this.isFollowRedirects()); if (MapUtils.isNotEmpty(headers)) { - for (String headerKey: headers.keySet()) { - req.setHeader(headerKey, headers.get(headerKey)); + for (Map.Entry entry : headers.entrySet()) { + req.setHeader(entry.getKey(), entry.getValue()); } } } @@ -120,10 +119,10 @@ public class HttpsDirectTemplateDownloader extends DirectTemplateDownloaderImpl String password = "changeit"; defaultKeystore.load(is, password.toCharArray()); } - TrustManager[] tm = HttpsMultiTrustManager.getTrustManagersFromKeyStores(customKeystore, defaultKeystore); - SSLContext sslContext = SSLUtils.getSSLContext(); - sslContext.init(null, tm, null); - return sslContext; + return SSLContexts.custom() + .loadTrustMaterial(customKeystore, null) + .loadTrustMaterial(defaultKeystore, null) + .build(); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | KeyManagementException e) { logger.error(String.format("Failure getting SSL context for HTTPS downloader, using default SSL context: %s", e.getMessage()), e); try { diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsMultiTrustManager.java b/core/src/main/java/org/apache/cloudstack/direct/download/HttpsMultiTrustManager.java deleted file mode 100644 index fe47847c36c..00000000000 --- a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsMultiTrustManager.java +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package org.apache.cloudstack.direct.download; - -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; - -public class HttpsMultiTrustManager implements X509TrustManager { - - private final List trustManagers; - - public HttpsMultiTrustManager(KeyStore... keystores) { - List trustManagers = new ArrayList<>(); - trustManagers.add(getTrustManager(null)); - for (KeyStore keystore : keystores) { - trustManagers.add(getTrustManager(keystore)); - } - this.trustManagers = ImmutableList.copyOf(trustManagers); - } - - public static TrustManager[] getTrustManagersFromKeyStores(KeyStore... keyStore) { - return new TrustManager[] { new HttpsMultiTrustManager(keyStore) }; - - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - for (X509TrustManager trustManager : trustManagers) { - try { - trustManager.checkClientTrusted(chain, authType); - return; - } catch (CertificateException ignored) {} - } - throw new CertificateException("None of the TrustManagers trust this certificate chain"); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - for (X509TrustManager trustManager : trustManagers) { - try { - trustManager.checkServerTrusted(chain, authType); - return; - } catch (CertificateException ignored) {} - } - throw new CertificateException("None of the TrustManagers trust this certificate chain"); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - ImmutableList.Builder certificates = ImmutableList.builder(); - for (X509TrustManager trustManager : trustManagers) { - for (X509Certificate cert : trustManager.getAcceptedIssuers()) { - certificates.add(cert); - } - } - return Iterables.toArray(certificates.build(), X509Certificate.class); - } - - public X509TrustManager getTrustManager(KeyStore keystore) { - return getTrustManager(TrustManagerFactory.getDefaultAlgorithm(), keystore); - } - - public X509TrustManager getTrustManager(String algorithm, KeyStore keystore) { - TrustManagerFactory factory; - try { - factory = TrustManagerFactory.getInstance(algorithm); - factory.init(keystore); - return Iterables.getFirst(Iterables.filter( - Arrays.asList(factory.getTrustManagers()), X509TrustManager.class), null); - } catch (NoSuchAlgorithmException | KeyStoreException e) { - e.printStackTrace(); - } - return null; - } -} diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java index aac082a0133..36550420909 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java @@ -93,4 +93,9 @@ public class CopyCommand extends StorageSubSystemCommand { public void setExecuteInSequence(final boolean inSeq) { executeInSequence = inSeq; } + + @Override + public boolean isReconcile() { + return true; + } } diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/ImageStoreTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/ImageStoreTO.java index 046a2ab9410..4bf29205673 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 org.apache.cloudstack.storage.image.datastore.ImageStoreInfo; 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 class ImageStoreTO implements DataStoreTO { @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 a6a74176c13..2c758fa5087 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 org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; 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 class PrimaryDataStoreTO implements DataStoreTO { @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 76b93909b8c..0c3bb99e75c 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java @@ -35,12 +35,16 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { private VolumeObjectTO volume; private String parentSnapshotPath; private DataStoreTO dataStore; + private DataStoreTO imageStore; + private boolean kvmIncrementalSnapshot = false; private String vmName; private String name; private HypervisorType hypervisorType; private long id; private boolean quiescevm; private String[] parents; + private DataStoreTO parentStore; + private String checkpointPath; private Long physicalSize = (long) 0; private long accountId; @@ -49,6 +53,11 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { } + @Override + public DataObjectType getObjectType() { + return DataObjectType.SNAPSHOT; + } + public SnapshotObjectTO(SnapshotInfo snapshot) { this.path = snapshot.getPath(); this.setId(snapshot.getId()); @@ -59,27 +68,28 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { this.setVmName(vol.getAttachedVmName()); } - SnapshotInfo parentSnapshot = snapshot.getParent(); - ArrayList parentsArry = new ArrayList(); - if (parentSnapshot != null) { - this.parentSnapshotPath = parentSnapshot.getPath(); - while(parentSnapshot != null) { - parentsArry.add(parentSnapshot.getPath()); - parentSnapshot = parentSnapshot.getParent(); - } - parents = parentsArry.toArray(new String[parentsArry.size()]); - ArrayUtils.reverse(parents); - } - this.dataStore = snapshot.getDataStore().getTO(); this.setName(snapshot.getName()); this.hypervisorType = snapshot.getHypervisorType(); this.quiescevm = false; - } - @Override - public DataObjectType getObjectType() { - return DataObjectType.SNAPSHOT; + this.checkpointPath = snapshot.getCheckpointPath(); + this.kvmIncrementalSnapshot = snapshot.isKvmIncrementalSnapshot(); + + SnapshotInfo parentSnapshot = snapshot.getParent(); + + if (parentSnapshot == null || (HypervisorType.KVM.equals(snapshot.getHypervisorType()) && !parentSnapshot.isKvmIncrementalSnapshot())) { + return; + } + + ArrayList parentsArray = new ArrayList<>(); + this.parentSnapshotPath = parentSnapshot.getPath(); + while (parentSnapshot != null) { + parentsArray.add(parentSnapshot.getPath()); + parentSnapshot = parentSnapshot.getParent(); + } + parents = parentsArray.toArray(new String[parentsArray.size()]); + ArrayUtils.reverse(parents); } @Override @@ -91,6 +101,30 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { this.dataStore = store; } + public DataStoreTO getImageStore() { + return imageStore; + } + + public void setImageStore(DataStoreTO imageStore) { + this.imageStore = imageStore; + } + + public boolean isKvmIncrementalSnapshot() { + return kvmIncrementalSnapshot; + } + + public void setKvmIncrementalSnapshot(boolean kvmIncrementalSnapshot) { + this.kvmIncrementalSnapshot = kvmIncrementalSnapshot; + } + + public String getCheckpointPath() { + return checkpointPath; + } + + public void setCheckpointPath(String checkpointPath) { + this.checkpointPath = checkpointPath; + } + @Override public String getPath() { return this.path; @@ -178,6 +212,14 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { this.accountId = accountId; } + public DataStoreTO getParentStore() { + return parentStore; + } + + public void setParentStore(DataStoreTO parentStore) { + this.parentStore = parentStore; + } + @Override public String toString() { return new StringBuilder("SnapshotTO[datastore=").append(dataStore).append("|volume=").append(volume).append("|path").append(path).append("]").toString(); diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/TemplateObjectTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/TemplateObjectTO.java index eafe8f83269..dc68b31a3fd 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,6 +27,7 @@ import com.cloud.agent.api.to.DataTO; 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 extends DownloadableObjectTO implements DataTO { private String path; @@ -264,6 +265,8 @@ public class TemplateObjectTO extends DownloadableObjectTO implements DataTO { @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 2bb67c80ce4..92f9ee497a4 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java @@ -30,8 +30,11 @@ import com.cloud.offering.DiskOffering.DiskCacheMode; 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 extends DownloadableObjectTO implements DataTO { private String uuid; @@ -39,6 +42,7 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { 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 extends DownloadableObjectTO implements DataTO { @LogLevel(LogLevel.Log4jLevel.Off) private byte[] passphrase; private String encryptFormat; + private List checkpointPaths; + private Set checkpointImageStoreUrls; public VolumeObjectTO() { @@ -120,6 +126,8 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { this.passphrase = volume.getPassphrase(); this.encryptFormat = volume.getEncryptFormat(); this.followRedirects = volume.isFollowRedirects(); + this.checkpointPaths = volume.getCheckpointPaths(); + this.checkpointImageStoreUrls = volume.getCheckpointImageStoreUrls(); } public String getUuid() { @@ -161,6 +169,10 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { return size; } + public Long getUsableSize() { + return usableSize; + } + @Override public DataObjectType getObjectType() { return DataObjectType.VOLUME; @@ -178,6 +190,10 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { this.size = size; } + public void setUsableSize(Long usableSize) { + this.usableSize = usableSize; + } + public void setPath(String path) { this.path = path; } @@ -249,7 +265,9 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { @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) { @@ -385,4 +403,21 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { public boolean requiresEncryption() { return passphrase != null && passphrase.length > 0; } + + + public List getCheckpointPaths() { + return checkpointPaths; + } + + public void setCheckpointPaths(List checkpointPaths) { + this.checkpointPaths = checkpointPaths; + } + + public Set getCheckpointImageStoreUrls() { + return checkpointImageStoreUrls; + } + + public void setCheckpointImageStoreUrls(Set checkpointImageStoreUrls) { + this.checkpointImageStoreUrls = checkpointImageStoreUrls; + } } diff --git a/core/src/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 a0d1b4cfd43..9f04a7fb618 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 ec3bb63aeb6..6d05f858303 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 91a35f18a89..995ed30eb5e 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 49775fe41e1..01c568d7891 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"> - @@ -358,4 +358,12 @@ + + + + + + + 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 00000000000..ab1f88e9844 --- /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 00000000000..63d11c65bac --- /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 df1a4b5c229..96a9a634bae 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 2240d1f2606..e5c232267ae 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/system/spring-core-system-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/system/spring-core-system-context-inheritable.xml index 4a59e95de35..89442fc12b4 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 00000000000..44af83ada2d --- /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 00000000000..26948f2b823 --- /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 00000000000..7b8a9114571 --- /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 00000000000..4cd15e4465a --- /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 00000000000..882c3b9da30 --- /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/facade/SetBgpPeersConfigItemTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItemTest.java new file mode 100644 index 00000000000..5f177c88abf --- /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 00000000000..eba423e55ed --- /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/transport/ResponseTest.java b/core/src/test/java/com/cloud/agent/transport/ResponseTest.java new file mode 100644 index 00000000000..06869b42eb9 --- /dev/null +++ b/core/src/test/java/com/cloud/agent/transport/ResponseTest.java @@ -0,0 +1,46 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.transport; + +import junit.framework.TestCase; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.junit.Assert; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer; + +import com.cloud.agent.transport.Request.Version; + +public class ResponseTest extends TestCase { + protected Logger logger = LogManager.getLogger(getClass()); + + public void testCheckS2SVpnConnectionsAnswer() { + logger.info("Testing CheckS2SVpnConnectionsAnswer"); + String content = "[{\"com.cloud.agent.api.CheckS2SVpnConnectionsAnswer\":{\"ipToConnected\":{\"10.0.53.13\":true}," + + "\"ipToDetail\":{\"10.0.53.13\":\"IPsec SA found;Site-to-site VPN have connected\"}," + + "\"details\":\"10.0.53.13:0:IPsec SA found;Site-to-site VPN have connected\\u0026\\n\"," + + "\"result\":true,\"contextMap\":{},\"wait\":0,\"bypassHostMaintenance\":false}}]"; + Response response = new Response(Version.v2, 1L, 2L, 3L, 1L, (short)1, content); + Answer answer = response.getAnswer(); + Assert.assertTrue(answer instanceof CheckS2SVpnConnectionsAnswer); + } + +} diff --git a/core/src/test/java/com/cloud/serializer/GsonHelperTest.java b/core/src/test/java/com/cloud/serializer/GsonHelperTest.java new file mode 100644 index 00000000000..e8b0b373060 --- /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 8ab54644718..8674a8df286 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 class OVAProcessorTest { 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,27 +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) { - if (data.size() > 0) { - def hypervisor = template.tokenize('-')[-1] - pom.properties["$hypervisor" + ".checksum"] = data[0].tokenize(' ')[0] + + + @@ -135,7 +146,7 @@ org.codehaus.mojo exec-maven-plugin - 1.2.1 + ${cs.exec-maven-plugin.version} systemvm-template-metadata @@ -154,6 +165,15 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + true + + + @@ -183,7 +203,7 @@ 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} @@ -219,7 +239,7 @@ 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} @@ -255,7 +275,7 @@ 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} @@ -291,7 +311,7 @@ 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} @@ -327,7 +347,7 @@ 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} 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 132fd3fe5a2..fb2d61d8e11 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java +++ b/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java @@ -135,8 +135,8 @@ public class CapacityVO implements Capacity { return podId; } - public void setPodId(long podId) { - this.podId = new Long(podId); + public void setPodId(Long podId) { + this.podId = podId; } @Override @@ -144,8 +144,8 @@ public class CapacityVO implements Capacity { return clusterId; } - public void setClusterId(long clusterId) { - this.clusterId = new Long(clusterId); + public void setClusterId(Long clusterId) { + this.clusterId = clusterId; } @Override @@ -254,6 +254,8 @@ public class CapacityVO implements Capacity { capacityNames.put(CAPACITY_TYPE_GPU, "GPU"); capacityNames.put(CAPACITY_TYPE_CPU_CORE, "CPU_CORE"); capacityNames.put(CAPACITY_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET, "VIRTUAL_NETWORK_IPV6_SUBNET"); + capacityNames.put(CAPACITY_TYPE_BACKUP_STORAGE, "BACKUP_STORAGE"); + capacityNames.put(CAPACITY_TYPE_OBJECT_STORAGE, "OBJECT_STORAGE"); } public static String getCapacityName (Short capacityType) { diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java index 9616f31d0c5..42313efa512 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java @@ -28,7 +28,9 @@ import com.cloud.utils.db.GenericDao; 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); @@ -46,7 +48,7 @@ public interface CapacityDao extends GenericDao { List findFilteredCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId, List hostIds, List poolIds); - List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam, short capacityType); + List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam); Pair, Map> orderPodsByAggregateCapacity(long zoneId, short capacityType); @@ -62,5 +64,11 @@ public interface CapacityDao extends GenericDao { float findClusterConsumption(Long clusterId, short capacityType, long computeRequested); - List orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityType); + Pair, Map> orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityType); + + List listHostCapacityByCapacityTypes(Long zoneId, Long clusterId, List capacityTypes); + + List listPodCapacityByCapacityTypes(Long zoneId, List capacityTypes); + + List listClusterCapacityByCapacityTypes(Long zoneId, Long podId, List capacityTypes); } diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java index 3acae985af4..91949bded50 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java @@ -204,9 +204,9 @@ public class CapacityDaoImpl extends GenericDaoBase implements "(CASE WHEN ISNULL(service_offering.speed) THEN custom_speed.value ELSE service_offering.speed end) AS speed, " + "(CASE WHEN ISNULL(service_offering.ram_size) THEN custom_ram_size.value ELSE service_offering.ram_size end) AS ram_size " + "FROM vm_instance vi LEFT JOIN service_offering ON(((vi.service_offering_id = service_offering.id))) " + - "LEFT JOIN user_vm_details custom_cpu ON(((custom_cpu.vm_id = vi.id) AND (custom_cpu.name = 'CpuNumber'))) " + - "LEFT JOIN user_vm_details custom_speed ON(((custom_speed.vm_id = vi.id) AND (custom_speed.name = 'CpuSpeed'))) " + - "LEFT JOIN user_vm_details custom_ram_size ON(((custom_ram_size.vm_id = vi.id) AND (custom_ram_size.name = 'memory'))) "; + "LEFT JOIN vm_instance_details custom_cpu ON(((custom_cpu.vm_id = vi.id) AND (custom_cpu.name = 'CpuNumber'))) " + + "LEFT JOIN vm_instance_details custom_speed ON(((custom_speed.vm_id = vi.id) AND (custom_speed.name = 'CpuSpeed'))) " + + "LEFT JOIN vm_instance_details custom_ram_size ON(((custom_ram_size.vm_id = vi.id) AND (custom_ram_size.name = 'memory'))) "; private static final String WHERE_STATE_IS_NOT_DESTRUCTIVE = "WHERE ISNULL(vi.removed) AND vi.state NOT IN ('Destroyed', 'Error', 'Expunging')"; @@ -672,7 +672,19 @@ public class CapacityDaoImpl extends GenericDaoBase implements } @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(); @@ -1016,10 +1028,11 @@ public class CapacityDaoImpl extends GenericDaoBase implements } @Override - public List orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityTypeForOrdering){ + public Pair, Map> orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityTypeForOrdering){ TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; - List result = new ArrayList(); + List result = new ArrayList<>(); + Map hostCapacityMap = new HashMap<>(); StringBuilder sql = new StringBuilder(ORDER_HOSTS_BY_FREE_CAPACITY_PART1); if (zoneId != null) { sql.append(" AND data_center_id = ?"); @@ -1042,9 +1055,11 @@ public class CapacityDaoImpl extends GenericDaoBase implements ResultSet rs = pstmt.executeQuery(); while (rs.next()) { - result.add(rs.getLong(1)); + Long hostId = rs.getLong(1); + result.add(hostId); + hostCapacityMap.put(hostId, rs.getDouble(2)); } - return result; + return new Pair<>(result, hostCapacityMap); } catch (SQLException e) { throw new CloudRuntimeException("DB Exception on: " + sql, e); } catch (Throwable e) { @@ -1053,7 +1068,65 @@ public class CapacityDaoImpl extends GenericDaoBase implements } @Override - public List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam, short capacityType) { + public List listHostCapacityByCapacityTypes(Long zoneId, Long clusterId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.and("capacityTypes", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.and("capacityState", sb.entity().getCapacityState(), Op.EQ); + sb.done(); + + SearchCriteria sc = sb.create(); + sc.setParameters("capacityState", "Enabled"); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (clusterId != null) { + sc.setParameters("clusterId", clusterId); + } + sc.setParameters("capacityTypes", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listPodCapacityByCapacityTypes(Long zoneId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("capacityTypes", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.and("capacityState", sb.entity().getCapacityState(), Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("capacityState", "Enabled"); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + sc.setParameters("capacityTypes", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listClusterCapacityByCapacityTypes(Long zoneId, Long podId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); + sb.and("capacityTypes", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.and("capacityState", sb.entity().getCapacityState(), Op.EQ); + sb.done(); + + SearchCriteria sc = sb.create(); + sc.setParameters("capacityState", "Enabled"); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (podId != null) { + sc.setParameters("podId", podId); + } + sc.setParameters("capacityTypes", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); diff --git a/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java b/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java index 51b7f62f56d..841447de5fd 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java +++ b/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java @@ -21,7 +21,7 @@ import org.apache.cloudstack.framework.config.Configurable; public interface ManagementServiceConfiguration extends Configurable { ConfigKey PingInterval = new ConfigKey("Advanced", Integer.class, "ping.interval", "60", - "Interval to send application level pings to make sure the connection is still working", false); + "Interval in seconds to send application level pings to make sure the connection is still working", false); ConfigKey PingTimeout = new ConfigKey("Advanced", Float.class, "ping.timeout", "2.5", "Multiplier to ping.interval before announcing an agent has timed out", true); public int getPingInterval(); diff --git a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java index b978cc04bfa..ebacbd8b3e5 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java +++ b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java @@ -62,20 +62,6 @@ public interface ResourceCountDao extends GenericDao { long removeEntriesByOwner(long ownerId, ResourceOwnerType ownerType); - /** - * Counts the number of CPU cores allocated for the given account. - * - * Side note: This method is not using the "resource_count" table. It is executing the actual count instead. - */ - long countCpuNumberAllocatedToAccount(long accountId); - - /** - * Counts the amount of memory allocated for the given account. - * - * Side note: This method is not using the "resource_count" table. It is executing the actual count instead. - */ - long countMemoryAllocatedToAccount(long accountId); - void removeResourceCountsForNonMatchingTags(Long ownerId, ResourceOwnerType ownerType, List types, List tags); List lockRows(Set ids); diff --git a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java index 65d7fed2d1a..2083fb422d2 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java @@ -336,22 +336,9 @@ public class ResourceCountDaoImpl extends GenericDaoBase + " END)) as total " + " from vm_instance vm " + " join service_offering so on so.id = vm.service_offering_id " - + " left join user_vm_details vmd on vmd.vm_id = vm.id and vmd.name = '%s' " + + " left join vm_instance_details vmd on vmd.vm_id = vm.id and vmd.name = '%s' " + " where vm.type = 'User' and state not in ('Destroyed', 'Error', 'Expunging') and display_vm = true and account_id = ? "; - @Override - public long countCpuNumberAllocatedToAccount(long accountId) { - String sqlCountCpuNumberAllocatedToAccount = String.format(baseSqlCountComputingResourceAllocatedToAccount, ResourceType.cpu, ResourceType.cpu, "cpuNumber"); - return executeSqlCountComputingResourcesForAccount(accountId, sqlCountCpuNumberAllocatedToAccount); - } - - @Override - public long countMemoryAllocatedToAccount(long accountId) { - String serviceOfferingRamSizeField = "ram_size"; - String sqlCountCpuNumberAllocatedToAccount = String.format(baseSqlCountComputingResourceAllocatedToAccount, serviceOfferingRamSizeField, serviceOfferingRamSizeField, "memory"); - return executeSqlCountComputingResourcesForAccount(accountId, sqlCountCpuNumberAllocatedToAccount); - } - private long executeSqlCountComputingResourcesForAccount(long accountId, String sqlCountComputingResourcesAllocatedToAccount) { TransactionLegacy tx = TransactionLegacy.currentTxn(); try { diff --git a/engine/schema/src/main/java/com/cloud/dc/ASNumberRangeVO.java b/engine/schema/src/main/java/com/cloud/dc/ASNumberRangeVO.java new file mode 100644 index 00000000000..3790213b3ad --- /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 00000000000..529d1cfb5fe --- /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 06c9c525504..27cea8d5c2d 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 c2058ad5644..4c752ff9b4f 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 class ClusterDetailsDaoImpl extends GenericDaoBase 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,8 +169,8 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase key) { - ClusterDetailsVO vo = findDetail(id, key.key()); + public String getConfigValue(long id, String key) { + ClusterDetailsVO vo = findDetail(id, key); return vo == null ? null : vo.getValue(); } @@ -161,4 +194,13 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase 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 6eb9e7466a7..b213f8f2594 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.GenerationType; 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 @@ public class ClusterDetailsVO implements InternalIdentity { } 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 class ClusterDetailsVO implements InternalIdentity { 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 90591690eb0..a18097db6d6 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java @@ -16,12 +16,14 @@ // 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; @@ -69,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; @@ -79,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; @@ -200,6 +210,23 @@ public class ClusterVO implements Cluster { 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 3d68cc3d9a8..874b05673eb 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 class DataCenterIpAddressVO implements InternalIdentity { 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 827b72b58b0..9b24e51a1a8 100644 --- a/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java @@ -142,6 +142,9 @@ public class DataCenterVO implements DataCenter { @Enumerated(value = EnumType.STRING) private DataCenter.Type type; + @Column(name = "storage_access_groups") + String storageAccessGroups; + @Override public String getDnsProvider() { return dnsProvider; @@ -485,6 +488,14 @@ public class DataCenterVO implements DataCenter { this.type = type; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + @Override public String toString() { return String.format("Zone {\"id\": \"%s\", \"name\": \"%s\", \"uuid\": \"%s\"}", id, name, uuid); diff --git a/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java b/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java index d9971815f5e..99ebcf2346c 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 javax.persistence.Table; 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 class HostPodVO implements Pod { 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 7423ded598f..c271325f3de 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.Id; 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 class VlanVO implements Vlan { @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 6390d923ed8..5a4a71f82e7 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 javax.persistence.Table; 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 class VmwareDatacenterVO implements VmwareDatacenter { @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 00000000000..192f6bbaf31 --- /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 00000000000..1d2adf4d424 --- /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 00000000000..3309a6f5fe5 --- /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 00000000000..4a4170685dc --- /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 ab9c5cab8c4..1745f5380e2 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 com.cloud.dc.ClusterVO; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.utils.db.GenericDao; - import java.util.List; import java.util.Map; -import java.util.Set; + +import com.cloud.cpu.CPU; +import com.cloud.dc.ClusterVO; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; public interface ClusterDao extends GenericDao { 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> listDistinctHypervisorsArchAcrossClusters(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 4d9bedba966..ea82a10f9c9 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.Func; 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 class ClusterDaoImpl extends GenericDaoBase implements C 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 class ClusterDaoImpl extends GenericDaoBase implements C 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 class ClusterDaoImpl extends GenericDaoBase implements C return findOneBy(sc); } - @Override - public List listByHyTypeWithoutGuid(String hyType) { - SearchCriteria sc = HyTypeWithoutGuidSearch.create(); - sc.setParameters("hypervisorType", hyType); - - return listBy(sc); - } - @Override public List listByDcHyType(long dcId, String hyType) { SearchCriteria sc = ZoneHyTypeSearch.create(); @@ -153,23 +162,26 @@ public class ClusterDaoImpl extends GenericDaoBase implements C 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()); } @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> listDistinctHypervisorsArchAcrossClusters(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.done(); + SearchCriteria sc = sb.create(); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); } - return hypers; + final List clusters = search(sc, null); + return clusters.stream() + .map(c -> new Pair<>(c.getHypervisorType(), c.getArch())) + .collect(Collectors.toList()); } @Override @@ -252,6 +264,23 @@ public class ClusterDaoImpl extends GenericDaoBase implements C 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 +304,7 @@ public class ClusterDaoImpl extends GenericDaoBase implements C } @Override - public List listAllClusters(Long zoneId) { + public List listAllClusterIds(Long zoneId) { SearchCriteria sc = ClusterIdSearch.create(); if (zoneId != null) { sc.setParameters("dataCenterId", zoneId); @@ -301,4 +330,77 @@ public class ClusterDaoImpl extends GenericDaoBase implements C 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/DataCenterDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java index dddbce31772..0ba88f39b23 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java @@ -117,4 +117,6 @@ public interface DataCenterDao extends GenericDao { List listAllZones(); List listByIds(List ids); + + List listDistinctStorageAccessGroups(String name, String keyword); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java index 2776b09c2a1..d8ab12e82e6 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java @@ -25,6 +25,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.db.GenericSearchBuilder; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -164,8 +165,8 @@ public class DataCenterDaoImpl extends GenericDaoBase implem } @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 class DataCenterDaoImpl extends GenericDaoBase implem } @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 class DataCenterDaoImpl extends GenericDaoBase implem } @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 class DataCenterDaoImpl extends GenericDaoBase implem } @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 class DataCenterDaoImpl extends GenericDaoBase implem sc.setParameters("ids", ids.toArray()); return listBy(sc); } + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDetailsDaoImpl.java index e36c8ebd6c7..bb03a96d02e 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 @@ -44,8 +44,8 @@ public class DataCenterDetailsDaoImpl extends ResourceDetailsDaoBase key) { - ResourceDetail vo = findDetail(id, key.key()); + public String getConfigValue(long id, String key) { + ResourceDetail vo = findDetail(id, key); return vo == null ? null : vo.getValue(); } 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 c23137095e6..ba01e31f80a 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 class DataCenterIpAddressDaoImpl extends GenericDaoBase 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 class DataCenterIpAddressDaoImpl extends GenericDaoBase result = listBy(sc); - return result.size(); + return getCount(sc); } public DataCenterIpAddressDaoImpl() { @@ -305,7 +304,7 @@ public class DataCenterIpAddressDaoImpl extends GenericDaoBase 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 b2e9b898606..2549a0555e8 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java @@ -34,4 +34,6 @@ public interface HostPodDao extends GenericDao { public List listAllPods(Long zoneId); public List listAllPodsByCidr(long zoneId, String cidr); + + List listDistinctStorageAccessGroups(String name, String keyword); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java index f1835067380..08901c9a61e 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java @@ -143,4 +143,36 @@ public class HostPodDaoImpl extends GenericDaoBase implements H return listBy(sc); } + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } + } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java index 84f38f05441..a6c267bb189 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java @@ -64,4 +64,6 @@ public interface VlanDao extends GenericDao { List listIpv6RangeByZoneIdAndVlanId(long zoneId, String vlanId); List listIpv6SupportingVlansByZone(long zoneId); + + List listVlansForExternalNetworkProvider(long zoneId, String detailKey); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java index 461a9a13b10..d9fad3cad12 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java @@ -26,6 +26,7 @@ import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.VlanDetailsVO; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -67,6 +68,8 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao protected SearchBuilder ZoneVlanIp6Search; protected SearchBuilder ZoneIp6Search; protected SearchBuilder ZoneVlansSearch; + protected SearchBuilder ProviderVlanSearch; + protected SearchBuilder VlanDetailsProviderSearch; protected SearchBuilder AccountVlanMapSearch; protected SearchBuilder DomainVlanMapSearch; @@ -79,6 +82,8 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao protected DomainVlanMapDao _domainVlanMapDao; @Inject protected IPAddressDao _ipAddressDao; + @Inject + protected VlanDetailsDao vlanDetailsDao; @Override public VlanVO findByZoneAndVlanId(long zoneId, String vlanId) { @@ -277,6 +282,19 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao ZoneVlansSearch.and("zoneId", ZoneVlansSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneVlansSearch.and("vlan", ZoneVlansSearch.entity().getVlanTag(), SearchCriteria.Op.IN); ZoneVlansSearch.done(); + + ProviderVlanSearch = createSearchBuilder(); + ProviderVlanSearch.and("removed", ProviderVlanSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + ProviderVlanSearch.and("dataCenterId", ProviderVlanSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + VlanDetailsProviderSearch = vlanDetailsDao.createSearchBuilder(); + VlanDetailsProviderSearch.and("name", VlanDetailsProviderSearch.entity().getName(), SearchCriteria.Op.EQ); + VlanDetailsProviderSearch.and("value", VlanDetailsProviderSearch.entity().getValue(), SearchCriteria.Op.EQ); + ProviderVlanSearch.join("VlanDetailsProviderSearch", VlanDetailsProviderSearch, ProviderVlanSearch.entity().getId(), + VlanDetailsProviderSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); + + VlanDetailsProviderSearch.done(); + ProviderVlanSearch.done(); + return result; } @@ -434,4 +452,13 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao return listBy(sc); } + @Override + public List listVlansForExternalNetworkProvider(long zoneId, String detailKey) { + SearchCriteria sc = ProviderVlanSearch.create(); + sc.setParameters("dataCenterId", zoneId); + sc.setJoinParameters("VlanDetailsProviderSearch", "name", detailKey); + sc.setJoinParameters("VlanDetailsProviderSearch", "value", "true"); + return search(sc, null); + } + } diff --git a/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java b/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java index df5a2283baa..6f803cc9f2f 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.GenerationType; 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 @@ public class DomainDetailVO implements InternalIdentity { } 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 class DomainDetailVO implements InternalIdentity { 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 4c36a3401ca..c950fa31c88 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.GenerationType; 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 class DomainVO implements Domain { @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 937d99f5052..3aee371da25 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 74f2932ca43..56d971bbe01 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 @@ package com.cloud.domain.dao; 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 class DomainDaoImpl extends GenericDaoBase implements Dom 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 51362cf885e..ae149ff4381 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 java.util.Map; 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 dad3fe9ad1e..5b4e4c591ff 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 java.util.Map; 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 class DomainDetailsDaoImpl extends GenericDaoBase i @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 class DomainDetailsDaoImpl extends GenericDaoBase i } @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 class DomainDetailsDaoImpl extends GenericDaoBase i break; } } - return vo == null ? null : vo.getValue(); + return vo == null ? null : getActualValue(vo); } } diff --git a/engine/schema/src/main/java/com/cloud/gpu/GpuCardVO.java b/engine/schema/src/main/java/com/cloud/gpu/GpuCardVO.java new file mode 100644 index 00000000000..2410077c84a --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/GpuCardVO.java @@ -0,0 +1,147 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.gpu; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gpu.GpuCard; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "gpu_card") +public class GpuCardVO implements GpuCard { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "device_id") + private String deviceId; + + @Column(name = "device_name") + private String deviceName; + + @Column(name = "name") + private String name; + + @Column(name = "vendor_name") + private String vendorName; + + @Column(name = "vendor_id") + private String vendorId; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public GpuCardVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public GpuCardVO(String deviceId, String deviceName, String name, String vendorName, String vendorId) { + this.uuid = UUID.randomUUID().toString(); + this.deviceId = deviceId; + this.deviceName = deviceName; + this.name = name; + this.vendorName = vendorName; + this.vendorId = vendorId; + this.created = new Date(); + } + + @Override + public String toString() { + return String.format("GPUCard %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "deviceId", "deviceName", "vendorId", "vendorName")); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getVendorName() { + return vendorName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + @Override + public String getVendorId() { + return vendorId; + } + + public void setVendorId(String vendorId) { + this.vendorId = vendorId; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public String getGroupName() { + return "Group of " + getVendorName() + " " + getDeviceName() + " GPUs"; + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/GpuDeviceVO.java b/engine/schema/src/main/java/com/cloud/gpu/GpuDeviceVO.java new file mode 100644 index 00000000000..ac20e74c360 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/GpuDeviceVO.java @@ -0,0 +1,200 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu; + +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.UUID; + +@Entity +@Table(name = "gpu_device") +public class GpuDeviceVO implements GpuDevice { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "card_id") + private long cardId; + + @Column(name = "vgpu_profile_id") + private long vgpuProfileId; + + @Column(name = "bus_address") + private String busAddress; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "vm_id") + private Long vmId; + + @Column(name = "type") + @Enumerated(value = EnumType.STRING) + private DeviceType type = DeviceType.PCI; + + @Column(name = "state") + @Enumerated(value = EnumType.STRING) + private State state = State.Free; + + @Column(name = "managed_state") + @Enumerated(value = EnumType.STRING) + private ManagedState managedState = ManagedState.Managed; + + @Column(name = "parent_gpu_device_id") + private Long parentGpuDeviceId; + + @Column(name = "numa_node") + private String numaNode; + + @Column(name = "pci_root") + private String pciRoot; + + public GpuDeviceVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public GpuDeviceVO(long cardId, long vgpuProfileId, String busAddress, long hostId, Long parentGpuDeviceId, + String numaNode, String pciRoot) { + this.uuid = UUID.randomUUID().toString(); + this.cardId = cardId; + this.vgpuProfileId = vgpuProfileId; + this.busAddress = busAddress; + this.hostId = hostId; + this.parentGpuDeviceId = parentGpuDeviceId; + this.numaNode = numaNode; + this.pciRoot = pciRoot; + } + + @Override + public String toString() { + return String.format("GpuDevice %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "cardId", "vgpuProfileId", "busAddress", "hostId", "vmId", + "parentGpuDeviceId", "numaNode", "pciRoot", "state", "resourceState")); + } + + @Override + public long getId() { + return id; + } + + public String getUuid() { + return uuid; + } + + public long getCardId() { + return cardId; + } + + public void setCardId(long cardId) { + this.cardId = cardId; + } + + public long getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(long vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public String getBusAddress() { + return busAddress; + } + + public void setBusAddress(String busAddress) { + this.busAddress = busAddress; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + @Override + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + public DeviceType getType() { + return type; + } + + public void setType(DeviceType type) { + this.type = type; + } + + public ManagedState getManagedState() { + return managedState; + } + + public void setManagedState(ManagedState managedState) { + this.managedState = managedState; + } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public Long getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public void setParentGpuDeviceId(Long parentGpuDeviceId) { + this.parentGpuDeviceId = parentGpuDeviceId; + } + + public String getNumaNode() { + return numaNode; + } + + public void setNumaNode(String numaNode) { + this.numaNode = numaNode; + } + + public String getPciRoot() { + return pciRoot; + } + + public void setPciRoot(String pciRoot) { + this.pciRoot = pciRoot; + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java b/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java index 5bbf90854ee..4944d51f1b4 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java +++ b/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java @@ -40,19 +40,19 @@ public class VGPUTypesVO implements InternalIdentity { private String vgpuType; @Column(name="video_ram") - private long videoRam; + private Long videoRam; @Column(name="max_heads") - private long maxHeads; + private Long maxHeads; @Column(name="max_resolution_x") - private long maxResolutionX; + private Long maxResolutionX; @Column(name="max_resolution_y") - private long maxResolutionY; + private Long maxResolutionY; @Column(name="max_vgpu_per_pgpu") - private long maxVgpuPerPgpu; + private Long maxVgpuPerPgpu; @Column(name="remaining_capacity") private long remainingCapacity; @@ -63,7 +63,7 @@ public class VGPUTypesVO implements InternalIdentity { protected VGPUTypesVO() { } - public VGPUTypesVO(long gpuGroupId, String vgpuType, long videoRam, long maxHeads, long maxResolutionX, long maxResolutionY, long maxVgpuPerPgpu, + public VGPUTypesVO(long gpuGroupId, String vgpuType, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY, Long maxVgpuPerPgpu, long remainingCapacity, long maxCapacity) { this.gpuGroupId = gpuGroupId; this.vgpuType = vgpuType; @@ -92,43 +92,43 @@ public class VGPUTypesVO implements InternalIdentity { this.vgpuType = vgpuType; } - public long getVideoRam() { + public Long getVideoRam() { return videoRam; } - public void setVideoRam(long videoRam) { + public void setVideoRam(Long videoRam) { this.videoRam = videoRam; } - public long getMaxHeads() { + public Long getMaxHeads() { return maxHeads; } - public void setMaxHeads(long maxHeads) { + public void setMaxHeads(Long maxHeads) { this.maxHeads = maxHeads; } - public long getMaxResolutionX() { + public Long getMaxResolutionX() { return maxResolutionX; } - public void setMaxResolutionX(long maxResolutionX) { + public void setMaxResolutionX(Long maxResolutionX) { this.maxResolutionX = maxResolutionX; } - public long getMaxResolutionY() { + public Long getMaxResolutionY() { return maxResolutionY; } - public void setMaxResolutionY(long maxResolutionY) { + public void setMaxResolutionY(Long maxResolutionY) { this.maxResolutionY = maxResolutionY; } - public long getMaxVgpuPerPgpu() { + public Long getMaxVgpuPerPgpu() { return maxVgpuPerPgpu; } - public void setMaxVgpuPerPgpu(long maxVgpuPerPgpu) { + public void setMaxVgpuPerPgpu(Long maxVgpuPerPgpu) { this.maxVgpuPerPgpu = maxVgpuPerPgpu; } diff --git a/engine/schema/src/main/java/com/cloud/gpu/VgpuProfileVO.java b/engine/schema/src/main/java/com/cloud/gpu/VgpuProfileVO.java new file mode 100644 index 00000000000..86f5eb94415 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/VgpuProfileVO.java @@ -0,0 +1,191 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.gpu; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gpu.VgpuProfile; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "vgpu_profile") +public class VgpuProfileVO implements VgpuProfile { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name; + + @Column(name = "description") + private String description; + + @Column(name = "card_id") + private Long cardId; + + @Column(name = "max_vgpu_per_pgpu") + private Long maxVgpuPerPgpu; + + @Column(name = "video_ram") + private Long videoRam; + + @Column(name = "max_heads") + private Long maxHeads; + + @Column(name = "max_resolution_x") + private Long maxResolutionX; + + @Column(name = "max_resolution_y") + private Long maxResolutionY; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public VgpuProfileVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public VgpuProfileVO(String name, String description, Long gpuCardId, Long maxVgpuPerPgpu) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.description = description; + this.cardId = gpuCardId; + this.maxVgpuPerPgpu = maxVgpuPerPgpu; + this.created = new Date(); + } + + + public VgpuProfileVO(String name, String description, Long gpuCardId, Long maxVgpuPerPgpu, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.description = description; + this.cardId = gpuCardId; + this.maxVgpuPerPgpu = maxVgpuPerPgpu; + this.videoRam = videoRam; + this.maxHeads = maxHeads; + this.maxResolutionX = maxResolutionX; + this.maxResolutionY = maxResolutionY; + this.created = new Date(); + } + + @Override + public String toString() { + return String.format("VgpuProfile %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "cardId")); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Long getCardId() { + return cardId; + } + + public void setCardId(Long cardId) { + this.cardId = cardId; + } + + @Override + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public void setMaxVgpuPerPgpu(Long maxVgpuPerPgpu) { + this.maxVgpuPerPgpu = maxVgpuPerPgpu; + } + + @Override + public Long getVideoRam() { + return videoRam; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + @Override + public Long getMaxHeads() { + return maxHeads; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + @Override + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + @Override + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDao.java new file mode 100644 index 00000000000..4463a690ae9 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDao.java @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface GpuCardDao extends GenericDao { + + /** + * Find GPU card by vendor and device id + * + * @param vendorId the vendor id + * @param deviceId the device id + * @return GpuCardVO + */ + GpuCardVO findByVendorIdAndDeviceId(String vendorId, String deviceId); + + Pair, Integer> searchAndCountGpuCards( + Long id, String keyword, String vendorId, String vendorName, + String deviceId, String deviceName, boolean activeOnly, Long startIndex, Long pageSize); +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDaoImpl.java new file mode 100644 index 00000000000..8aad85d4508 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDaoImpl.java @@ -0,0 +1,122 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.List; + +@Component +public class GpuCardDaoImpl extends GenericDaoBase implements GpuCardDao { + + private final SearchBuilder allFieldSearch; + + @Inject + private GpuDeviceDao gpuDeviceDao; + + public GpuCardDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and("name", allFieldSearch.entity().getName(), SearchCriteria.Op.EQ); + allFieldSearch.and("vendorId", allFieldSearch.entity().getVendorId(), SearchCriteria.Op.EQ); + allFieldSearch.and("vendorName", allFieldSearch.entity().getVendorName(), SearchCriteria.Op.EQ); + allFieldSearch.and("deviceId", allFieldSearch.entity().getDeviceId(), SearchCriteria.Op.EQ); + allFieldSearch.and("deviceName", allFieldSearch.entity().getDeviceName(), SearchCriteria.Op.EQ); + allFieldSearch.done(); + } + + @Override + public GpuCardVO findByVendorIdAndDeviceId(String vendorId, String deviceId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("vendorId", vendorId); + sc.setParameters("deviceId", deviceId); + return findOneBy(sc); + } + + @Override + public Pair, Integer> searchAndCountGpuCards(Long id, String keyword, String vendorId, + String vendorName, String deviceId, String deviceName, boolean activeOnly, Long startIndex, Long pageSize + ) { + + Filter searchFilter = new Filter(GpuCardVO.class, "id", true, startIndex, pageSize); + SearchBuilder sb = createSearchBuilder(); + + if (id != null) { + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + } + if (keyword != null) { + sb.op("nameKeyword", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("deviceNameKeyword", sb.entity().getDeviceName(), SearchCriteria.Op.LIKE); + sb.and("vendorNameKeyword", sb.entity().getVendorName(), SearchCriteria.Op.LIKE); + sb.cp(); + } + if (vendorId != null) { + sb.and("vendorId", sb.entity().getVendorId(), SearchCriteria.Op.EQ); + } + if (vendorName != null) { + sb.and("vendorName", sb.entity().getVendorName(), SearchCriteria.Op.EQ); + } + if (deviceId != null) { + sb.and("deviceId", sb.entity().getDeviceId(), SearchCriteria.Op.EQ); + } + if (deviceName != null) { + sb.and("deviceName", sb.entity().getDeviceName(), SearchCriteria.Op.EQ); + } + if (activeOnly) { + sb.and("ids", sb.entity().getId(), SearchCriteria.Op.IN); + } + sb.done(); + + // Build search criteria + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (keyword != null) { + sc.setParameters("nameKeyword", "%" + keyword + "%"); + sc.setParameters("deviceNameKeyword", "%" + keyword + "%"); + sc.setParameters("vendorNameKeyword", "%" + keyword + "%"); + } + if (vendorId != null) { + sc.setParameters("vendorId", vendorId); + } + if (vendorName != null) { + sc.setParameters("vendorName", vendorName); + } + if (deviceId != null) { + sc.setParameters("deviceId", deviceId); + } + if (deviceName != null) { + sc.setParameters("deviceName", deviceName); + } + if (activeOnly) { + List cardIds = gpuDeviceDao.getDistinctGpuCardIds(); + if (cardIds.isEmpty()) { + return new Pair<>(List.of(), 0); + } + sc.setParameters("ids", cardIds.toArray()); + } + + return searchAndCount(sc, searchFilter); + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDao.java new file mode 100644 index 00000000000..e362f23888d --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDao.java @@ -0,0 +1,71 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuDeviceVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface GpuDeviceDao extends GenericDao { + + List listByIds(List ids); + + /** + * Find GPU device by host ID and bus address + * + * @param hostId the host ID + * @param busAddress the PCI bus address + * @return GpuDeviceVO + */ + GpuDeviceVO findByHostIdAndBusAddress(long hostId, String busAddress); + + /** + * List GPU devices by host ID + * + * @param hostId the ID of the host + * @return a list of GPU devices for the host + */ + List listByHostId(long hostId); + + /** + * List GPU devices by VM ID + * + * @param vmId the VM ID + * @return list of GpuDeviceVO + */ + List listByVmId(long vmId); + + boolean isVgpuProfileInUse(long vgpuProfileId); + + boolean isGpuCardInUse(long cardId); + + List listByHostAndVm(Long hostId, long vmId); + + List listDevicesForAllocation(Long hostId, Long vgpuProfileId); + + Pair, Integer> searchAndCountGpuDevices( + Long id, String keyword, Long hostId, Long vmId, Long gpuCardId, Long vgpuProfileId, + Long startIndex, Long pageSize); + + List getDistinctGpuCardIds(); + + List getDistinctVgpuProfileIds(); + + List listByParentGpuDeviceId(Long parentGpuDeviceId); +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDaoImpl.java new file mode 100644 index 00000000000..bd7032aff27 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDaoImpl.java @@ -0,0 +1,260 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.gpu.GpuDeviceVO; +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +public class GpuDeviceDaoImpl extends GenericDaoBase implements GpuDeviceDao { + + private static final String IDS = "ids"; + private static final String HOST_ID = "hostId"; + private static final String VM_ID = "vmId"; + private static final String BUS_ADDRESS = "busAddress"; + private static final String CARD_ID = "cardId"; + private static final String VGPU_PROFILE_ID = "vgpuProfileId"; + private static final String PARENT_GPU_DEVICE_ID = "parentGpuDeviceId"; + private static final String STATE = "state"; + private static final String MANAGED_STATE = "managedState"; + private static final String TYPE = "type"; + private final SearchBuilder allFieldSearch; + private SearchBuilder devicesForAllocationSearch; + @Inject + private GpuCardDao gpuCardDao; + @Inject + private VgpuProfileDao vgpuProfileDao; + + public GpuDeviceDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and(IDS, allFieldSearch.entity().getId(), SearchCriteria.Op.IN); + allFieldSearch.and(HOST_ID, allFieldSearch.entity().getHostId(), SearchCriteria.Op.EQ); + allFieldSearch.and(CARD_ID, allFieldSearch.entity().getCardId(), SearchCriteria.Op.EQ); + allFieldSearch.and(BUS_ADDRESS, allFieldSearch.entity().getBusAddress(), SearchCriteria.Op.EQ); + allFieldSearch.and(STATE, allFieldSearch.entity().getState(), SearchCriteria.Op.EQ); + allFieldSearch.and(VGPU_PROFILE_ID, allFieldSearch.entity().getVgpuProfileId(), SearchCriteria.Op.EQ); + allFieldSearch.and(PARENT_GPU_DEVICE_ID, allFieldSearch.entity().getParentGpuDeviceId(), SearchCriteria.Op.EQ); + allFieldSearch.and(VM_ID, allFieldSearch.entity().getVmId(), SearchCriteria.Op.EQ); + allFieldSearch.done(); + + devicesForAllocationSearch = createSearchBuilder(); + devicesForAllocationSearch.and(HOST_ID, devicesForAllocationSearch.entity().getHostId(), SearchCriteria.Op.EQ); + devicesForAllocationSearch.and(VGPU_PROFILE_ID, devicesForAllocationSearch.entity().getVgpuProfileId(), SearchCriteria.Op.IN); + devicesForAllocationSearch.and(STATE, devicesForAllocationSearch.entity().getState(), SearchCriteria.Op.EQ); + devicesForAllocationSearch.and(MANAGED_STATE, devicesForAllocationSearch.entity().getManagedState(), SearchCriteria.Op.EQ); + devicesForAllocationSearch.and(TYPE, devicesForAllocationSearch.entity().getType(), SearchCriteria.Op.NEQ); + devicesForAllocationSearch.done(); + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + return super.configure(name, params); + } + + @Override + public List listByIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return Collections.emptyList(); + } + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(IDS, ids.toArray()); + return listBy(sc); + } + + @Override + public GpuDeviceVO findByHostIdAndBusAddress(long hostId, String busAddress) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(HOST_ID, hostId); + sc.setParameters(BUS_ADDRESS, busAddress); + return findOneBy(sc); + } + + @Override + public List listByHostId(long hostId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(HOST_ID, hostId); + return listBy(sc); + } + + @Override + public List listByVmId(long vmId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(VM_ID, vmId); + return listBy(sc); + } + + @Override + public boolean isVgpuProfileInUse(long vgpuProfileId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(VGPU_PROFILE_ID, vgpuProfileId); + return getCount(sc) > 0; + } + + @Override + public boolean isGpuCardInUse(long cardId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(CARD_ID, cardId); + return getCount(sc) > 0; + } + + @Override + public List listByHostAndVm(Long hostId, long vmId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(HOST_ID, hostId); + sc.setParameters(VM_ID, vmId); + return search(sc, null); + } + + @Override + public List listDevicesForAllocation(Long hostId, Long vgpuProfileId) { + SearchCriteria sc = devicesForAllocationSearch.create(); + sc.setParameters(HOST_ID, hostId); + sc.setParameters(VGPU_PROFILE_ID, vgpuProfileId); + sc.setParameters(STATE, GpuDevice.State.Free); + sc.setParameters(MANAGED_STATE, GpuDevice.ManagedState.Managed); + sc.setParameters(TYPE, GpuDevice.DeviceType.VGPUOnly); + return search(sc, null); + } + + @Override + public Pair, Integer> searchAndCountGpuDevices(Long id, String keyword, Long hostId, Long vmId, + Long gpuCardId, Long vgpuProfileId, Long startIndex, Long pageSize) { + Filter searchFilter = new Filter(GpuDeviceVO.class, "id", true, startIndex, pageSize); + SearchBuilder sb = createSearchBuilder(); + + if (id != null) { + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + } + if (hostId != null) { + sb.and("hostId", sb.entity().getHostId(), SearchCriteria.Op.EQ); + } + if (vmId != null) { + sb.and("vmId", sb.entity().getVmId(), SearchCriteria.Op.EQ); + } + if (gpuCardId != null) { + sb.and("cardId", sb.entity().getCardId(), SearchCriteria.Op.EQ); + } + if (vgpuProfileId != null) { + sb.and("vgpuProfileId", sb.entity().getVgpuProfileId(), SearchCriteria.Op.EQ); + } + if (keyword != null) { + SearchBuilder cardSb = gpuCardDao.createSearchBuilder(); + SearchBuilder profileSb = vgpuProfileDao.createSearchBuilder(); + sb.join("cardJoin", cardSb, sb.entity().getCardId(), cardSb.entity().getId(), JoinBuilder.JoinType.INNER); + sb.join("profileJoin", profileSb, sb.entity().getCardId(), profileSb.entity().getId(), + JoinBuilder.JoinType.INNER); + + sb.op("cardNameKeyword", cardSb.entity().getName(), SearchCriteria.Op.LIKE); + sb.or("cardNameKeyword", cardSb.entity().getVendorName(), SearchCriteria.Op.LIKE); + sb.or("cardNameKeyword", cardSb.entity().getDeviceName(), SearchCriteria.Op.LIKE); + + sb.op("profileNameKeyword", profileSb.entity().getName(), SearchCriteria.Op.LIKE); + sb.op("profileDescriptionKeyword", profileSb.entity().getDescription(), SearchCriteria.Op.LIKE); + sb.cp(); + } + + sb.done(); + + // Build search criteria + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (hostId != null) { + sc.setParameters("hostId", hostId); + } + if (vmId != null) { + sc.setParameters("vmId", vmId); + } + if (gpuCardId != null) { + sc.setParameters("cardId", gpuCardId); + } + if (vgpuProfileId != null) { + sc.setParameters("vgpuProfileId", vgpuProfileId); + } + + if (keyword != null) { + sc.setJoinParameters("cardJoin", "cardNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("cardJoin", "cardNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("cardJoin", "cardNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("profileJoin", "profileNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("profileJoin", "profileDescriptionKeyword", "%" + keyword + "%"); + } + + return searchAndCount(sc, searchFilter); + } + + @Override + public List getDistinctGpuCardIds() { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getCardId()); + sb.done(); + SearchCriteria sc = sb.create(); + + List gpuDevices = listBy(sc); + if (CollectionUtils.isEmpty(gpuDevices)) { + return Collections.emptyList(); + } + + return gpuDevices.stream() + .map(GpuDeviceVO::getCardId) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public List getDistinctVgpuProfileIds() { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getVgpuProfileId()); + sb.done(); + SearchCriteria sc = sb.create(); + + List gpuDevices = listBy(sc); + if (CollectionUtils.isEmpty(gpuDevices)) { + return Collections.emptyList(); + } + + return gpuDevices.stream() + .map(GpuDeviceVO::getVgpuProfileId) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public List listByParentGpuDeviceId(Long parentGpuDeviceId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(PARENT_GPU_DEVICE_ID, parentGpuDeviceId); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java index 8e4f2f742ac..99e33617539 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java @@ -19,6 +19,7 @@ package com.cloud.gpu.dao; import java.util.List; import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; public interface HostGpuGroupsDao extends GenericDao { @@ -57,4 +58,15 @@ public interface HostGpuGroupsDao extends GenericDao { */ void persist(long hostId, List gpuGroups); + + /** + * Returns max and remaining GPU capacity + * + * @param dcId + * @param podId + * @param clusterId + * @param hostId + * @return Pair containing max GPU capacity and remaining GPU capacity + */ + Pair getGpuStats(Long dcId, Long podId, Long clusterId, Long hostId); } diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java index 30535c7e27d..343b144597c 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java @@ -16,9 +16,16 @@ // under the License. package com.cloud.gpu.dao; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; +import com.cloud.utils.Pair; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; import org.springframework.stereotype.Component; import com.cloud.gpu.HostGpuGroupsVO; @@ -87,4 +94,75 @@ public class HostGpuGroupsDaoImpl extends GenericDaoBase sc.setParameters("hostId", hostId); remove(sc); } + + @Override + public Pair getGpuStats(Long dcId, Long podId, Long clusterId, Long hostId) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + Pair result = null; + List resourceIdList = new ArrayList<>(); + String query = getStatsQuery(resourceIdList, dcId, podId, clusterId, hostId); + + try { + PreparedStatement pstmt = txn.prepareAutoCloseStatement(query); + for (int i = 0; i < resourceIdList.size(); i++) { + pstmt.setLong(1 + i, resourceIdList.get(i)); + } + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + result = new Pair<>(rs.getLong(1), rs.getLong(2)); + } + return result; + } catch (SQLException e) { + throw new CloudRuntimeException("Error while fetching GPU stats: " + e.getMessage(), e); + } catch (Throwable e) { + throw new CloudRuntimeException("Caught: " + query, e); + } + } + + private String getStatsQuery(List resourceIdList, Long dcId, Long podId, Long clusterId, Long hostId) { + StringBuilder query = new StringBuilder("SELECT SUM(max_capacity), SUM(remaining_capacity)" + + "FROM vgpu_types " + + "WHERE" + + " gpu_group_id IN (" + + " SELECT" + + " host_gpu_groups.id" + + " FROM" + + " host_gpu_groups" + + " INNER JOIN host ON host.id = host_gpu_groups.host_id "); + if (dcId != null) { + query.append("WHERE host.data_center_id = ? "); + resourceIdList.add(dcId); + } + + if (podId != null) { + if (resourceIdList.isEmpty()) { + query.append("WHERE "); + } else { + query.append("AND "); + } + query.append(" host.pod_id = ? "); + resourceIdList.add(podId); + } + if (clusterId != null) { + if (resourceIdList.isEmpty()) { + query.append("WHERE "); + } else { + query.append("AND "); + } + query.append(" host.cluster_id = ? "); + resourceIdList.add(clusterId); + } + if (hostId != null) { + if (resourceIdList.isEmpty()) { + query.append("WHERE "); + } else { + query.append("AND "); + } + query.append(" host.id = ? "); + resourceIdList.add(hostId); + } + query.append(" )"); + return query.toString(); + } } diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java index edc5e1f67c8..524feed2467 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java @@ -16,6 +16,17 @@ //under the License. package com.cloud.gpu.dao; +import com.cloud.agent.api.VgpuTypesInfo; +import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.gpu.VGPUTypesVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -25,42 +36,30 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import com.cloud.agent.api.VgpuTypesInfo; -import com.cloud.gpu.HostGpuGroupsVO; -import com.cloud.gpu.VGPUTypesVO; -import com.cloud.utils.db.GenericDaoBase; -import com.cloud.utils.db.SearchBuilder; -import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.TransactionLegacy; -import com.cloud.utils.exception.CloudRuntimeException; - @Component public class VGPUTypesDaoImpl extends GenericDaoBase implements VGPUTypesDao { - private final SearchBuilder _searchByGroupId; - private final SearchBuilder _searchByGroupIdVGPUType; - - @Inject protected HostGpuGroupsDao _hostGpuGroupsDao; + @Inject + protected HostGpuGroupsDao hostGpuGroupsDao; private static final String LIST_ZONE_POD_CLUSTER_WIDE_GPU_CAPACITIES = "SELECT host_gpu_groups.group_name, vgpu_type, max_vgpu_per_pgpu, SUM(remaining_capacity) AS remaining_capacity, SUM(max_capacity) AS total_capacity FROM" + - " `cloud`.`vgpu_types` INNER JOIN `cloud`.`host_gpu_groups` ON vgpu_types.gpu_group_id = host_gpu_groups.id INNER JOIN `cloud`.`host`" + - " ON host_gpu_groups.host_id = host.id WHERE host.type = 'Routing' AND host.data_center_id = ?"; + " `cloud`.`vgpu_types` INNER JOIN `cloud`.`host_gpu_groups` ON vgpu_types.gpu_group_id = host_gpu_groups.id INNER JOIN `cloud`.`host`" + + " ON host_gpu_groups.host_id = host.id WHERE host.type = 'Routing' AND vgpu_types.max_capacity > 0 AND host.data_center_id = ?"; + + private final SearchBuilder searchByGroupId; + private final SearchBuilder searchByGroupIdVGPUType; public VGPUTypesDaoImpl() { - _searchByGroupId = createSearchBuilder(); - _searchByGroupId.and("groupId", _searchByGroupId.entity().getGpuGroupId(), SearchCriteria.Op.EQ); - _searchByGroupId.done(); + searchByGroupId = createSearchBuilder(); + searchByGroupId.and("groupId", searchByGroupId.entity().getGpuGroupId(), SearchCriteria.Op.EQ); + searchByGroupId.done(); - _searchByGroupIdVGPUType = createSearchBuilder(); - _searchByGroupIdVGPUType.and("groupId", _searchByGroupIdVGPUType.entity().getGpuGroupId(), SearchCriteria.Op.EQ); - _searchByGroupIdVGPUType.and("vgpuType", _searchByGroupIdVGPUType.entity().getVgpuType(), SearchCriteria.Op.EQ); - _searchByGroupIdVGPUType.done(); + searchByGroupIdVGPUType = createSearchBuilder(); + searchByGroupIdVGPUType.and("groupId", searchByGroupIdVGPUType.entity().getGpuGroupId(), SearchCriteria.Op.EQ); + searchByGroupIdVGPUType.and("vgpuType", searchByGroupIdVGPUType.entity().getVgpuType(), SearchCriteria.Op.EQ); + searchByGroupIdVGPUType.done(); } @Override @@ -83,7 +82,7 @@ public class VGPUTypesDaoImpl extends GenericDaoBase implemen finalQuery.append(" AND host.cluster_id = ?"); resourceIdList.add(clusterId); } - finalQuery.append(" GROUP BY host_gpu_groups.group_name, vgpu_type"); + finalQuery.append(" GROUP BY host_gpu_groups.group_name, vgpu_type, max_vgpu_per_pgpu"); try { pstmt = txn.prepareAutoCloseStatement(finalQuery.toString()); @@ -106,14 +105,14 @@ public class VGPUTypesDaoImpl extends GenericDaoBase implemen @Override public List listByGroupId(long groupId) { - SearchCriteria sc = _searchByGroupId.create(); + SearchCriteria sc = searchByGroupId.create(); sc.setParameters("groupId", groupId); return listBy(sc); } @Override public VGPUTypesVO findByGroupIdVGPUType(long groupId, String vgpuType) { - SearchCriteria sc = _searchByGroupIdVGPUType.create(); + SearchCriteria sc = searchByGroupIdVGPUType.create(); sc.setParameters("groupId", groupId); sc.setParameters("vgpuType", vgpuType); return findOneBy(sc); @@ -124,7 +123,7 @@ public class VGPUTypesDaoImpl extends GenericDaoBase implemen Iterator>> it1 = groupDetails.entrySet().iterator(); while (it1.hasNext()) { Entry> entry = it1.next(); - HostGpuGroupsVO gpuGroup = _hostGpuGroupsDao.findByHostIdGroupName(hostId, entry.getKey()); + HostGpuGroupsVO gpuGroup = hostGpuGroupsDao.findByHostIdGroupName(hostId, entry.getKey()); HashMap values = entry.getValue(); Iterator> it2 = values.entrySet().iterator(); while (it2.hasNext()) { diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDao.java new file mode 100644 index 00000000000..2628f1851f2 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDao.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface VgpuProfileDao extends GenericDao { + + VgpuProfileVO findByNameAndCardId(String name, long cardId); + + int removeByCardId(long cardId); + + Pair, Integer> searchAndCountVgpuProfiles(Long id, String name, String keyword, Long gpuCardId, + boolean activeOnly, Long startIndex, Long pageSize); +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDaoImpl.java new file mode 100644 index 00000000000..11dd7edb30d --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDaoImpl.java @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.List; + +@Component +public class VgpuProfileDaoImpl extends GenericDaoBase implements VgpuProfileDao { + + private final SearchBuilder allFieldSearch; + + @Inject + private GpuDeviceDao gpuDeviceDao; + + public VgpuProfileDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and("name", allFieldSearch.entity().getName(), SearchCriteria.Op.EQ); + allFieldSearch.and("cardId", allFieldSearch.entity().getCardId(), SearchCriteria.Op.IN); + allFieldSearch.done(); + } + + @Override + public VgpuProfileVO findByNameAndCardId(String name, long cardId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("name", name); + sc.setParameters("cardId", cardId); + return findOneBy(sc); + } + + @Override + public int removeByCardId(long cardId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("cardId", cardId); + return remove(sc); + } + + @Override + public Pair, Integer> searchAndCountVgpuProfiles(Long id, String name, String keyword, + Long gpuCardId, boolean activeOnly, Long startIndex, Long pageSize) { + Filter searchFilter = new Filter(VgpuProfileVO.class, "id", true, startIndex, pageSize); + SearchBuilder sb = createSearchBuilder(); + + if (id != null) { + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + } + if (name != null) { + sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + } + if (keyword != null) { + sb.and("keywordName", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("keywordDescription", sb.entity().getDescription(), SearchCriteria.Op.LIKE); + } + if (gpuCardId != null) { + sb.and("cardId", sb.entity().getCardId(), SearchCriteria.Op.EQ); + } + if (activeOnly) { + sb.and("ids", sb.entity().getId(), SearchCriteria.Op.IN); + } + sb.done(); + + // Build search criteria + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (name != null) { + sc.setParameters("name", name); + } + if (keyword != null) { + sc.setParameters("keywordName", "%" + keyword + "%"); + sc.setParameters("keywordDescription", "%" + keyword + "%"); + } + if (gpuCardId != null) { + sc.setParameters("cardId", gpuCardId); + } + + if (activeOnly) { + List vgpuProfileIds = gpuDeviceDao.getDistinctVgpuProfileIds(); + if (vgpuProfileIds.isEmpty()) { + return new Pair<>(List.of(), 0); + } + sc.setParameters("ids", vgpuProfileIds.toArray()); + } + + return searchAndCount(sc, searchFilter); + } +} diff --git a/engine/schema/src/main/java/com/cloud/host/HostTagVO.java b/engine/schema/src/main/java/com/cloud/host/HostTagVO.java index cd4ac29738d..98071a2c073 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 class HostTagVO implements InternalIdentity { 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 3e64d20d0e2..d51b4eca057 100644 --- a/engine/schema/src/main/java/com/cloud/host/HostVO.java +++ b/engine/schema/src/main/java/com/cloud/host/HostVO.java @@ -16,13 +16,13 @@ // under the License. package com.cloud.host; -import java.util.ArrayList; 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; @@ -42,9 +42,12 @@ import javax.persistence.Temporal; 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; @@ -152,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. @@ -350,6 +360,15 @@ public class HostVO implements Host { return isTagARule; } + @Override + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + public HashMap> getGpuGroupDetails() { return groupDetails; } @@ -397,6 +416,9 @@ public class HostVO implements Host { @Column(name = "mgmt_server_id") private Long managementServerId; + @Column(name = "last_mgmt_server_id") + private Long lastManagementServerId; + @Column(name = "dom0_memory") private long dom0MinMemory; @@ -563,6 +585,10 @@ public class HostVO implements Host { this.managementServerId = managementServerId; } + public void setLastManagementServerId(Long lastManagementServerId) { + this.lastManagementServerId = lastManagementServerId; + } + @Override public long getLastPinged() { return lastPinged; @@ -632,6 +658,11 @@ public class HostVO implements Host { return managementServerId; } + @Override + public Long getLastManagementServerId() { + return lastManagementServerId; + } + @Override public Date getDisconnectedOn() { return disconnectedOn; @@ -705,7 +736,7 @@ public class HostVO implements Host { @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) { @@ -737,6 +768,15 @@ public class HostVO implements Host { 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; } @@ -768,27 +808,48 @@ public class HostVO implements Host { this.uuid = uuid; } - public boolean checkHostServiceOfferingAndTemplateTags(ServiceOffering serviceOffering, VirtualMachineTemplate template) { - if (serviceOffering == null || template == null) { - return false; - } + private Set getHostServiceOfferingAndTemplateStrictTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set strictHostTags) { if (StringUtils.isEmpty(serviceOffering.getHostTag()) && StringUtils.isEmpty(template.getTemplateTag())) { - return true; + return new HashSet<>(); } - if (getHostTags() == null) { - return false; - } - HashSet hostTagsSet = new HashSet<>(getHostTags()); - List tags = new ArrayList<>(); + 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.contains(template.getTemplateTag())) { + 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; 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 ca180e2323f..090b019334f 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java @@ -19,6 +19,7 @@ package com.cloud.host.dao; import java.util.Date; import java.util.List; +import com.cloud.cpu.CPU; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.HostVO; @@ -27,8 +28,10 @@ import com.cloud.hypervisor.Hypervisor; 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. @@ -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 findByClusterId(Long clusterId, Type type); + + 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 'Up' and 'Enabled' from the given Data Center/Zone + * Returns host Ids that are 'Up' and 'Enabled' from the given Data Center/Zone */ - List listByDataCenterId(long id); + List listEnabledIdsByDataCenterId(long id); /** - * 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 'Disabled' from the given Data Center/Zone */ - List listByDataCenterIdAndState(long id, ResourceState state); - - /** - * Returns hosts 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); + /** - * Retrieves the number of hosts/agents this {@see ManagementServer} has responsibility over. - * @param msid the id of the {@see ManagementServer} - * @return the number of hosts/agents this {@see ManagementServer} has responsibility over + * Count Hosts by given Management Server, Host and Hypervisor Types, + * and exclude Hosts with given Resource States. + * + * @param msId Management Server Id + * @param excludedResourceStates Resource States to be excluded + * @param hostTypes Host Types + * @param hypervisorTypes Hypervisor Types + * @return Hosts count */ - int countByMs(long msid); + int countHostsByMsResourceStateTypeAndHypervisorType(long msId, List excludedResourceStates, + List hostTypes, List hypervisorTypes); + + /** + * Retrieves the host ids/agents this {@see ManagementServer} has responsibility over. + * @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 last host ids/agents this {@see ManagementServer} has responsibility over. + * @param msId the id of the {@see ManagementServer} + * @return the last host ids/agents this {@see ManagementServer} has responsibility over + */ + List listByLastMs(long msId); /** * Retrieves the hypervisor versions of the hosts in the datacenter which are in Up state in ascending order @@ -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 5faa877b458..8f218841b07 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java @@ -20,6 +20,7 @@ import java.sql.PreparedStatement; 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.annotation.PostConstruct; import javax.inject.Inject; import javax.persistence.TableGenerator; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; import org.apache.commons.collections.CollectionUtils; @@ -41,12 +44,13 @@ import com.cloud.agent.api.VgpuTypesInfo; import com.cloud.cluster.agentlb.HostTransferMapVO; import com.cloud.cluster.agentlb.dao.HostTransferMapDao; import com.cloud.configuration.ManagementServiceConfiguration; +import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.gpu.dao.HostGpuGroupsDao; 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.Grouping; 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.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.JoinBuilder.JoinType; +import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; @@ -73,20 +80,19 @@ import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.UpdateBuilder; import com.cloud.utils.exception.CloudRuntimeException; - -import 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 class HostDaoImpl extends GenericDaoBase implements HostDao 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 class HostDaoImpl extends GenericDaoBase implements HostDao TypeDcStatusSearch.and("resourceState", TypeDcStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); TypeDcStatusSearch.done(); - TypeClusterStatusSearch = createSearchBuilder(); - TypeClusterStatusSearch.and("type", TypeClusterStatusSearch.entity().getType(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.and("cluster", TypeClusterStatusSearch.entity().getClusterId(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.and("status", TypeClusterStatusSearch.entity().getStatus(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.and("resourceState", TypeClusterStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.done(); + TypeStatusStateSearch = createSearchBuilder(); + TypeStatusStateSearch.and("type", TypeStatusStateSearch.entity().getType(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("cluster", TypeStatusStateSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("pod", TypeStatusStateSearch.entity().getPodId(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("zone", TypeStatusStateSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("status", TypeStatusStateSearch.entity().getStatus(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("resourceState", TypeStatusStateSearch.entity().getResourceState(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.done(); + + IdsSearch = createSearchBuilder(); + IdsSearch.and("id", IdsSearch.entity().getId(), SearchCriteria.Op.IN); + IdsSearch.done(); IdStatusSearch = createSearchBuilder(); IdStatusSearch.and("id", IdStatusSearch.entity().getId(), SearchCriteria.Op.EQ); @@ -291,10 +331,12 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao PodSearch = createSearchBuilder(); PodSearch.and("podId", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); + PodSearch.and("type", PodSearch.entity().getType(), Op.EQ); PodSearch.done(); ClusterSearch = createSearchBuilder(); ClusterSearch.and("clusterId", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + ClusterSearch.and("type", ClusterSearch.entity().getType(), Op.EQ); ClusterSearch.done(); TypeSearch = createSearchBuilder(); @@ -341,6 +383,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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 class HostDaoImpl extends GenericDaoBase implements HostDao 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 class HostDaoImpl extends GenericDaoBase implements HostDao 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 class HostDaoImpl extends GenericDaoBase implements HostDao sc.setParameters("resourceState", (Object[])states); sc.setParameters("cluster", clusterId); - List hosts = listBy(sc); - return hosts.size(); + return getCount(sc); } @Override @@ -503,36 +540,62 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } @Override - public Integer countAllByTypeInZone(long zoneId, Type type) { - SearchCriteria sc = HostTypeCountSearch.create(); - sc.setParameters("type", type); - sc.setParameters("dc", zoneId); + 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 List listByDataCenterId(long id) { - return listByDataCenterIdAndState(id, ResourceState.Enabled); + public Integer countAllByTypeInZone(long zoneId, Type type) { + SearchCriteria sc = HostTypeCountSearch.create(); + sc.setParameters("type", type); + 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 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 listDisabledByDataCenterId(long id) { - return listByDataCenterIdAndState(id, ResourceState.Disabled); + 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 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; + private List listIdsForRoutingByZoneIdAndResourceState(long zoneId, ResourceState state) { + return listIdsBy(Type.Routing, Status.Up, state, null, zoneId, null, null); + } + + @Override + public List listEnabledIdsByDataCenterId(long id) { + return listIdsForRoutingByZoneIdAndResourceState(id, ResourceState.Enabled); + } + + @Override + public List listDisabledIdsByDataCenterId(long id) { + return listIdsForRoutingByZoneIdAndResourceState(id, ResourceState.Disabled); } @Override @@ -590,9 +653,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao sb.append(" "); } - if (logger.isTraceEnabled()) { - logger.trace("Following hosts got reset: " + sb.toString()); - } + logger.trace("Following hosts got reset: {}", sb); } /* @@ -602,8 +663,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao SearchCriteria sc = ClustersOwnedByMSSearch.create(); sc.setParameters("server", managementServerId); - List clusters = customSearch(sc, null); - return clusters; + return customSearch(sc, null); } /* @@ -613,13 +673,11 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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 class HostDaoImpl extends GenericDaoBase implements HostDao 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 class HostDaoImpl extends GenericDaoBase implements HostDao 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 class HostDaoImpl extends GenericDaoBase implements HostDao 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 class HostDaoImpl extends GenericDaoBase implements HostDao 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 class HostDaoImpl extends GenericDaoBase implements HostDao 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(); @@ -781,6 +829,15 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Override 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,7 +848,9 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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); } @@ -804,27 +863,38 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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(); @@ -835,18 +905,12 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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); } @@ -886,12 +950,12 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @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)); @@ -924,7 +988,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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); } @@ -967,7 +1031,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao boolean persisted = super.update(hostId, host); if (!persisted) { - return persisted; + return false; } saveDetails(host); @@ -976,7 +1040,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao txn.commit(); - return persisted; + return true; } @Override @@ -987,11 +1051,10 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao + "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); @@ -1015,9 +1078,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @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!"; @@ -1086,31 +1147,30 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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; } } @@ -1136,25 +1196,24 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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; @@ -1177,20 +1236,103 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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(); @@ -1234,7 +1376,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Override public List findHypervisorHostInCluster(long clusterId) { - SearchCriteria sc = TypeClusterStatusSearch.create(); + SearchCriteria sc = TypeStatusStateSearch.create(); sc.setParameters("type", Host.Type.Routing); sc.setParameters("cluster", clusterId); sc.setParameters("status", Status.Up); @@ -1243,9 +1385,40 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return listBy(sc); } + @Override + public List findHypervisorHostInZone(long zoneId) { + SearchCriteria sc = TypeStatusStateSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("zone", zoneId); + sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); + + return listBy(sc); + } + + @Override + public List findHypervisorHostInPod(long podId) { + SearchCriteria sc = TypeStatusStateSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("pod", podId); + sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); + + return listBy(sc); + } + + @Override + public HostVO findAnyStateHypervisorHostInCluster(long clusterId) { + SearchCriteria sc = TypeStatusStateSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("cluster", clusterId); + List list = listBy(sc, new Filter(1)); + 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); @@ -1253,7 +1426,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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); } @@ -1262,9 +1435,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @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 @@ -1298,19 +1469,19 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } @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)); } @@ -1321,20 +1492,20 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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)); } @@ -1345,7 +1516,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao pstmt.close(); return result; } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + select, e); + throw new CloudRuntimeException("DB Exception on: " + selectStmtToListHostIdsByHostTags, e); } } @@ -1370,16 +1541,41 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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 { @@ -1398,15 +1594,77 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } @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(); @@ -1423,15 +1681,6 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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(); @@ -1506,12 +1755,42 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao 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); } @@ -1546,4 +1825,163 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } 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 8dc4efa91f3..5d8bd0a0a3a 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java @@ -32,4 +32,7 @@ public interface HostDetailsDao extends GenericDao { void deleteDetails(long hostId); List findByName(String name); + + void replaceExternalDetails(long hostId, Map details); + } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java index 9c1340592f9..3eb9faeb1c1 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java @@ -18,6 +18,7 @@ package com.cloud.host.dao; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,6 +32,7 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VmDetailConstants; @Component public class HostDetailsDaoImpl extends GenericDaoBase implements HostDetailsDao { @@ -130,4 +132,34 @@ public class HostDetailsDaoImpl extends GenericDaoBase implement sc.setParameters("name", name); return listBy(sc); } + + @Override + public void replaceExternalDetails(long hostId, Map details) { + if (details.isEmpty()) { + return; + } + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + List detailVOs = new ArrayList<>(); + for (Map.Entry entry : details.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + if ("password".equals(entry.getKey())) { + value = DBEncryptionUtil.encrypt(value); + } + detailVOs.add(new DetailVO(hostId, name, value)); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("hostId", sb.entity().getHostId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("name", VmDetailConstants.EXTERNAL_DETAIL_PREFIX + "%"); + remove(sc); + for (DetailVO detail : detailVOs) { + persist(detail); + } + txn.commit(); + } } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDao.java index d134db33403..7a00829fd44 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 java.util.List; 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 65deb1d1c9b..4aa14a31cfc 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.SearchCriteria; 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 class HostTagsDaoImpl extends GenericDaoBase implements 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 class HostTagsDaoImpl extends GenericDaoBase implements 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 class HostTagsDaoImpl extends GenericDaoBase implements 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 class HostTagsDaoImpl extends GenericDaoBase implements 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 4455c7491dd..a3b03280fdf 100644 --- a/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java +++ b/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java @@ -80,6 +80,18 @@ public class HypervisorCapabilitiesVO implements HypervisorCapabilities { 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 22bb2c26b65..ee5f67b09cd 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.PrimaryKeyJoinColumn; 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 class LBHealthCheckPolicyVO implements HealthCheckPolicy { 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 fa5dcafba34..24d8b8e7f40 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 javax.persistence.TemporalType; 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 class AutoScalePolicyVO implements AutoScalePolicy, InternalIdentity { @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 652cbb340a3..307de9f1a60 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 javax.persistence.TemporalType; 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 class AutoScaleVmGroupVO implements AutoScaleVmGroup, InternalIdentity, I @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 21291062756..562d908507e 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 javax.persistence.Table; 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 class AutoScaleVmProfileVO implements AutoScaleVmProfile, Identity, Inter @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 18e67a4af61..0679dac3235 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.Identity; 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 class ConditionVO implements Condition, Identity, InternalIdentity { @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 e5ab9886dda..be21515bb51 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 org.apache.cloudstack.api.InternalIdentity; 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 class CounterVO implements Counter, Identity, InternalIdentity { @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 4b25c63403e..9775f8ad5b1 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java @@ -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 8fca4c26f9a..b2f4e578a82 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java @@ -18,7 +18,10 @@ package com.cloud.network.as.dao; 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.VMInstanceVO; 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 class AutoScaleVmGroupVmMapDaoImpl extends GenericDaoBase= 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/dao/ExternalLoadBalancerDeviceVO.java b/engine/schema/src/main/java/com/cloud/network/dao/ExternalLoadBalancerDeviceVO.java index 80bec1b8152..88c5c0885a8 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 javax.persistence.Table; 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 class ExternalLoadBalancerDeviceVO implements InternalIdentity, Identity 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 55c454860ef..df5b6b5d647 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 fdd1e0ec43a..6279289bdfe 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.ArrayList; 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 class FirewallRulesCidrsDaoImpl extends GenericDaoBase 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 class FirewallRulesCidrsDaoImpl extends GenericDaoBase 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 21200dbf9b5..7f322ae6c03 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java @@ -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 3ac860b08c5..27bf7ba6aa8 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java @@ -48,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 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i 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 class FirewallRulesDaoImpl extends GenericDaoBase i } public void saveSourceCidrs(FirewallRuleVO firewallRule, List cidrList) { - if (cidrList == null) { - return; - } _firewallRulesCidrsDao.persist(firewallRule.getId(), cidrList); } @@ -258,8 +270,25 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i } @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 class FirewallRulesDaoImpl extends GenericDaoBase i rule.setDestinationCidrsList(destCidrs); } + @Override + public FirewallRuleVO findByNetworkIdAndPorts(long networkId, int startPort, int endPort) { + SearchCriteria sc = FirewallByPortsAndNetwork.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("sourcePortStart", startPort); + sc.setParameters("sourcePortEnd", endPort); + + return findOneBy(sc); + } + + @Override + public List listRoutingIngressFirewallRules(long networkId) { + SearchCriteria sc = RoutingFirewallRulesSearch.create(); + 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 b1b1e1cf757..3f8c36ac94e 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 ca779f7e9ce..0a5ecd25667 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java @@ -26,6 +26,7 @@ import javax.inject.Inject; 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 class IPAddressDaoImpl extends GenericDaoBase implemen address.setSourceNat(false); address.setOneToOneNat(false); address.setAssociatedWithVmId(null); + address.setForRouter(false); address.setState(State.Free); address.setAssociatedWithNetworkId(null); address.setVpcId(null); @@ -420,7 +422,7 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen 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 class IPAddressDaoImpl extends GenericDaoBase implemen 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 4c7569a55b9..a3a65fdb01b 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java @@ -33,6 +33,7 @@ import javax.persistence.TemporalType; 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 class IPAddressVO implements IpAddress { @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 class IPAddressVO implements IpAddress { public boolean isForSystemVms() { return forSystemVms; } + + @Override + public boolean isForRouter() { + return forRouter; + } + + public void setForRouter(boolean forRouter) { + this.forRouter = forRouter; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDao.java b/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDao.java index ac3845beffe..b1831b407a4 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 1c3f231f9c1..d64ba8b4155 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 class InlineLoadBalancerNicMapDaoImpl extends GenericDaoBase 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 e9f50a75a7b..72b8fc151b7 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 javax.persistence.Table; 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 class LBStickinessPolicyVO implements StickinessPolicy { 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 a25534b7010..be2941d5cb2 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 b32320a84cb..dc37cdeefe3 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 @@ 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.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 class LoadBalancerVMMapDaoImpl extends GenericDaoBase 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 bd5ea95dcc7..ad0338b9849 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 javax.persistence.Table; 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,11 @@ public class LoadBalancerVO extends FirewallRuleVO implements LoadBalancer { public String getCidrList() { return cidrList; } + + @Override + public String toString() { + return String.format("LoadBalancer %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "purpose", "state")); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java new file mode 100644 index 00000000000..fe21f72e4db --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.network.element.NetrisProviderVO; +import com.cloud.utils.db.GenericDao; + +public interface NetrisProviderDao extends GenericDao { + NetrisProviderVO findByZoneId(long zoneId); +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java new file mode 100644 index 00000000000..86ea04f1db0 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java @@ -0,0 +1,52 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.network.element.NetrisProviderVO; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +@Component +@DB() +public class NetrisProviderDaoImpl extends GenericDaoBase implements NetrisProviderDao { + + final SearchBuilder allFieldsSearch; + + public NetrisProviderDaoImpl() { + super(); + allFieldsSearch = createSearchBuilder(); + allFieldsSearch.and("id", allFieldsSearch.entity().getId(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("uuid", allFieldsSearch.entity().getUuid(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("hostname", allFieldsSearch.entity().getUrl(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("zone_id", allFieldsSearch.entity().getZoneId(), + SearchCriteria.Op.EQ); + allFieldsSearch.done(); + } + + @Override + public NetrisProviderVO findByZoneId(long zoneId) { + SearchCriteria sc = allFieldsSearch.create(); + sc.setParameters("zone_id", zoneId); + return findOneBy(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java index fa448b026e4..0aae532eac5 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 @@ -415,8 +415,7 @@ public class NetworkDaoImpl extends GenericDaoBaseimplements Ne 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 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 e0509f80c2a..1675c89811a 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 @@ import com.cloud.utils.db.GenericDao; 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 31e083075fa..f25bee5da47 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 class NetworkServiceMapDaoImpl extends GenericDaoBase 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 581f7899069..02abaacd854 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(); } @@ -444,6 +447,7 @@ public class NetworkVO implements Network { return gateway; } + @Override public void setGateway(String gateway) { this.gateway = gateway; } @@ -457,6 +461,7 @@ public class NetworkVO implements Network { return cidr; } + @Override public void setCidr(String cidr) { this.cidr = cidr; } @@ -759,4 +764,13 @@ public class NetworkVO implements Network { 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/OpRouterMonitorServiceDao.java b/engine/schema/src/main/java/com/cloud/network/dao/OpRouterMonitorServiceDao.java index ebc0f1af227..0516e26e13a 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 451320ac9b6..a8e818cfb18 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 415b513b405..9557c7465bf 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 org.apache.cloudstack.api.InternalIdentity; 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 class PhysicalNetworkServiceProviderVO implements PhysicalNetworkServiceP 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 4811b59d31e..09d9f1d7fbf 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java @@ -125,7 +125,7 @@ public class PhysicalNetworkTrafficTypeDaoImpl extends GenericDaoBase List findByAccount(Long accountId); List listByNetworkId(Long networkId); + + List listByVpcId(Long vpcId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java index 484aa6f6631..ccbc60a5562 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java @@ -85,4 +85,11 @@ public class RemoteAccessVpnDaoImpl extends GenericDaoBase listByVpcId(Long vpcId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcId", vpcId); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnVO.java b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnVO.java index 95e3693a99c..2439ea55b4a 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 @@ package com.cloud.network.dao; 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 class RemoteAccessVpnVO implements RemoteAccessVpn { 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/Site2SiteCustomerGatewayVO.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java index 52741fdd9a5..e5394238c31 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 javax.persistence.Table; 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 class Site2SiteCustomerGatewayVO implements Site2SiteCustomerGateway { 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 b032966dd5a..4d6bee5c861 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 org.apache.cloudstack.api.InternalIdentity; 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 Site2SiteVpnConnectionVO implements Site2SiteVpnConnection, Interna 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 d3fef252f50..3475003c269 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java @@ -20,4 +20,6 @@ import com.cloud.utils.db.GenericDao; public interface Site2SiteVpnGatewayDao extends GenericDao { Site2SiteVpnGatewayVO findByVpcId(long vpcId); + + Site2SiteVpnGatewayVO findByPublicIpAddress(long ipAddressId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java index d1fde963217..0aeefe90c29 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java @@ -35,6 +35,7 @@ public class Site2SiteVpnGatewayDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); + sc.setParameters("ipAddressId", ipAddressId); + return findOneBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayVO.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayVO.java index 703c78c7b86..a5eb7efce23 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 javax.persistence.Table; import com.cloud.network.Site2SiteVpnGateway; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @@ -70,6 +71,13 @@ public class Site2SiteVpnGatewayVO implements Site2SiteVpnGateway { 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/element/NetrisProviderVO.java b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java new file mode 100644 index 00000000000..113678f7b01 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java @@ -0,0 +1,265 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.element; + +import com.cloud.network.netris.NetrisProvider; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "netris_providers") +public class NetrisProviderVO implements NetrisProvider { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "url") + private String url; + + @Column(name = "username") + private String username; + + @Column(name = "password") + private String password; + + @Column(name = "site_name") + private String siteName; + + @Column(name = "tenant_name") + private String tenantName; + + @Column(name = "netris_tag") + private String netrisTag; + + @Column(name = "created") + private Date created; + + @Column(name = "removed") + private Date removed; + + public NetrisProviderVO() { + this.uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @Override + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + @Override + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getSiteName() { + return siteName; + } + + public void setSiteName(String siteName) { + this.siteName = siteName; + } + + public String getTenantName() { + return tenantName; + } + + public void setTenantName(String tenantName) { + this.tenantName = tenantName; + } + + public String getNetrisTag() { + return netrisTag; + } + + public void setNetrisTag(String netrisTag) { + this.netrisTag = netrisTag; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public static final class Builder { + private long zoneId; + private long hostId; + private String name; + private String url; + private String username; + private String password; + private String siteName; + private String tenantName; + private String netrisTag; + + public Builder() { + // Default constructor + } + + public Builder setZoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder setHostId(long hostId) { + this.hostId = hostId; + return this; + } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public Builder setUrl(String url) { + this.url = url; + return this; + } + + public Builder setUsername(String username) { + this.username = username; + return this; + } + + public Builder setPassword(String password) { + this.password = password; + return this; + } + + public Builder setSiteName(String siteName) { + this.siteName = siteName; + return this; + } + + public Builder setTenantName(String tenantName) { + this.tenantName = tenantName; + return this; + } + + public Builder setNetrisTag(String netrisTag) { + this.netrisTag = netrisTag; + return this; + } + + public NetrisProviderVO build() { + NetrisProviderVO provider = new NetrisProviderVO(); + provider.setZoneId(this.zoneId); + provider.setHostId(this.hostId); + provider.setUuid(UUID.randomUUID().toString()); + provider.setName(this.name); + provider.setUrl(this.url); + provider.setUsername(this.username); + provider.setPassword(this.password); + provider.setSiteName(this.siteName); + provider.setTenantName(this.tenantName); + provider.setNetrisTag(this.netrisTag); + provider.setCreated(new Date()); + return provider; + } + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/rules/FirewallRuleVO.java b/engine/schema/src/main/java/com/cloud/network/rules/FirewallRuleVO.java index 07b25e7a28c..1dfdc5093a5 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 javax.persistence.Transient; 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 class FirewallRuleVO implements FirewallRule { @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 e1a698881f3..576e2f8172e 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.EnumType; 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 class PortForwardingRuleVO extends FirewallRuleVO implements PortForwardi 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 b89d04ad15a..a737f1b9a20 100644 --- a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java +++ b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java @@ -47,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 29cba516d72..637f47731b4 100644 --- a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java @@ -20,6 +20,7 @@ import java.util.List; 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.GenericDaoBase; 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 @@ public class PortForwardingRulesDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("sourcePortStart", startPort); + sc.setParameters("sourcePortEnd", endPort); + return findOneBy(sc); + } + + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + 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 1980cd33d14..325a6efc867 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 class SecurityGroupRuleVO implements SecurityRule { } } + @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/SecurityGroupVO.java b/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVO.java index ec1cfae43b6..940baaad18d 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 class SecurityGroupVO implements SecurityGroup { 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/vpc/NetworkACLItemVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLItemVO.java index f28b3125a09..4333d35d473 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 javax.persistence.Transient; 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 class NetworkACLItemVO implements NetworkACLItem, Cloneable { @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 280d5dfaf4b..37b9e7ff296 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 class NetworkACLVO implements NetworkACL { @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 2246bd6eed2..632d96819cd 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java @@ -27,6 +27,7 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import javax.persistence.Transient; import com.cloud.utils.db.GenericDao; @@ -42,7 +43,10 @@ public class StaticRouteVO implements StaticRoute { String uuid; @Column(name = "vpc_gateway_id", updatable = false) - long vpcGatewayId; + Long vpcGatewayId; + + @Column(name = "next_hop") + private String nextHop; @Column(name = "cidr") private String cidr; @@ -67,6 +71,9 @@ public class StaticRouteVO implements StaticRoute { uuid = UUID.randomUUID().toString(); } + @Transient + boolean forVpn = false; + /** * @param vpcGatewayId * @param cidr @@ -74,7 +81,7 @@ public class StaticRouteVO implements StaticRoute { * @param accountId TODO * @param domainId TODO */ - public StaticRouteVO(long vpcGatewayId, String cidr, Long vpcId, long accountId, long domainId) { + public StaticRouteVO(Long vpcGatewayId, String cidr, Long vpcId, long accountId, long domainId, String nextHop) { super(); this.vpcGatewayId = vpcGatewayId; this.cidr = cidr; @@ -82,14 +89,32 @@ public class StaticRouteVO implements StaticRoute { this.vpcId = vpcId; this.accountId = accountId; this.domainId = domainId; + this.nextHop = nextHop; uuid = UUID.randomUUID().toString(); } + public StaticRouteVO(String cidr, Long vpcId, long accountId, long domainId, String nextHop, State state, boolean forVpn) { + super(); + this.cidr = cidr; + this.state = state; + this.vpcId = vpcId; + this.accountId = accountId; + this.domainId = domainId; + this.nextHop = nextHop; + uuid = UUID.randomUUID().toString(); + this.forVpn = forVpn; + } + @Override - public long getVpcGatewayId() { + public Long getVpcGatewayId() { return vpcGatewayId; } + @Override + public String getNextHop() { + return nextHop; + } + @Override public String getCidr() { return cidr; @@ -145,4 +170,8 @@ public class StaticRouteVO implements StaticRoute { public String getName() { return null; } + + public boolean isForVpn() { + return forVpn; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcGatewayVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcGatewayVO.java index 72f6a89e70f..b1d4df35d4c 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.Id; 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 class VpcGatewayVO implements VpcGateway { @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 350dda3f3b8..9320a37bc96 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java @@ -28,7 +28,9 @@ import javax.persistence.GenerationType; 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,11 +60,8 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "default") boolean isDefault = false; - @Column(name = "for_nsx") - boolean forNsx = false; - - @Column(name = "nsx_mode") - String nsxMode; + @Column(name = "network_mode") + NetworkOffering.NetworkMode networkMode; @Column(name = GenericDao.REMOVED_COLUMN) Date removed; @@ -85,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(); } @@ -150,20 +156,12 @@ public class VpcOfferingVO implements VpcOffering { return isDefault; } - public boolean isForNsx() { - return forNsx; + public NetworkOffering.NetworkMode getNetworkMode() { + return networkMode; } - public void setForNsx(boolean forNsx) { - this.forNsx = forNsx; - } - - public String getNsxMode() { - return nsxMode; - } - - public void setNsxMode(String nsxMode) { - this.nsxMode = nsxMode; + public void setNetworkMode(NetworkOffering.NetworkMode networkMode) { + this.networkMode = networkMode; } public void setUniqueName(String uniqueName) { @@ -172,8 +170,9 @@ public class VpcOfferingVO implements VpcOffering { @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) { @@ -226,4 +225,21 @@ public class VpcOfferingVO implements VpcOffering { 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 c2024e06c51..e942eadb8ff 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java @@ -28,6 +28,7 @@ import javax.persistence.Table; 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 class VpcVO implements Vpc { return cidr; } + public void setCidr(String cidr) { + this.cidr = cidr; + } + @Override public long getDomainId() { return domainId; @@ -206,8 +214,9 @@ public class VpcVO implements Vpc { @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 class VpcVO implements Vpc { public String getIp6Dns2() { return ip6Dns2; } + + @Override + public boolean useRouterIpAsResolver() { + return useRouterIpResolver; + } + + public void setUseRouterIpResolver(boolean useRouterIpResolver) { + this.useRouterIpResolver = useRouterIpResolver; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java index 264a1ebc75e..aa17723f0b1 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 1cc6a21da76..b83fd891305 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 @@ package com.cloud.network.vpc.dao; 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 class VpcOfferingDaoImpl extends GenericDaoBase impl 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 06cfd25e670..020536e97ec 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java @@ -18,6 +18,7 @@ package com.cloud.network.vpc.dao; import java.util.List; +import com.cloud.network.Network; import com.cloud.network.Network.Service; import com.cloud.network.vpc.VpcOfferingServiceMapVO; import com.cloud.utils.db.GenericDao; @@ -37,4 +38,8 @@ public interface VpcOfferingServiceMapDao extends GenericDao listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service); + } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java index c7400f6edfd..dcb1becf9e8 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java @@ -19,6 +19,7 @@ package com.cloud.network.vpc.dao; import java.util.List; +import com.cloud.network.Network; import org.springframework.stereotype.Component; import com.cloud.network.Network.Service; @@ -110,4 +111,22 @@ public class VpcOfferingServiceMapDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); + sc.setParameters("vpcOffId", vpcOfferingId); + sc.setParameters("provider", provider.getName()); + return findOneBy(sc) != null; + } + + @Override + public List listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service) { + SearchCriteria sc = AllFieldsSearch.create(); + + sc.setParameters("vpcOffId", vpcOfferingId); + sc.setParameters("service", service.getName()); + + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java index 753c45fcc78..a5c4c83ff0f 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java @@ -68,8 +68,15 @@ public class VpcServiceMapDaoImpl extends GenericDaoBase @Override public boolean canProviderSupportServiceInVpc(long vpcId, Service service, Provider provider) { - // TODO Auto-generated method stub - return false; + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcId", vpcId); + sc.setParameters("service", service.getName()); + sc.setParameters("provider", provider.getName()); + if (findOneBy(sc) != null) { + return true; + } else { + return false; + } } @Override diff --git a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java index b2fabf2e3cd..904c8e646eb 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.Network; 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,14 +134,8 @@ public class NetworkOfferingVO implements NetworkOffering { @Column(name = "for_vpc") boolean forVpc; - @Column(name = "for_tungsten") - boolean forTungsten = false; - - @Column(name = "for_nsx") - boolean forNsx = false; - - @Column(name = "nsx_mode") - String nsxMode; + @Column(name = "network_mode") + NetworkMode networkMode; @Column(name = "egress_default_policy") boolean egressdefaultpolicy; @@ -174,6 +169,13 @@ public class NetworkOfferingVO implements NetworkOffering { @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; @@ -193,30 +195,12 @@ public class NetworkOfferingVO implements NetworkOffering { } @Override - public boolean isForTungsten() { - return forTungsten; + public NetworkMode getNetworkMode() { + return networkMode; } - public void setForTungsten(boolean forTungsten) { - this.forTungsten = forTungsten; - } - - @Override - public boolean isForNsx() { - return forNsx; - } - - public void setForNsx(boolean forNsx) { - this.forNsx = forNsx; - } - - @Override - public String getNsxMode() { - return nsxMode; - } - - public void setNsxMode(String nsxMode) { - this.nsxMode = nsxMode; + public void setNetworkMode(NetworkMode networkMode) { + this.networkMode = networkMode; } @Override @@ -464,8 +448,8 @@ public class NetworkOfferingVO implements NetworkOffering { @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 @@ -582,4 +566,21 @@ public class NetworkOfferingVO implements NetworkOffering { 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 381d2144df1..abb63a10d06 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 823ea36b97f..9bc74b13932 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 class NetworkOfferingDaoImpl extends GenericDaoBase { 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 8947cc600b3..b6eb6d44cea 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 class ProjectAccountDaoImpl extends GenericDaoBase 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 3724e03d9d0..cee5cc15e11 100644 --- a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java +++ b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java @@ -22,10 +22,13 @@ import com.cloud.server.ResourceTag; import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.api.response.ResourceIconResponse; +import java.util.Collection; import java.util.List; public interface ResourceIconDao extends GenericDao { ResourceIconResponse newResourceIconResponse(ResourceIcon resourceIconVO); ResourceIconVO findByResourceUuid(String resourceUuid, ResourceTag.ResourceObjectType resourceType); + List listByResourceTypeAndIds(ResourceTag.ResourceObjectType resourceType, Collection resourceIds); + List listByResourceTypeAndUuids(ResourceTag.ResourceObjectType resourceType, Collection resourceUuids); List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType); } diff --git a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java index 1ae01bfc1ec..49a1fe7a560 100644 --- a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java @@ -24,8 +24,10 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.commons.collections.CollectionUtils; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public class ResourceIconDaoImpl extends GenericDaoBase implements ResourceIconDao { @@ -58,11 +60,36 @@ public class ResourceIconDaoImpl extends GenericDaoBase im } @Override - public List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType) { + public List listByResourceTypeAndIds(ResourceTag.ResourceObjectType resourceType, + Collection resourceIds) { + if (CollectionUtils.isEmpty(resourceIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("resourceId", sb.entity().getResourceId(), SearchCriteria.Op.IN); + sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("resourceId", resourceIds.toArray()); + sc.setParameters("resourceType", resourceType); + return listBy(sc); + } + + @Override + public List listByResourceTypeAndUuids(ResourceTag.ResourceObjectType resourceType, + Collection resourceUuids) { + if (CollectionUtils.isEmpty(resourceUuids)) { + return new ArrayList<>(); + } SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("uuid", resourceUuids.toArray()); sc.setParameters("resourceType", resourceType); - List resourceIcons = listBy(sc); + return listBy(sc); + } + + @Override + public List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType) { + List resourceIcons = listByResourceTypeAndUuids(resourceType, resourceUuids); List iconResponses = new ArrayList<>(); for (ResourceIconVO resourceIcon : resourceIcons) { ResourceIconResponse response = new ResourceIconResponse(); diff --git a/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDao.java b/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDao.java index 98fc8c8687b..5023aaa3794 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 f89a1bbf4cc..8229c3a62fc 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 @@ package com.cloud.secstorage; 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 class CommandExecLogDaoImpl extends GenericDaoBase 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 7f5c1a7afa1..cfe8049f5b2 100644 --- a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java @@ -124,6 +124,15 @@ public class ServiceOfferingVO implements ServiceOffering { @Column(name = "dynamic_scaling_enabled") private boolean dynamicScalingEnabled = true; + @Column(name = "vgpu_profile_id") + private Long vgpuProfileId; + + @Column(name = "gpu_count") + private Integer gpuCount; + + @Column(name = "gpu_display") + private Boolean gpuDisplay; + // This is a delayed load value. If the value is null, // then this field has not been loaded yet. // Call service offering dao to load it. @@ -198,6 +207,8 @@ public class ServiceOfferingVO implements ServiceOffering { systemUse = offering.isSystemUse(); dynamicScalingEnabled = offering.isDynamicScalingEnabled(); diskOfferingStrictness = offering.diskOfferingStrictness; + vgpuProfileId = offering.vgpuProfileId; + gpuCount = offering.gpuCount; } @Override @@ -445,4 +456,30 @@ public class ServiceOfferingVO implements ServiceOffering { public void setDiskOfferingStrictness(boolean diskOfferingStrictness) { this.diskOfferingStrictness = diskOfferingStrictness; } + + @Override + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(Long vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + @Override + public Integer getGpuCount() { + return gpuCount; + } + + public void setGpuCount(Integer gpuCount) { + this.gpuCount = gpuCount; + } + + public Boolean getGpuDisplay() { + return gpuDisplay; + } + + public void setGpuDisplay(Boolean gpuDisplay) { + this.gpuDisplay = gpuDisplay; + } } diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java index d086ad1dac1..d3bab4fcbe2 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java @@ -22,6 +22,7 @@ import java.util.Map; import com.cloud.service.ServiceOfferingVO; import com.cloud.storage.Storage.ProvisioningType; import com.cloud.utils.db.GenericDao; +import com.cloud.utils.db.SearchBuilder; import com.cloud.vm.VirtualMachine; /* @@ -54,7 +55,9 @@ public interface ServiceOfferingDao extends GenericDao List listPublicByCpuAndMemory(Integer cpus, Integer memory); - ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(long diskOfferingId); + ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(long diskOfferingId, boolean includingRemoved); - List listByHostTag(String tag); + 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 34ac7c47521..f360770ad68 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java @@ -34,11 +34,12 @@ import com.cloud.service.ServiceOfferingVO; 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 class ServiceOfferingDaoImpl extends GenericDaoBase dynamicOffering = userVmDetailsDao.listDetailsKeyPairs(vmId); + Map dynamicOffering = vmInstanceDetailsDao.listDetailsKeyPairs(vmId); return getComputeOffering(offering, dynamicOffering); } return offering; @@ -282,10 +283,10 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase 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; } @@ -293,8 +294,9 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase listByHostTag(String tag) { - SearchBuilder sb = createSearchBuilder(); + 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); @@ -302,11 +304,21 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase sc = sb.create(); + SearchCriteria sc = sb.create(); + sc.setParameters("tagEq", tag); sc.setParameters("tagStartLike", tag + ",%"); sc.setParameters("tagMidLike", "%," + tag + ",%"); sc.setParameters("tagEndLike", "%," + tag); - return listBy(sc); + 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 181b02e5a1b..a54c1dd9b08 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 @@ package com.cloud.storage; 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 BucketVO implements Bucket { @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 b4f112f98e8..79f5bcb5157 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 javax.persistence.Transient; import com.cloud.offering.DiskOffering; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "disk_offering") @@ -588,4 +589,11 @@ public class DiskOfferingVO implements DiskOffering { 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 36773e351e3..642705ffcbe 100644 --- a/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.storage; +import java.util.Date; import java.util.UUID; import javax.persistence.Column; @@ -25,6 +26,8 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.utils.db.GenericDao; + @Entity @Table(name = "guest_os_category") public class GuestOSCategoryVO implements GuestOsCategory { @@ -39,6 +42,26 @@ public class GuestOSCategoryVO implements GuestOsCategory { @Column(name = "uuid") String uuid = UUID.randomUUID().toString(); + @Column(name = "featured") + boolean featured; + + @Column(name = "sort_key") + private int sortKey; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + public GuestOSCategoryVO() { + } + + public GuestOSCategoryVO(String name, boolean featured) { + this.name = name; + this.featured = featured; + } + @Override public long getId() { return id; @@ -59,7 +82,25 @@ public class GuestOSCategoryVO implements GuestOsCategory { return this.uuid; } - public void setUuid(String uuid) { - this.uuid = uuid; + @Override + public boolean isFeatured() { + return featured; + } + + public void setFeatured(Boolean featured) { + this.featured = featured; + } + + public void setSortKey(int key) { + sortKey = key; + } + + public int getSortKey() { + return sortKey; + } + + @Override + public Date getCreated() { + return created; } } diff --git a/engine/schema/src/main/java/com/cloud/storage/SnapshotPolicyVO.java b/engine/schema/src/main/java/com/cloud/storage/SnapshotPolicyVO.java index c7848586826..f57d9d3dccf 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 javax.persistence.Table; import com.cloud.storage.snapshot.SnapshotPolicy; import com.cloud.utils.DateUtil.IntervalType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "snapshot_policy") @@ -76,6 +77,13 @@ public class SnapshotPolicyVO implements SnapshotPolicy { this.uuid = UUID.randomUUID().toString(); } + @Override + public String toString() { + return String.format("SnapshotPolicy %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "volumeId")); + } + @Override public long getId() { return id; 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 80a890aacad..5e013e76d3c 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.Temporal; 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 class SnapshotScheduleVO implements SnapshotSchedule { 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 39d2cdd0b77..4a504333344 100644 --- a/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java @@ -30,12 +30,11 @@ import javax.persistence.Id; import javax.persistence.Table; import org.apache.cloudstack.util.HypervisorTypeConverter; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; 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") @@ -283,7 +282,8 @@ public class SnapshotVO implements Snapshot { @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/server/src/main/java/com/cloud/api/query/vo/HostTagVO.java b/engine/schema/src/main/java/com/cloud/storage/StoragePoolAndAccessGroupMapVO.java similarity index 58% rename from server/src/main/java/com/cloud/api/query/vo/HostTagVO.java rename to engine/schema/src/main/java/com/cloud/storage/StoragePoolAndAccessGroupMapVO.java index 0a279e5c490..5690324340c 100644 --- a/server/src/main/java/com/cloud/api/query/vo/HostTagVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/StoragePoolAndAccessGroupMapVO.java @@ -14,48 +14,51 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.api.query.vo; +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; -/** - * Storage Tags DB view. - * - */ @Entity -@Table(name = "host_tags") -public class HostTagVO extends BaseViewVO implements InternalIdentity { - private static final long serialVersionUID = 1L; +@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 = "tag") - private String name; + @Column(name = "pool_id") + private long poolId; - @Column(name = "host_id") - long hostId; + @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 id; + return this.id; } - public String getName() { - return name; + public long getPoolId() { + return poolId; } - public long getHostId() { - return hostId; + public String getStorageAccessGroup() { + return storageAccessGroup; } - public void setHostId(long hostId) { - this.hostId = hostId; - } } 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 25b02db6479..3486bac010e 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java @@ -32,7 +32,9 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; +import com.cloud.cpu.CPU; import com.cloud.user.UserData; +import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @@ -160,6 +162,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; @@ -167,6 +172,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; @@ -209,7 +221,7 @@ public class VMTemplateVO implements VirtualMachineTemplate { public VMTemplateVO(long id, String name, ImageFormat format, boolean isPublic, boolean featured, boolean isExtractable, TemplateType type, String url, boolean requiresHvm, int bits, long accountId, String cksum, String displayText, boolean enablePassword, long guestOSId, boolean bootable, HypervisorType hyperType, String templateTag, Map details, boolean sshKeyEnabled, boolean isDynamicallyScalable, boolean directDownload, - boolean deployAsIs) { + boolean deployAsIs, CPU.CPUArch arch, Long extensionId) { this(id, name, format, @@ -235,6 +247,8 @@ public class VMTemplateVO implements VirtualMachineTemplate { 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, @@ -565,7 +579,9 @@ public class VMTemplateVO implements VirtualMachineTemplate { @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) { @@ -655,6 +671,14 @@ public class VMTemplateVO implements VirtualMachineTemplate { this.deployAsIs = deployAsIs; } + public boolean isForCks() { + return forCks; + } + + public void setForCks(boolean forCks) { + this.forCks = forCks; + } + @Override public Long getUserDataId() { return userDataId; @@ -673,4 +697,20 @@ public class VMTemplateVO implements VirtualMachineTemplate { 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/VolumeVO.java b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java index e12859ea8d6..653be54a910 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,9 @@ 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, @@ -425,11 +431,11 @@ public class VolumeVO implements Volume { } public String getHostIp() { - return hostip; + return hostIp; } public void setHostIp(String hostip) { - this.hostip = hostip; + this.hostIp = hostip; } public void setPodId(Long podId) { @@ -508,7 +514,9 @@ public class VolumeVO implements Volume { @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 @@ -678,4 +686,21 @@ public class VolumeVO implements Volume { 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 f45f28b5c2c..2511df49807 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 98bef6201a1..473879d933d 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 class BucketDaoImpl extends GenericDaoBase implements Buc 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 class BucketDaoImpl extends GenericDaoBase implements Buc 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/DiskOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java index 853a9998226..93e74766277 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 @@ -32,7 +32,6 @@ 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; @@ -60,16 +59,6 @@ public class DiskOfferingDaoImpl extends GenericDaoBase im _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); - } - - @Override - public List customSearchIncludingRemoved(SearchCriteria sc, final Filter filter) { - return super.customSearchIncludingRemoved(sc, filter); - } - @Override protected List executeList(final String sql, final Object... params) { StringBuilder builder = new StringBuilder(sql); diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java index 6fad6c5c47e..cc1c96aee52 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java @@ -27,7 +27,6 @@ import com.cloud.utils.db.GenericDaoBase; public class GuestOSCategoryDaoImpl extends GenericDaoBase implements GuestOSCategoryDao { protected GuestOSCategoryDaoImpl() { - } @Override diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java index 13cd398073a..1a2b098c40a 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java @@ -16,14 +16,14 @@ // under the License. package com.cloud.storage.dao; +import java.util.List; +import java.util.Set; + import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSVO; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; -import java.util.List; -import java.util.Set; - public interface GuestOSDao extends GenericDao { GuestOSVO findOneByDisplayName(String displayName); @@ -36,4 +36,6 @@ public interface GuestOSDao extends GenericDao { List listByDisplayName(String displayName); Pair, Integer> listGuestOSByCriteria(Long startIndex, Long pageSize, Long id, Long osCategoryId, String description, String keyword, Boolean forDisplay); + + List listIdsByCategoryId(final long categoryId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java index efcaa482a67..881be207c1a 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java @@ -25,19 +25,20 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import com.cloud.storage.GuestOS; -import com.cloud.utils.Pair; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.TransactionLegacy; -import com.cloud.utils.exception.CloudRuntimeException; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; +import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; @Component public class GuestOSDaoImpl extends GenericDaoBase implements GuestOSDao { @@ -152,4 +153,14 @@ public class GuestOSDaoImpl extends GenericDaoBase implements G return new Pair<>(result.first(), result.second()); } + @Override + public List listIdsByCategoryId(final long categoryId) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("categoryId", sb.entity().getCategoryId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("categoryId", categoryId); + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDao.java index 998d0bbd724..3cda7d42760 100755 --- a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDao.java @@ -57,4 +57,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 030d10d6682..c479a386d79 100755 --- a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java @@ -18,11 +18,14 @@ package com.cloud.storage.dao; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.annotation.PostConstruct; 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 @@ public class SnapshotDaoImpl extends GenericDaoBase implements InstanceIdSearch.join("instanceSnapshots", volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER); InstanceIdSearch.done(); + + volumeIdAndTypeNotInSearch = createSearchBuilder(); + volumeIdAndTypeNotInSearch.and(VOLUME_ID, volumeIdAndTypeNotInSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + volumeIdAndTypeNotInSearch.and(STATUS, volumeIdAndTypeNotInSearch.entity().getState(), SearchCriteria.Op.NEQ); + volumeIdAndTypeNotInSearch.and(NOT_TYPE, volumeIdAndTypeNotInSearch.entity().getTypeDescription(), SearchCriteria.Op.NOTIN); + volumeIdAndTypeNotInSearch.done(); } @Override @@ -285,4 +300,26 @@ public class SnapshotDaoImpl extends GenericDaoBase implements 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 43bb5b3d4d5..02a0355d92d 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 e4ae22cd021..584a2481726 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 7ca0a3915f5..284a42cf9e1 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 @@ import com.cloud.utils.db.GenericDao; */ 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 925d02dd90b..14669ce1d43 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 @@ import com.cloud.utils.db.SearchCriteria; 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 @@ public class SnapshotScheduleDaoImpl extends GenericDaoBase 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 class SnapshotScheduleDaoImpl extends GenericDaoBase 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 00000000000..3ff797f7e74 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDao.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage.dao; + +import java.util.List; + +import com.cloud.storage.StoragePoolAndAccessGroupMapVO; + +import com.cloud.utils.db.GenericDao; + +public interface StoragePoolAndAccessGroupMapDao extends GenericDao { + + void persist(long poolId, List storageAccessGroups); + List getStorageAccessGroups(long poolId); + void deleteStorageAccessGroups(long poolId); + List listDistinctStorageAccessGroups(String name, String keyword); +} diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDaoImpl.java new file mode 100644 index 00000000000..63e82b79748 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDaoImpl.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage.dao; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.storage.StoragePoolAndAccessGroupMapVO; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; + +public class StoragePoolAndAccessGroupMapDaoImpl extends GenericDaoBase implements StoragePoolAndAccessGroupMapDao { + + protected final SearchBuilder StoragePoolAccessGroupSearch; + + public StoragePoolAndAccessGroupMapDaoImpl() { + StoragePoolAccessGroupSearch = createSearchBuilder(); + StoragePoolAccessGroupSearch.and("poolId", StoragePoolAccessGroupSearch.entity().getPoolId(), SearchCriteria.Op.EQ); + StoragePoolAccessGroupSearch.done(); + } + + @Override + public void persist(long poolId, List storageAccessGroups) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + + txn.start(); + SearchCriteria sc = StoragePoolAccessGroupSearch.create(); + sc.setParameters("poolId", poolId); + expunge(sc); + + for (String sag : storageAccessGroups) { + sag = sag.trim(); + if (sag.length() > 0) { + StoragePoolAndAccessGroupMapVO vo = new StoragePoolAndAccessGroupMapVO(poolId, sag); + persist(vo); + } + } + txn.commit(); + } + + @Override + public List getStorageAccessGroups(long poolId) { + SearchCriteria sc = StoragePoolAccessGroupSearch.create(); + sc.setParameters("poolId", poolId); + + List results = search(sc, null); + List storagePoolAccessGroups = new ArrayList(results.size()); + for (StoragePoolAndAccessGroupMapVO result : results) { + storagePoolAccessGroups.add(result.getStorageAccessGroup()); + } + + return storagePoolAccessGroups; + } + + @Override + public void deleteStorageAccessGroups(long poolId) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + SearchCriteria sc = StoragePoolAccessGroupSearch.create(); + sc.setParameters("poolId", poolId); + expunge(sc); + txn.commit(); + } + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroup()); + searchBuilder.and("name", searchBuilder.entity().getStorageAccessGroup(), SearchCriteria.Op.EQ); + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroup(), SearchCriteria.Op.LIKE); + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + + if (name != null) { + sc.setParameters("name", name); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } + +} diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java index 0c39a8c581a..a3baa3b4cb0 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.StoragePoolDetailVO; 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 class StoragePoolDetailsDaoImpl extends ResourceDetailsDaoBase 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 class StoragePoolDetailsDaoImpl extends ResourceDetailsDaoBase 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 b099a6d6bdb..94e13ba2d56 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java @@ -30,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 9e7bdca1181..55b5668bbc1 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java @@ -23,12 +23,18 @@ import java.util.ArrayList; 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 class StoragePoolHostDaoImpl extends GenericDaoBase 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 class StoragePoolHostDaoImpl extends GenericDaoBase findByLocalPath(String path) { + SearchCriteria sc = LocalPathSearch.create(); + sc.setParameters("local_path", path); + return listBy(sc); + } + @Override public List listByHostStatus(long poolId, Status hostStatus) { TransactionLegacy txn = TransactionLegacy.currentTxn(); @@ -149,23 +180,23 @@ public class StoragePoolHostDaoImpl extends GenericDaoBase> 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 class StoragePoolHostDaoImpl extends GenericDaoBase, 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/VMTemplateDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java index 1c5a2cb4256..d70eeb87653 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java @@ -19,6 +19,7 @@ package com.cloud.storage.dao; import java.util.List; import java.util.Map; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateVO; @@ -57,6 +58,8 @@ public interface VMTemplateDao extends GenericDao, StateDao< public List listInZoneByState(long dataCenterId, VirtualMachineTemplate.State... states); + public List listTemplateIsoByArchVnfAndZone(Long dataCenterId, CPU.CPUArch arch, Boolean isIso, Boolean isVnf); + public List listAllActive(); public List listByState(VirtualMachineTemplate.State... states); @@ -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); + 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,12 +94,13 @@ public interface VMTemplateDao extends GenericDao, StateDao< List listByParentTemplatetId(long parentTemplatetId); - VMTemplateVO findLatestTemplateByName(String name); + VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch); List findTemplatesLinkedToUserdata(long userdataId); List listByIds(List ids); - List listByTemplateTag(String tag); + List listIdsByTemplateTag(String tag); + List listIdsByExtensionId(long extensionId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java index 4665f660251..267cef2169a 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,8 +17,11 @@ 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; @@ -28,8 +31,10 @@ import javax.naming.ConfigurationException; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import com.cloud.cpu.CPU; import com.cloud.dc.dao.DataCenterDao; import com.cloud.domain.dao.DomainDao; import com.cloud.host.Host; @@ -47,6 +52,7 @@ import com.cloud.storage.VMTemplateZoneVO; import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.template.VirtualMachineTemplate; +import com.cloud.utils.Pair; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; @@ -111,6 +117,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem LatestTemplateByHypervisorTypeSearch = createSearchBuilder(); LatestTemplateByHypervisorTypeSearch.and("hypervisorType", LatestTemplateByHypervisorTypeSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); LatestTemplateByHypervisorTypeSearch.and("templateType", LatestTemplateByHypervisorTypeSearch.entity().getTemplateType(), SearchCriteria.Op.EQ); + LatestTemplateByHypervisorTypeSearch.and("arch", LatestTemplateByHypervisorTypeSearch.entity().getArch(), SearchCriteria.Op.EQ); LatestTemplateByHypervisorTypeSearch.and("removed", LatestTemplateByHypervisorTypeSearch.entity().getRemoved(), SearchCriteria.Op.NULL); } @@ -238,10 +245,16 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem @Override - public VMTemplateVO findLatestTemplateByName(String name) { - SearchCriteria sc = createSearchCriteria(); - sc.addAnd("name", SearchCriteria.Op.EQ, name); - sc.addAnd("removed", SearchCriteria.Op.NULL); + public VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch) { + SearchBuilder sb = createSearchBuilder(); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("name", name); + if (arch != null) { + sc.setParameters("arch", arch); + } Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L); List templates = listBy(sc, filter); if ((templates != null) && !templates.isEmpty()) { @@ -344,19 +357,12 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem 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(); @@ -515,6 +521,48 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem return listBy(sc); } + @Override + public List listTemplateIsoByArchVnfAndZone(Long dataCenterId, CPU.CPUArch arch, Boolean isIso, + Boolean isVnf) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.select(null, Func.DISTINCT, sb.entity().getGuestOSId()); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.IN); + sb.and("type", sb.entity().getTemplateType(), SearchCriteria.Op.IN); + sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ); + if (isIso != null) { + sb.and("isIso", sb.entity().getFormat(), isIso ? SearchCriteria.Op.EQ : SearchCriteria.Op.NEQ); + } + if (dataCenterId != null) { + SearchBuilder templateZoneSearch = _templateZoneDao.createSearchBuilder(); + templateZoneSearch.and("removed", templateZoneSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + templateZoneSearch.and("zoneId", templateZoneSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + sb.join("templateZoneSearch", templateZoneSearch, templateZoneSearch.entity().getTemplateId(), + sb.entity().getId(), JoinBuilder.JoinType.INNER); + templateZoneSearch.done(); + } + sb.done(); + SearchCriteria sc = sb.create(); + List types = new ArrayList<>(Arrays.asList(TemplateType.USER, TemplateType.BUILTIN, + TemplateType.PERHOST)); + if (isVnf == null) { + types.add(TemplateType.VNF); + } else if (isVnf) { + types = Collections.singletonList(TemplateType.VNF); + } + sc.setParameters("type", types.toArray()); + sc.setParameters("state", VirtualMachineTemplate.State.Active); + if (dataCenterId != null) { + sc.setJoinParameters("templateZoneSearch", "zoneId", dataCenterId); + } + if (arch != null) { + sc.setParameters("arch", arch); + } + if (isIso != null) { + sc.setParameters("isIso", ImageFormat.ISO); + } + return customSearch(sc, null); + } + @Override public List listAllActive() { SearchCriteria sc = ActiveTmpltSearch.create(); @@ -556,29 +604,88 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem } @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)); - - if (tmplts.size() > 0) { - if (hypervisorType == HypervisorType.Any) { - return tmplts.get(0); - } - for (VMTemplateVO tmplt : tmplts) { - if (tmplt.getHypervisorType() == hypervisorType) { - return tmplt; - } - } + return listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, null)); + } + @Override + public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType) { + List templates = listAllReadySystemVMTemplates(zoneId); + if (CollectionUtils.isEmpty(templates)) { + return null; } - return null; + 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(); + } + 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 +726,43 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem } @Override - public VMTemplateVO findLatestTemplateByTypeAndHypervisor(HypervisorType hypervisorType, TemplateType type) { + public List findRoutingTemplates(HypervisorType hType, String templateName, String preferredArch) { + SearchCriteria sc = tmpltTypeHyperSearch2.create(); + sc.setParameters("templateType", TemplateType.ROUTING); + sc.setParameters("hypervisorType", hType); + sc.setParameters("state", VirtualMachineTemplate.State.Active.toString()); + if (templateName != null) { + sc.setParameters("templateName", templateName); + } + List templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, 1L)); + if (CollectionUtils.isEmpty(templates)) { + sc = tmpltTypeHyperSearch2.create(); + sc.setParameters("templateType", TemplateType.SYSTEM); + sc.setParameters("hypervisorType", hType); + sc.setParameters("state", VirtualMachineTemplate.State.Active.toString()); + if (templateName != null) { + sc.setParameters("templateName", templateName); + } + templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, 1L)); + } + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + for (VMTemplateVO template : templates) { + Pair key = new Pair<>(template.getHypervisorType(), template.getArch()); + if (!uniqueTemplates.containsKey(key)) { + uniqueTemplates.put(key, template); + } + } + return getSortedTemplatesListWithPreferredArch(uniqueTemplates, preferredArch); + } + + @Override + public VMTemplateVO findLatestTemplateByTypeAndHypervisorAndArch(HypervisorType hypervisorType, CPU.CPUArch arch, TemplateType type) { SearchCriteria sc = LatestTemplateByHypervisorTypeSearch.create(); sc.setParameters("hypervisorType", hypervisorType); sc.setParameters("templateType", type); + if (arch != null) { + sc.setParameters("arch", arch); + } Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L); List templates = listBy(sc, filter); if (templates != null && !templates.isEmpty()) { @@ -687,13 +827,25 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem } @Override - public List listByTemplateTag(String tag) { - SearchBuilder sb = createSearchBuilder(); + 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(); + SearchCriteria sc = sb.create(); sc.setParameters("tag", tag); - return listIncludingRemovedBy(sc); + 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 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 4e9c63699ca..0f47c9e7155 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java @@ -112,7 +112,8 @@ public interface VolumeDao extends GenericDao, StateDao virtualRouters); @@ -158,4 +159,9 @@ public interface VolumeDao extends GenericDao, StateDao listAllocatedVolumesForAccountDiskOfferingIdsAndNotForVms(long accountId, List diskOfferingIds, List vmIds); + List searchRemovedByVms(List vmIds, Long batchSize); + + VolumeVO findOneByIScsiName(String iScsiName); + + VolumeVO findByLastIdAndState(long lastVolumeId, Volume.State...states); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java index 31d64daf147..f8b6bb3ed68 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java @@ -27,14 +27,12 @@ import java.util.stream.Collectors; 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 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; @@ -48,12 +46,16 @@ import com.cloud.storage.VolumeVO; 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; @@ -76,12 +78,12 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol protected GenericSearchBuilder primaryStorageSearch2; protected GenericSearchBuilder secondaryStorageSearch; private final SearchBuilder poolAndPathSearch; + @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 @@ -395,6 +397,8 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol 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(); @@ -502,7 +506,6 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol poolAndPathSearch.and("poolId", poolAndPathSearch.entity().getPoolId(), Op.EQ); poolAndPathSearch.and("path", poolAndPathSearch.entity().getPath(), Op.EQ); poolAndPathSearch.done(); - } @Override @@ -570,14 +573,6 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol } } - public static class SumCount { - public long sum; - public long count; - - public SumCount() { - } - } - @Override public List listVolumesToBeDestroyed() { SearchCriteria sc = AllFieldsSearch.create(); @@ -740,7 +735,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol 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); @@ -763,7 +758,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol 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"); } @@ -896,4 +891,33 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol 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 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 ff6af56c9c3..b4a596dfc8d 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 5d0d3c8921c..e4c19fd1666 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 java.util.List; 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 @@ import com.cloud.storage.VolumeStatsVO; @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 class VolumeStatsDaoImpl extends GenericDaoBase impl } @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 03857137ded..5c1a7504692 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 class 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 154a8d11887..384826227af 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java @@ -116,10 +116,6 @@ public class DatabaseCreator { } 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 class DatabaseCreator { 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 ea8ce47611a..1e3b3a7e5ec 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -33,7 +33,6 @@ import java.util.List; 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; @@ -87,6 +86,10 @@ import com.cloud.upgrade.dao.Upgrade41710to41720; 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.Upgrade420to421; import com.cloud.upgrade.dao.Upgrade421to430; import com.cloud.upgrade.dao.Upgrade430to440; @@ -129,6 +132,7 @@ 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 +230,10 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { .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()) .build(); } @@ -296,83 +303,120 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { } protected void upgrade(CloudStackVersion dbVersion, CloudStackVersion currentVersion) { + executeProcedureScripts(); + final DbUpgrade[] upgrades = executeUpgrades(dbVersion, currentVersion); + + executeViewScripts(); + updateSystemVmTemplates(upgrades); + } + + protected void executeProcedureScripts() { + LOGGER.info(String.format("Executing Stored Procedure scripts that are under resource directory [%s].", PROCEDURES_DIRECTORY)); + List filesPathUnderViewsDirectory = FileUtil.getFilesPathsUnderResourceDirectory(PROCEDURES_DIRECTORY); + + try (TransactionLegacy txn = TransactionLegacy.open("execute-procedure-scripts")) { + Connection conn = txn.getConnection(); + + for (String filePath : filesPathUnderViewsDirectory) { + LOGGER.debug(String.format("Executing PROCEDURE script [%s].", filePath)); + + InputStream viewScript = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath); + runScript(conn, viewScript); + } + + LOGGER.info(String.format("Finished execution of PROCEDURE scripts that are under resource directory [%s].", PROCEDURES_DIRECTORY)); + } 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 " + dbVersion + " to " + currentVersion); final DbUpgrade[] upgrades = calculateUpgradePath(dbVersion, currentVersion); 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(); + VersionVO version = executeUpgrade(upgrade); + executeUpgradeCleanup(upgrade, version); + } + return upgrades; + } + + private VersionVO executeUpgrade(DbUpgrade upgrade) { + VersionVO version; + LOGGER.debug("Running upgrade " + upgrade.getClass().getSimpleName() + " to upgrade from " + upgrade.getUpgradableVersionRange()[0] + "-" + upgrade + .getUpgradableVersionRange()[1] + " to " + upgrade.getUpgradedVersion()); + TransactionLegacy txn = TransactionLegacy.open("Upgrade"); + txn.start(); + try { + Connection conn; 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); - } - } - - upgrade.performDataMigration(conn); - - version = new VersionVO(upgrade.getUpgradedVersion()); - version = _dao.persist(version); - - txn.commit(); - } catch (CloudRuntimeException e) { + 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.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(); - } + txn.commit(); + } catch (CloudRuntimeException e) { + String errorMessage = "Unable to upgrade the database"; + LOGGER.error(errorMessage, e); + throw new CloudRuntimeException(errorMessage, e); + } finally { + txn.close(); } + return version; + } - executeViewScripts(); - updateSystemVmTemplates(upgrades); + private void executeUpgradeCleanup(DbUpgrade upgrade, VersionVO version) { + TransactionLegacy txn; + // 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()); + + 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); + } + + 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.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() { 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 33371fc436e..e711c564015 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java @@ -16,6 +16,47 @@ // 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.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl; +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.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; @@ -36,6 +77,7 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.upgrade.dao.BasicTemplateDataStoreDaoImpl; import com.cloud.user.Account; import com.cloud.utils.DateUtil; +import com.cloud.utils.HttpUtils; import com.cloud.utils.Pair; import com.cloud.utils.UriUtils; import com.cloud.utils.db.GlobalLock; @@ -46,45 +88,10 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.dao.VMInstanceDaoImpl; -import 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/"; @@ -99,6 +106,9 @@ public class SystemVmTemplateRegistration { private static final Integer LINUX_7_ID = 183; private static final Integer SCRIPT_TIMEOUT = 1800000; private static final Integer LOCK_WAIT_TIMEOUT = 1200; + protected static final List DOWNLOADABLE_TEMPLATE_ARCH_TYPES = Arrays.asList( + CPU.CPUArch.arm64 + ); public static String CS_MAJOR_VERSION = null; @@ -117,12 +127,16 @@ public class SystemVmTemplateRegistration { @Inject ImageStoreDao imageStoreDao; @Inject + ImageStoreDetailsDao imageStoreDetailsDao; + @Inject ClusterDao clusterDao; @Inject ConfigurationDao configurationDao; private String systemVmTemplateVersion; + private final File tempDownloadDir; + public SystemVmTemplateRegistration() { dataCenterDao = new DataCenterDaoImpl(); vmTemplateDao = new VMTemplateDaoImpl(); @@ -130,8 +144,10 @@ public class SystemVmTemplateRegistration { templateDataStoreDao = new BasicTemplateDataStoreDaoImpl(); vmInstanceDao = new VMInstanceDaoImpl(); imageStoreDao = new ImageStoreDaoImpl(); + imageStoreDetailsDao = new ImageStoreDetailsDaoImpl(); clusterDao = new ClusterDaoImpl(); configurationDao = new ConfigurationDaoImpl(); + tempDownloadDir = new File(System.getProperty("java.io.tmpdir")); } /** @@ -142,6 +158,14 @@ public class SystemVmTemplateRegistration { this.systemVmTemplateVersion = systemVmTemplateVersion; } + public static String getMountCommand(String nfsVersion, String device, String dir) { + String cmd = MOUNT_COMMAND_BASE; + if (StringUtils.isNotBlank(nfsVersion)) { + cmd = String.format("%s -o vers=%s", cmd, nfsVersion); + } + return String.format("%s %s %s", cmd, device, dir); + } + public String getSystemVmTemplateVersion() { if (StringUtils.isEmpty(systemVmTemplateVersion)) { return String.format("%s.%s", CS_MAJOR_VERSION, CS_TINY_VERSION); @@ -149,6 +173,10 @@ public class SystemVmTemplateRegistration { return systemVmTemplateVersion; } + public File getTempDownloadDir() { + return tempDownloadDir; + } + private static class SystemVMTemplateDetails { Long id; String uuid; @@ -160,6 +188,7 @@ public class SystemVmTemplateRegistration { ImageFormat format; Integer guestOsId; Hypervisor.HypervisorType hypervisorType; + CPU.CPUArch arch; Long storeId; Long size; Long physicalSize; @@ -169,7 +198,7 @@ public class SystemVmTemplateRegistration { SystemVMTemplateDetails(String uuid, String name, Date created, String url, String checksum, ImageFormat format, Integer guestOsId, Hypervisor.HypervisorType hypervisorType, - Long storeId) { + CPU.CPUArch arch, Long storeId) { this.uuid = uuid; this.name = name; this.created = created; @@ -178,6 +207,7 @@ public class SystemVmTemplateRegistration { this.format = format; this.guestOsId = guestOsId; this.hypervisorType = hypervisorType; + this.arch = arch; this.storeId = storeId; } @@ -221,6 +251,10 @@ public class SystemVmTemplateRegistration { return hypervisorType; } + public CPU.CPUArch getArch() { + return arch; + } + public Long getStoreId() { return storeId; } @@ -274,18 +308,17 @@ public class SystemVmTemplateRegistration { } } - public static final List hypervisorList = Arrays.asList(Hypervisor.HypervisorType.KVM, - Hypervisor.HypervisorType.VMware, - Hypervisor.HypervisorType.XenServer, - Hypervisor.HypervisorType.Hyperv, - Hypervisor.HypervisorType.LXC, - Hypervisor.HypervisorType.Ovm3 + public static final List> hypervisorList = Arrays.asList( + new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), + new Pair<>(Hypervisor.HypervisorType.VMware, null), + new Pair<>(Hypervisor.HypervisorType.XenServer, null), + new Pair<>(Hypervisor.HypervisorType.Hyperv, null), + new Pair<>(Hypervisor.HypervisorType.LXC, null), + new Pair<>(Hypervisor.HypervisorType.Ovm3, null) ); - public static final Map NewTemplateNameList = new HashMap(); - public static final Map FileNames = new HashMap(); - public static final Map NewTemplateUrl = new HashMap(); - public static final Map NewTemplateChecksum = new HashMap(); + public static final Map NewTemplateMap = new HashMap<>(); public static final Map RouterTemplateConfigurationNames = new HashMap() { { @@ -320,19 +353,22 @@ public class SystemVmTemplateRegistration { } }; - public static boolean validateIfSeeded(String url, String path) { + 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); + mountStore(url, filePath, nfsVersion); int lastIdx = path.lastIndexOf(File.separator); String partialDirPath = path.substring(0, lastIdx); String templatePath = filePath + File.separator + partialDirPath; File templateProps = new File(templatePath + "/template.properties"); if (templateProps.exists()) { + Pair templateSizes = readTemplatePropertiesSizes(templatePath + "/template.properties"); + updateSeededTemplateDetails(templDataStoreVO.getTemplateId(), templDataStoreVO.getDataStoreId(), + templateSizes.first(), templateSizes.second()); LOGGER.info("SystemVM template already seeded, skipping registration"); return true; } @@ -351,55 +387,73 @@ public class SystemVmTemplateRegistration { } } - public Long getRegisteredTemplateId(Pair hypervisorAndTemplateName) { - VMTemplateVO vmTemplate = vmTemplateDao.findLatestTemplateByName(hypervisorAndTemplateName.second()); - Long templateId = null; - if (vmTemplate != null) { - templateId = vmTemplate.getId(); + private static String getHypervisorArchLog(Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) { + StringBuilder sb = new StringBuilder("hypervisor: ").append(hypervisorType.name()); + if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + sb.append(", arch: ").append(arch == null ? CPU.CPUArch.amd64.getType() : arch.getType()); } - return templateId; + return sb.toString(); } - private static String fetchTemplatesPath() { - String filePath = RELATIVE_TEMPLATE_PATH + METADATA_FILE_NAME; - LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath)); - File metaFile = new File(filePath); - String templatePath = null; - if (metaFile.exists()) { - templatePath = RELATIVE_TEMPLATE_PATH; + protected static String getHypervisorArchKey(Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) { + if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + return String.format("%s-%s", hypervisorType.name().toLowerCase(), + arch == null ? CPU.CPUArch.amd64.getType() : arch.getType()); } - if (templatePath == null) { - filePath = ABSOLUTE_TEMPLATE_PATH + METADATA_FILE_NAME; - metaFile = new File(filePath); - templatePath = ABSOLUTE_TEMPLATE_PATH; - LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath)); - if (!metaFile.exists()) { - String errMsg = String.format("Unable to locate metadata file in your setup at %s", filePath.toString()); - LOGGER.error(errMsg); - throw new CloudRuntimeException(errMsg); + return hypervisorType.name().toLowerCase(); + } + + protected static MetadataTemplateDetails getMetadataTemplateDetails(Hypervisor.HypervisorType hypervisorType, + CPU.CPUArch arch) { + return NewTemplateMap.get(getHypervisorArchKey(hypervisorType, arch)); + } + + public VMTemplateVO getRegisteredTemplate(String templateName, CPU.CPUArch arch) { + return vmTemplateDao.findLatestTemplateByName(templateName, arch); + } + + private static boolean isRunningInTest() { + return "true".equalsIgnoreCase(System.getProperty("test.mode")); + } + + /** + * Attempts to determine the templates directory path by locating the metadata file. + *

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

+ *

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

+ *

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

+ * + * @return the path to the templates directory if the metadata file is found, or {@code RELATIVE_TEMPLATE_PATH} + * when running in a test environment. + * @throws CloudRuntimeException if the metadata file cannot be located in any of the candidate paths. + */ + private static String fetchTemplatesPath() { + if (isRunningInTest()) { + return RELATIVE_TEMPLATE_PATH; + } + List paths = Arrays.asList(RELATIVE_TEMPLATE_PATH, ABSOLUTE_TEMPLATE_PATH); + for (String path : paths) { + String filePath = path + METADATA_FILE_NAME; + LOGGER.debug("Looking for file [ {} ] in the classpath.", filePath); + File metaFile = new File(filePath); + if (metaFile.exists()) { + return path; } } - return templatePath; - } - - private String getHypervisorName(String name) { - if (name.equals("xenserver")) { - return "xen"; - } - if (name.equals("ovm3")) { - return "ovm"; - } - return name; - - } - - private Hypervisor.HypervisorType getHypervisorType(String hypervisor) { - if (hypervisor.equalsIgnoreCase("xen")) { - hypervisor = "xenserver"; - } else if (hypervisor.equalsIgnoreCase("ovm")) { - hypervisor = "ovm3"; - } - return Hypervisor.HypervisorType.getType(hypervisor); + String errMsg = String.format("Unable to locate metadata file in your setup at %s", StringUtils.join(paths)); + LOGGER.error(errMsg); + throw new CloudRuntimeException(errMsg); } private List getEligibleZoneIds() { @@ -413,29 +467,28 @@ public class SystemVmTemplateRegistration { return zoneIds; } - private Pair getNfsStoreInZone(Long zoneId) { - String url = null; - Long storeId = null; + protected Pair getNfsStoreInZone(Long zoneId) { ImageStoreVO storeVO = imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs"); if (storeVO == null) { - String errMsg = String.format("Failed to fetch NFS store in zone = %s for SystemVM template registration", zoneId); + String errMsg = String.format("Failed to fetch NFS store in zone = %s for SystemVM template registration", + zoneId); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } - url = storeVO.getUrl(); - storeId = storeVO.getId(); + String url = storeVO.getUrl(); + Long storeId = storeVO.getId(); return new Pair<>(url, storeId); } - public static void mountStore(String storeUrl, String path) { + public static void mountStore(String storeUrl, String path, String nfsVersion) { try { - if (storeUrl != null) { - URI uri = new URI(UriUtils.encodeURIComponent(storeUrl)); - String host = uri.getHost(); - String mountPath = uri.getPath(); - String mount = String.format(MOUNT_COMMAND, host + ":" + mountPath, path); - Script.runSimpleBashScript(mount); + 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); @@ -443,13 +496,6 @@ public class SystemVmTemplateRegistration { } } - private List fetchAllHypervisors(Long zoneId) { - List hypervisorList = new ArrayList<>(); - List hypervisorTypes = clusterDao.getAvailableHypervisorInZone(zoneId); - hypervisorList = hypervisorTypes.stream().distinct().map(Hypervisor.HypervisorType::name).collect(Collectors.toList()); - return hypervisorList; - } - private VMTemplateVO createTemplateObjectInDB(SystemVMTemplateDetails details) { Long templateId = vmTemplateDao.getNextInSequence(Long.class, "id"); VMTemplateVO template = new VMTemplateVO(); @@ -470,6 +516,7 @@ public class SystemVmTemplateRegistration { template.setGuestOSId(details.getGuestOsId()); template.setCrossZones(true); template.setHypervisorType(details.getHypervisorType()); + template.setArch(details.getArch()); template.setState(VirtualMachineTemplate.State.Inactive); template.setDeployAsIs(false); template = vmTemplateDao.persist(template); @@ -483,19 +530,19 @@ public class SystemVmTemplateRegistration { 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) { + private 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_ref record for the systemVM template (id: %s) and zone: %s", templateId, dc)); } } } @@ -529,6 +576,21 @@ public class SystemVmTemplateRegistration { } } + public 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_ref entry for seeded systemVM template"); + } + } + public void updateSystemVMEntries(Long templateId, Hypervisor.HypervisorType hypervisorType) { vmInstanceDao.updateSystemVmTemplateId(templateId, hypervisorType); } @@ -542,7 +604,7 @@ public class SystemVmTemplateRegistration { } } - private static void readTemplateProperties(String path, SystemVMTemplateDetails details) { + private static Pair readTemplatePropertiesSizes(String path) { File tmpFile = new File(path); Long size = null; Long physicalSize = 0L; @@ -561,8 +623,13 @@ public class SystemVmTemplateRegistration { } catch (IOException ex) { LOGGER.warn("Failed to read from template.properties", ex); } - details.setSize(size); - details.setPhysicalSize(physicalSize); + return new Pair<>(size, physicalSize); + } + + public static void readTemplateProperties(String path, SystemVMTemplateDetails details) { + Pair templateSizes = readTemplatePropertiesSizes(path); + details.setSize(templateSizes.first()); + details.setPhysicalSize(templateSizes.second()); } private void updateTemplateTablesOnFailure(long templateId) { @@ -591,16 +658,20 @@ public class SystemVmTemplateRegistration { } } - private void setupTemplate(String templateName, Pair hypervisorAndTemplateName, - String destTempFolder) throws CloudRuntimeException { + private void setupTemplate(String templateName, Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch, + String destTempFolder) throws CloudRuntimeException { String setupTmpltScript = Script.findScript(storageScriptsDir, "setup-sysvm-tmplt"); if (setupTmpltScript == null) { throw new CloudRuntimeException("Unable to find the createtmplt.sh"); } Script scr = new Script(setupTmpltScript, SCRIPT_TIMEOUT, LOGGER); scr.add("-u", templateName); - scr.add("-f", TEMPLATES_PATH + FileNames.get(hypervisorAndTemplateName.first())); - scr.add("-h", hypervisorAndTemplateName.first().name().toLowerCase(Locale.ROOT)); + MetadataTemplateDetails templateDetails = NewTemplateMap.get(getHypervisorArchKey(hypervisor, arch)); + String filePath = StringUtils.isNotBlank(templateDetails.getDownloadedFilePath()) ? + templateDetails.getDownloadedFilePath() : + templateDetails.getDefaultFilePath(); + scr.add("-f", filePath); + scr.add("-h", hypervisor.name().toLowerCase(Locale.ROOT)); scr.add("-d", destTempFolder); String result = scr.execute(); if (result != null) { @@ -608,25 +679,24 @@ public class SystemVmTemplateRegistration { LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } - } - private Long performTemplateRegistrationOperations(Pair hypervisorAndTemplateName, - String url, String checksum, ImageFormat format, long guestOsId, - Long storeId, Long templateId, String filePath, TemplateDataStoreVO templateDataStoreVO) { - Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first(); + private Long performTemplateRegistrationOperations(Hypervisor.HypervisorType hypervisor, + String name, CPU.CPUArch arch, String url, String checksum, ImageFormat format, long guestOsId, + Long storeId, Long templateId, String filePath, TemplateDataStoreVO templateDataStoreVO) { String templateName = UUID.randomUUID().toString(); Date created = new Date(DateUtil.currentGMTTime().getTime()); - SystemVMTemplateDetails details = new SystemVMTemplateDetails(templateName, hypervisorAndTemplateName.second(), created, - url, checksum, format, (int) guestOsId, hypervisor, storeId); + SystemVMTemplateDetails details = new SystemVMTemplateDetails(templateName, name, created, + url, checksum, format, (int) guestOsId, hypervisor, arch, storeId); if (templateId == null) { VMTemplateVO template = createTemplateObjectInDB(details); if (template == null) { throw new CloudRuntimeException(String.format("Failed to register template for hypervisor: %s", hypervisor.name())); } templateId = template.getId(); - createCrossZonesTemplateZoneRefEntries(template); } + createCrossZonesTemplateZoneRefEntries(templateId); + details.setId(templateId); String destTempFolderName = String.valueOf(templateId); String destTempFolder = filePath + PARTIAL_TEMPLATE_FOLDER + destTempFolderName; @@ -634,23 +704,44 @@ public class SystemVmTemplateRegistration { if (templateDataStoreVO == null) { createTemplateStoreRefEntry(details); } - setupTemplate(templateName, hypervisorAndTemplateName, destTempFolder); + setupTemplate(templateName, hypervisor, arch, destTempFolder); readTemplateProperties(destTempFolder + "/template.properties", details); details.setUpdated(new Date(DateUtil.currentGMTTime().getTime())); updateTemplateDetails(details); return templateId; } - public void registerTemplate(Pair hypervisorAndTemplateName, - Pair storeUrlAndId, VMTemplateVO templateVO, - TemplateDataStoreVO templateDataStoreVO, String filePath) { + public void registerTemplate(Hypervisor.HypervisorType hypervisor, String name, Long storeId, + VMTemplateVO templateVO, TemplateDataStoreVO templateDataStoreVO, String filePath) { + try { + performTemplateRegistrationOperations(hypervisor, name, templateVO.getArch(), templateVO.getUrl(), + templateVO.getChecksum(), templateVO.getFormat(), templateVO.getGuestOSId(), storeId, + templateVO.getId(), filePath, templateDataStoreVO); + } catch (Exception e) { + String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisor); + LOGGER.error(errMsg, e); + updateTemplateTablesOnFailure(templateVO.getId()); + cleanupStore(templateVO.getId(), filePath); + throw new CloudRuntimeException(errMsg, e); + } + } + + public void registerTemplateForNonExistingEntries(Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch, + String name, Pair storeUrlAndId, String filePath) { Long templateId = null; try { - templateId = templateVO.getId(); - performTemplateRegistrationOperations(hypervisorAndTemplateName, templateVO.getUrl(), templateVO.getChecksum(), - templateVO.getFormat(), templateVO.getGuestOSId(), storeUrlAndId.second(), templateId, filePath, templateDataStoreVO); + MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisor, arch); + templateId = performTemplateRegistrationOperations(hypervisor, name, + templateDetails.getArch(), templateDetails.getUrl(), + templateDetails.getChecksum(), hypervisorImageFormat.get(hypervisor), + hypervisorGuestOsMap.get(hypervisor), storeUrlAndId.second(), null, filePath, null); + Map configParams = new HashMap<>(); + configParams.put(RouterTemplateConfigurationNames.get(hypervisor), templateDetails.getName()); + configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion()); + updateConfigurationParams(configParams); + updateSystemVMEntries(templateId, hypervisor); } catch (Exception e) { - String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisorAndTemplateName.first()); + String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisor); LOGGER.error(errMsg, e); if (templateId != null) { updateTemplateTablesOnFailure(templateId); @@ -660,26 +751,31 @@ public class SystemVmTemplateRegistration { } } - public void registerTemplate(Pair hypervisorAndTemplateName, Pair storeUrlAndId, String filePath) { - Long templateId = null; - try { - Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first(); - templateId = performTemplateRegistrationOperations(hypervisorAndTemplateName, NewTemplateUrl.get(hypervisor), NewTemplateChecksum.get(hypervisor), - hypervisorImageFormat.get(hypervisor), hypervisorGuestOsMap.get(hypervisor), storeUrlAndId.second(), null, filePath, null); - Map configParams = new HashMap<>(); - configParams.put(RouterTemplateConfigurationNames.get(hypervisorAndTemplateName.first()), hypervisorAndTemplateName.second()); - configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion()); - updateConfigurationParams(configParams); - updateSystemVMEntries(templateId, hypervisorAndTemplateName.first()); - } catch (Exception e) { - String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisorAndTemplateName.first()); - LOGGER.error(errMsg, e); - if (templateId != null) { - updateTemplateTablesOnFailure(templateId); - cleanupStore(templateId, filePath); - } - throw new CloudRuntimeException(errMsg, e); + protected void validateTemplateFileForHypervisorAndArch(Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch) { + MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisor, arch); + File templateFile = getTemplateFile(templateDetails); + if (templateFile == null) { + throw new CloudRuntimeException("Failed to find local template file"); } + if (isTemplateFileChecksumDifferent(templateDetails, templateFile)) { + throw new CloudRuntimeException("Checksum failed for local template file"); + } + } + + public void validateAndRegisterTemplate(Hypervisor.HypervisorType hypervisor, String name, Long storeId, + VMTemplateVO templateVO, TemplateDataStoreVO templateDataStoreVO, String filePath) { + validateTemplateFileForHypervisorAndArch(hypervisor, templateVO.getArch()); + registerTemplate(hypervisor, name, storeId, templateVO, templateDataStoreVO, filePath); + } + + public void validateAndRegisterTemplateForNonExistingEntries(Hypervisor.HypervisorType hypervisor, + CPU.CPUArch arch, String name, Pair storeUrlAndId, String filePath) { + validateTemplateFileForHypervisorAndArch(hypervisor, arch); + registerTemplateForNonExistingEntries(hypervisor, arch, name, storeUrlAndId, filePath); + } + + protected static String getMetadataFilePath() { + return METADATA_FILE; } /** @@ -689,26 +785,41 @@ public class SystemVmTemplateRegistration { * exist a template corresponding to the current code version. */ public static String parseMetadataFile() { - try { - Ini ini = new Ini(); - ini.load(new FileReader(METADATA_FILE)); - for (Hypervisor.HypervisorType hypervisorType : hypervisorList) { - String hypervisor = hypervisorType.name().toLowerCase(Locale.ROOT); - Ini.Section section = ini.get(hypervisor); - NewTemplateNameList.put(hypervisorType, section.get("templatename")); - FileNames.put(hypervisorType, section.get("filename")); - NewTemplateChecksum.put(hypervisorType, section.get("checksum")); - NewTemplateUrl.put(hypervisorType, section.get("downloadurl")); - } - Ini.Section section = ini.get("default"); - return section.get("version"); - } catch (Exception e) { - String errMsg = String.format("Failed to parse systemVM template metadata file: %s", METADATA_FILE); + String metadataFilePath = getMetadataFilePath(); + String errMsg = String.format("Failed to parse systemVM template metadata file: %s", metadataFilePath); + final Ini ini = new Ini(); + try (FileReader reader = new FileReader(metadataFilePath)) { + ini.load(reader); + } catch (IOException e) { LOGGER.error(errMsg, e); throw new CloudRuntimeException(errMsg, e); } + if (!ini.containsKey("default")) { + errMsg = String.format("%s as unable to default section", errMsg); + LOGGER.error(errMsg); + throw new CloudRuntimeException(errMsg); + } + for (Pair hypervisorType : hypervisorList) { + String key = getHypervisorArchKey(hypervisorType.first(), hypervisorType.second()); + Ini.Section section = ini.get(key); + if (section == null) { + LOGGER.error("Failed to find details for {} in template metadata file: {}", + key, metadataFilePath); + continue; + } + NewTemplateMap.put(key, new MetadataTemplateDetails( + hypervisorType.first(), + section.get("templatename"), + section.get("filename"), + section.get("downloadurl"), + section.get("checksum"), + hypervisorType.second())); + } + Ini.Section defaultSection = ini.get("default"); + return defaultSection.get("version").trim(); } + private static void cleanupStore(Long templateId, String filePath) { String destTempFolder = filePath + PARTIAL_TEMPLATE_FOLDER + String.valueOf(templateId); try { @@ -718,31 +829,60 @@ public class SystemVmTemplateRegistration { } } - private void validateTemplates(Set hypervisorsInUse) { - Set hypervisors = hypervisorsInUse.stream(). - map(Hypervisor.HypervisorType::name).map(name -> name.toLowerCase(Locale.ROOT)).map(this::getHypervisorName).collect(Collectors.toSet()); - List templates = new ArrayList<>(); - for (Hypervisor.HypervisorType hypervisorType : hypervisorsInUse) { - templates.add(FileNames.get(hypervisorType)); + protected File getTemplateFile(MetadataTemplateDetails templateDetails) { + File templateFile = new File(templateDetails.getDefaultFilePath()); + if (templateFile.exists()) { + return templateFile; } + LOGGER.debug("{} is not present", templateFile.getAbsolutePath()); + if (DOWNLOADABLE_TEMPLATE_ARCH_TYPES.contains(templateDetails.getArch()) && + StringUtils.isNotBlank(templateDetails.getUrl())) { + LOGGER.debug("Downloading the template file {} for {}", + templateDetails.getUrl(), templateDetails.getHypervisorArchLog()); + Path path = Path.of(TEMPLATES_PATH); + if (!Files.isWritable(path)) { + templateFile = new File(tempDownloadDir, templateDetails.getFilename()); + } + if (!templateFile.exists() && + !HttpUtils.downloadFileWithProgress(templateDetails.getUrl(), templateFile.getAbsolutePath(), + LOGGER)) { + return null; + } + templateDetails.setDownloadedFilePath(templateFile.getAbsolutePath()); + } + return templateFile; + } + protected boolean isTemplateFileChecksumDifferent(MetadataTemplateDetails templateDetails, File templateFile) { + String templateChecksum = DigestHelper.calculateChecksum(templateFile); + if (!templateChecksum.equals(templateDetails.getChecksum())) { + LOGGER.error("Checksum {} for file {} does not match checksum {} from metadata", + templateChecksum, templateFile, templateDetails.getChecksum()); + return true; + } + return false; + } + + protected void validateTemplates(List> hypervisorsArchInUse) { boolean templatesFound = true; - for (String hypervisor : hypervisors) { - String matchedTemplate = templates.stream().filter(x -> x.contains(hypervisor)).findAny().orElse(null); + for (Pair hypervisorArch : hypervisorsArchInUse) { + MetadataTemplateDetails matchedTemplate = getMetadataTemplateDetails(hypervisorArch.first(), + hypervisorArch.second()); if (matchedTemplate == null) { templatesFound = false; break; } - - File tempFile = new File(TEMPLATES_PATH + matchedTemplate); - String templateChecksum = DigestHelper.calculateChecksum(tempFile); - if (!templateChecksum.equals(NewTemplateChecksum.get(getHypervisorType(hypervisor)))) { - LOGGER.error(String.format("Checksum mismatch: %s != %s ", templateChecksum, NewTemplateChecksum.get(getHypervisorType(hypervisor)))); + File tempFile = getTemplateFile(matchedTemplate); + if (tempFile == null) { + LOGGER.warn("Failed to download template for {}, moving ahead", + matchedTemplate.getHypervisorArchLog()); + continue; + } + if (isTemplateFileChecksumDifferent(matchedTemplate, tempFile)) { templatesFound = false; break; } } - if (!templatesFound) { String errMsg = "SystemVm template not found. Cannot upgrade system Vms"; LOGGER.error(errMsg); @@ -750,7 +890,40 @@ public class SystemVmTemplateRegistration { } } - public void registerTemplates(Set hypervisorsInUse) { + protected void registerTemplatesForZone(long zoneId, String filePath) { + Pair storeUrlAndId = getNfsStoreInZone(zoneId); + String nfsVersion = getNfsVersion(storeUrlAndId.second()); + mountStore(storeUrlAndId.first(), filePath, nfsVersion); + List> hypervisorArchList = + clusterDao.listDistinctHypervisorsArchAcrossClusters(zoneId); + for (Pair hypervisorArch : hypervisorArchList) { + Hypervisor.HypervisorType hypervisorType = hypervisorArch.first(); + MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisorType, + hypervisorArch.second()); + if (templateDetails == null) { + continue; + } + VMTemplateVO templateVO = getRegisteredTemplate(templateDetails.getName(), templateDetails.getArch()); + if (templateVO != null) { + TemplateDataStoreVO templateDataStoreVO = + templateDataStoreDao.findByStoreTemplate(storeUrlAndId.second(), templateVO.getId()); + if (templateDataStoreVO != null) { + String installPath = templateDataStoreVO.getInstallPath(); + if (validateIfSeeded(templateDataStoreVO, storeUrlAndId.first(), installPath, nfsVersion)) { + continue; + } + } + registerTemplate(hypervisorType, templateDetails.getName(), storeUrlAndId.second(), templateVO, + templateDataStoreVO, filePath); + updateRegisteredTemplateDetails(templateVO.getId(), templateDetails); + continue; + } + registerTemplateForNonExistingEntries(hypervisorType, templateDetails.getArch(), templateDetails.getName(), + storeUrlAndId, filePath); + } + } + + public void registerTemplates(List> hypervisorsArchInUse) { GlobalLock lock = GlobalLock.getInternLock("UpgradeDatabase-Lock"); try { LOGGER.info("Grabbing lock to register templates."); @@ -758,7 +931,7 @@ public class SystemVmTemplateRegistration { throw new CloudRuntimeException("Unable to acquire lock to register SystemVM template."); } try { - validateTemplates(hypervisorsInUse); + validateTemplates(hypervisorsArchInUse); // Perform Registration if templates not already registered Transaction.execute(new TransactionCallbackNoReturn() { @Override @@ -771,27 +944,7 @@ public class SystemVmTemplateRegistration { 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); @@ -809,7 +962,7 @@ public class SystemVmTemplateRegistration { } } - private void updateRegisteredTemplateDetails(Long templateId, Map.Entry hypervisorAndTemplateName) { + private void updateRegisteredTemplateDetails(Long templateId, MetadataTemplateDetails templateDetails) { VMTemplateVO templateVO = vmTemplateDao.findById(templateId); templateVO.setTemplateType(Storage.TemplateType.SYSTEM); boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO); @@ -818,68 +971,81 @@ public class SystemVmTemplateRegistration { LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } - - updateSystemVMEntries(templateId, hypervisorAndTemplateName.getKey()); - + Hypervisor.HypervisorType hypervisorType = templateDetails.getHypervisorType(); + updateSystemVMEntries(templateId, hypervisorType); // Change value of global configuration parameter router.template.* for the corresponding hypervisor and minreq.sysvmtemplate.version for the ACS version Map configParams = new HashMap<>(); - configParams.put(RouterTemplateConfigurationNames.get(hypervisorAndTemplateName.getKey()), hypervisorAndTemplateName.getValue()); + configParams.put(RouterTemplateConfigurationNames.get(hypervisorType), templateDetails.getName()); configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion()); updateConfigurationParams(configParams); } - private void updateTemplateUrlAndChecksum(VMTemplateVO templateVO, Map.Entry hypervisorAndTemplateName) { - templateVO.setUrl(NewTemplateUrl.get(hypervisorAndTemplateName.getKey())); - templateVO.setChecksum(NewTemplateChecksum.get(hypervisorAndTemplateName.getKey())); + private void updateTemplateUrlAndChecksum(VMTemplateVO templateVO, MetadataTemplateDetails templateDetails) { + templateVO.setUrl(templateDetails.getUrl()); + templateVO.setChecksum(templateDetails.getChecksum()); boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO); if (!updated) { - String errMsg = String.format("updateSystemVmTemplates:Exception while updating 'url' and 'checksum' for hypervisor type %s", hypervisorAndTemplateName.getKey().name()); + String errMsg = String.format("updateSystemVmTemplates:Exception while updating 'url' and 'checksum' for hypervisor type %s", templateDetails.getHypervisorType()); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } } + protected boolean registerOrUpdateSystemVmTemplate(MetadataTemplateDetails templateDetails, + List> hypervisorsInUse) { + LOGGER.debug("Updating System VM template for {}", templateDetails.getHypervisorArchLog()); + VMTemplateVO registeredTemplate = getRegisteredTemplate(templateDetails.getName(), templateDetails.getArch()); + // change template type to SYSTEM + if (registeredTemplate != null) { + updateRegisteredTemplateDetails(registeredTemplate.getId(), templateDetails); + } else { + boolean isHypervisorArchMatchMetadata = hypervisorsInUse.stream() + .anyMatch(p -> p.first().equals(templateDetails.getHypervisorType()) + && Objects.equals(p.second(), templateDetails.getArch())); + if (isHypervisorArchMatchMetadata) { + try { + registerTemplates(hypervisorsInUse); + return true; + } catch (final Exception e) { + throw new CloudRuntimeException(String.format("Failed to register %s templates for hypervisors: [%s]. " + + "Cannot upgrade system VMs", + getSystemVmTemplateVersion(), + StringUtils.join(hypervisorsInUse.stream() + .map(x -> getHypervisorArchKey(x.first(), x.second())) + .collect(Collectors.toList()), ",")), e); + } + } else { + LOGGER.warn("Cannot upgrade {} system VM template for {} as it is not used, not failing upgrade", + getSystemVmTemplateVersion(), templateDetails.getHypervisorArchLog()); + VMTemplateVO templateVO = vmTemplateDao.findLatestTemplateByTypeAndHypervisorAndArch( + templateDetails.getHypervisorType(), templateDetails.getArch(), Storage.TemplateType.SYSTEM); + if (templateVO != null) { + updateTemplateUrlAndChecksum(templateVO, templateDetails); + } + } + } + return false; + } + public void updateSystemVmTemplates(final Connection conn) { LOGGER.debug("Updating System Vm template IDs"); Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(final TransactionStatus status) { - Set hypervisorsListInUse = new HashSet(); + List> hypervisorsInUse; try { - hypervisorsListInUse = clusterDao.getDistictAvailableHypervisorsAcrossClusters(); - + hypervisorsInUse = clusterDao.listDistinctHypervisorsArchAcrossClusters(null); } catch (final Exception e) { - LOGGER.error("updateSystemVmTemplates: Exception caught while getting hypervisor types from clusters: " + e.getMessage()); - throw new CloudRuntimeException("updateSystemVmTemplates:Exception while getting hypervisor types from clusters", e); + throw new CloudRuntimeException("Exception while getting hypervisor types from clusters", e); } - - for (final Map.Entry hypervisorAndTemplateName : NewTemplateNameList.entrySet()) { - LOGGER.debug("Updating " + hypervisorAndTemplateName.getKey() + " System Vms"); - Long templateId = getRegisteredTemplateId(new Pair<>(hypervisorAndTemplateName.getKey(), hypervisorAndTemplateName.getValue())); + Collection templateEntries = NewTemplateMap.values(); + for (MetadataTemplateDetails templateDetails : templateEntries) { try { - // change template type to SYSTEM - if (templateId != null) { - updateRegisteredTemplateDetails(templateId, hypervisorAndTemplateName); - } else { - if (hypervisorsListInUse.contains(hypervisorAndTemplateName.getKey())) { - try { - registerTemplates(hypervisorsListInUse); - break; - } catch (final Exception e) { - throw new CloudRuntimeException(String.format("%s %s SystemVm template not found. Cannot upgrade system Vms", getSystemVmTemplateVersion(), hypervisorAndTemplateName.getKey())); - } - } else { - LOGGER.warn(String.format("%s %s SystemVm template not found. Cannot upgrade system Vms hypervisor is not used, so not failing upgrade", - getSystemVmTemplateVersion(), hypervisorAndTemplateName.getKey())); - // Update the latest template URLs for corresponding hypervisor - VMTemplateVO templateVO = vmTemplateDao.findLatestTemplateByTypeAndHypervisor(hypervisorAndTemplateName.getKey(), Storage.TemplateType.SYSTEM); - if (templateVO != null) { - updateTemplateUrlAndChecksum(templateVO, hypervisorAndTemplateName); - } - } + if (registerOrUpdateSystemVmTemplate(templateDetails, hypervisorsInUse)) { + break; } } catch (final Exception e) { - String errMsg = "updateSystemVmTemplates:Exception while getting ids of templates"; + String errMsg = "Exception while registering/updating system VM templates for hypervisors in metadata"; LOGGER.error(errMsg, e); throw new CloudRuntimeException(errMsg, e); } @@ -888,4 +1054,77 @@ public class SystemVmTemplateRegistration { } }); } + + public String getNfsVersion(long storeId) { + final String configKey = "secstorage.nfs.version"; + final Map storeDetails = imageStoreDetailsDao.getDetails(storeId); + if (storeDetails != null && storeDetails.containsKey(configKey)) { + return storeDetails.get(configKey); + } + ConfigurationVO globalNfsVersion = configurationDao.findByName(configKey); + if (globalNfsVersion != null) { + return globalNfsVersion.getValue(); + } + return null; + } + + protected static class MetadataTemplateDetails { + private final Hypervisor.HypervisorType hypervisorType; + private final String name; + private final String filename; + private final String url; + private final String checksum; + private final CPU.CPUArch arch; + private String downloadedFilePath; + + MetadataTemplateDetails(Hypervisor.HypervisorType hypervisorType, String name, String filename, String url, + String checksum, CPU.CPUArch arch) { + this.hypervisorType = hypervisorType; + this.name = name; + this.filename = filename; + this.url = url; + this.checksum = checksum; + this.arch = arch; + } + + 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 getDownloadedFilePath() { + return downloadedFilePath; + } + + public void setDownloadedFilePath(String downloadedFilePath) { + this.downloadedFilePath = downloadedFilePath; + } + + public String getDefaultFilePath() { + return TEMPLATES_PATH + filename; + } + + public String getHypervisorArchLog() { + return SystemVmTemplateRegistration.getHypervisorArchLog(hypervisorType, arch); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/DatabaseAccessObject.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/DatabaseAccessObject.java index 1c2c4b3c7ce..223d7a46637 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 class DatabaseAccessObject { 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 class DatabaseAccessObject { } } + 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/DbUpgradeUtils.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeUtils.java index 51e6ac7b9a1..be073fcce77 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 class DbUpgradeUtils { } } + 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 class DbUpgradeUtils { } } + 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/Upgrade41700to41710.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41700to41710.java index e3eb2bf514d..266401e0c31 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 java.util.List; 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 { @@ -95,24 +99,58 @@ public class Upgrade41700to41710 extends DbUpgradeAbstractImpl implements DbUpgr } } - 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/Upgrade41900to41910.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to41910.java index f55580a7e19..4eb8e9bdb2a 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to41910.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to41910.java @@ -71,6 +71,10 @@ public class Upgrade41900to41910 extends DbUpgradeAbstractImpl implements DbUpgr 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"); @@ -81,8 +85,4 @@ public class Upgrade41900to41910 extends DbUpgradeAbstractImpl implements DbUpgr throw new CloudRuntimeException("Failed to find / register SystemVM template(s)"); } } - - private void initSystemVmTemplateRegistration() { - systemVmTemplateRegistration = new SystemVmTemplateRegistration(""); - } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to42000.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41910to41920.java similarity index 62% rename from engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to42000.java rename to engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41910to41920.java index 200c5fda232..6215021473e 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to42000.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41910to41920.java @@ -16,23 +16,21 @@ // under the License. package com.cloud.upgrade.dao; +import com.cloud.utils.exception.CloudRuntimeException; + 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; +public class Upgrade41910to41920 implements DbUpgrade { @Override public String[] getUpgradableVersionRange() { - return new String[] {"4.19.0.0", "4.20.0.0"}; + return new String[]{"4.19.1.0", "4.19.2.0"}; } @Override public String getUpgradedVersion() { - return "4.20.0.0"; + return "4.19.2.0"; } @Override @@ -42,13 +40,13 @@ public class Upgrade41900to42000 extends DbUpgradeAbstractImpl implements DbUpgr @Override public InputStream[] getPrepareScripts() { - final String scriptFile = "META-INF/db/schema-41900to42000.sql"; + 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}; + return new InputStream[]{script}; } @Override @@ -57,27 +55,12 @@ public class Upgrade41900to42000 extends DbUpgradeAbstractImpl implements DbUpgr @Override public InputStream[] getCleanupScripts() { - final String scriptFile = "META-INF/db/schema-41900to42000-cleanup.sql"; + 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}; - } - - 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)"); - } + 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 00000000000..4c26c6bde50 --- /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 00000000000..0c0a9f070ba --- /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 00000000000..fd52782e57c --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java @@ -0,0 +1,215 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; + +import java.io.InputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.framework.config.ConfigKey; + +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())); + } + } +} 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 00000000000..1385e69da03 --- /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 50884e3c012..b1f6d295fdd 100644 --- a/engine/schema/src/main/java/com/cloud/usage/UsageVO.java +++ b/engine/schema/src/main/java/com/cloud/usage/UsageVO.java @@ -113,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; } @@ -401,6 +404,14 @@ public class UsageVO implements Usage, InternalIdentity { 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"); 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 00000000000..e676b2bc2e9 --- /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 8a72182ec67..2a24016653d 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java @@ -24,7 +24,7 @@ import com.cloud.usage.UsageBackupVO; import com.cloud.utils.db.GenericDao; public interface UsageBackupDao extends GenericDao { - void updateMetrics(Long vmId, Long size, Long virtualSize); - void removeUsage(Long accountId, Long vmId, Date eventDate); + void updateMetrics(Long vmId, Long backupOfferingId, Long size, Long virtualSize); + void removeUsage(Long accountId, Long vmId, Long backupOfferingId, Date eventDate); List getUsageRecords(Long accountId, Date startDate, Date endDate); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java index 3403a8dfe5b..e5b46b02a59 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java @@ -36,16 +36,17 @@ import com.cloud.utils.db.TransactionLegacy; @Component public class UsageBackupDaoImpl extends GenericDaoBase implements UsageBackupDao { - protected static final String UPDATE_DELETED = "UPDATE usage_backup SET removed = ? WHERE account_id = ? AND vm_id = ? and removed IS NULL"; + protected static final String UPDATE_DELETED = "UPDATE usage_backup SET removed = ? WHERE account_id = ? AND vm_id = ? and backup_offering_id = ? and removed IS NULL"; protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, vm_id, backup_offering_id, size, protected_size, created, removed FROM usage_backup WHERE " + " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + " OR ((created <= ?) AND (removed >= ?)))"; @Override - public void updateMetrics(final Long vmId, final Long size, final Long virtualSize) { + public void updateMetrics(final Long vmId, Long backupOfferingId, final Long size, final Long virtualSize) { try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) { SearchCriteria sc = this.createSearchCriteria(); sc.addAnd("vmId", SearchCriteria.Op.EQ, vmId); + sc.addAnd("backupOfferingId", SearchCriteria.Op.EQ, backupOfferingId); UsageBackupVO vo = findOneBy(sc); if (vo != null) { vo.setSize(size); @@ -58,7 +59,7 @@ public class UsageBackupDaoImpl extends GenericDaoBase impl } @Override - public void removeUsage(Long accountId, Long vmId, Date eventDate) { + public void removeUsage(Long accountId, Long vmId, Long backupOfferingId, Date eventDate) { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { txn.start(); @@ -67,6 +68,7 @@ public class UsageBackupDaoImpl extends GenericDaoBase impl pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), eventDate)); pstmt.setLong(2, accountId); pstmt.setLong(3, vmId); + pstmt.setLong(3, backupOfferingId); pstmt.executeUpdate(); } } catch (SQLException e) { diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageDaoImpl.java index 2335043b7c5..fb925a28513 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 @@ -29,6 +29,7 @@ import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; @@ -538,21 +539,20 @@ public class UsageDaoImpl extends GenericDaoBase implements Usage @Override public void removeOldUsageRecords(int days) { - String sql = DELETE_ALL_BY_INTERVAL; - 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); - } finally { - txn.close(); - } + Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + PreparedStatement pstmt = null; + try { + pstmt = txn.prepareAutoCloseStatement(DELETE_ALL_BY_INTERVAL); + pstmt.setLong(1, days); + pstmt.executeUpdate(); + } catch (Exception ex) { + logger.error("error removing old cloud_usage records for interval: " + days); + } + } + }); } public UsageVO persistUsage(final UsageVO usage) { diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDao.java index f22a906054d..d4038d4ceeb 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDao.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDao.java @@ -37,4 +37,6 @@ public interface UsageJobDao extends GenericDao { UsageJobVO isOwner(String hostname, int pid); void updateJobSuccess(Long jobId, long startMillis, long endMillis, long execTime, boolean success); + + void removeLastOpenJobsOwned(String hostname, int pid); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java index 6d460aadd09..44a7d1a8b72 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java @@ -22,6 +22,7 @@ import java.util.Date; import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.usage.UsageJobVO; @@ -114,7 +115,7 @@ public class UsageJobDaoImpl extends GenericDaoBase implements public UsageJobVO isOwner(String hostname, int pid) { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { - if ((hostname == null) || (pid <= 0)) { + if (hostname == null || pid <= 0) { return null; } @@ -174,7 +175,7 @@ public class UsageJobDaoImpl extends GenericDaoBase implements SearchCriteria sc = createSearchCriteria(); sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); sc.addAnd("jobType", SearchCriteria.Op.EQ, Integer.valueOf(UsageJobVO.JOB_TYPE_SINGLE)); - sc.addAnd("scheduled", SearchCriteria.Op.EQ, Integer.valueOf(0)); + sc.addAnd("scheduled", SearchCriteria.Op.EQ, Integer.valueOf(UsageJobVO.JOB_NOT_SCHEDULED)); List jobs = search(sc, filter); if ((jobs == null) || jobs.isEmpty()) { @@ -194,4 +195,36 @@ public class UsageJobDaoImpl extends GenericDaoBase implements } return jobs.get(0).getHeartbeat(); } + + private List getLastOpenJobsOwned(String hostname, int pid) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); + sc.addAnd("host", SearchCriteria.Op.EQ, hostname); + if (pid > 0) { + sc.addAnd("pid", SearchCriteria.Op.EQ, Integer.valueOf(pid)); + } + return listBy(sc); + } + + @Override + public void removeLastOpenJobsOwned(String hostname, int pid) { + if (hostname == null) { + return; + } + + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + try { + List jobs = getLastOpenJobsOwned(hostname, pid); + if (CollectionUtils.isNotEmpty(jobs)) { + logger.info("Found {} opens job, to remove", jobs.size()); + for (UsageJobVO job : jobs) { + logger.debug("Removing job - id: {}, pid: {}, job type: {}, scheduled: {}, heartbeat: {}", + job.getId(), job.getPid(), job.getJobType(), job.getScheduled(), job.getHeartbeat()); + remove(job.getId()); + } + } + } finally { + txn.close(); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDao.java new file mode 100644 index 00000000000..4d84caaad44 --- /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 00000000000..e7ae622ae54 --- /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/UsageVpcDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDao.java new file mode 100644 index 00000000000..5167bf88c48 --- /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 00000000000..b5d8e46ef09 --- /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 863f6c96008..aa6e49666dd 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.GenerationType; 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 @@ public class AccountDetailVO implements InternalIdentity { } 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 class AccountDetailVO implements InternalIdentity { 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 f4534ee41ee..65bbe1670a8 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 @@ package com.cloud.user; 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 5451192fc6d..cbacf9af572 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 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.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 class AccountDetailsDaoImpl extends GenericDaoBase @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 class AccountDetailsDaoImpl extends GenericDaoBase } @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 class AccountDetailsDaoImpl extends GenericDaoBase 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 class AccountDetailsDaoImpl extends GenericDaoBase } 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 f04b2bafbde..74a538565d7 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 class AccountVO implements Account { 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 c18ca53f7ab..e4fcbad6b02 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,11 @@ import javax.persistence.Table; 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") @@ -361,6 +363,9 @@ public class UserAccountVO implements UserAccount, InternalIdentity { @Override public Map getDetails() { + if (details == null) { + details = new HashMap<>(); + } return details; } @@ -368,4 +373,10 @@ public class UserAccountVO implements UserAccount, InternalIdentity { 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 f54b1a8872e..e8864976069 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.GeneratedValue; 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 class UserDataVO implements UserData { @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 class UserDataVO implements UserData { return params; } + @Override + public boolean isForCks() { + return forCks; + } + public void setAccountId(long accountId) { this.accountId = accountId; } @@ -117,4 +132,14 @@ public class UserDataVO implements 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 69970bf2d2c..6e355e102e6 100644 --- a/engine/schema/src/main/java/com/cloud/user/UserVO.java +++ b/engine/schema/src/main/java/com/cloud/user/UserVO.java @@ -115,6 +115,9 @@ 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(); } @@ -293,7 +296,7 @@ public class UserVO implements User, Identity, InternalIdentity { @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 +353,13 @@ public class UserVO implements User, Identity, InternalIdentity { 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 17b07496731..dae5f3a3467 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/AccountDao.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/AccountDao.java @@ -16,6 +16,9 @@ // under the License. package com.cloud.user.dao; +import java.util.Date; +import java.util.List; + import com.cloud.user.Account; import com.cloud.user.AccountVO; import com.cloud.user.User; @@ -23,9 +26,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; -import java.util.Date; -import java.util.List; - public interface AccountDao extends GenericDao { Pair findUserAccountByApiKey(String apiKey); @@ -33,6 +33,8 @@ public interface AccountDao extends GenericDao { Pair, Integer> findAccountsLike(String accountName, Filter filter); + List findAccountsByName(String accountName); + List findActiveAccounts(Long maxAccountId, Filter filter); List findRecentlyDeletedAccounts(Long maxAccountId, Date earliestRemovedDate, Filter filter); diff --git a/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java b/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java index eed5572a0b2..f5f95d5da1f 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java @@ -16,6 +16,14 @@ // under the License. package com.cloud.user.dao; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + import com.cloud.user.Account; import com.cloud.user.Account.State; import com.cloud.user.AccountVO; @@ -30,19 +38,12 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; -import org.apache.commons.lang3.StringUtils; import com.cloud.utils.db.TransactionLegacy; -import org.springframework.stereotype.Component; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.util.Date; -import java.util.List; @Component public class AccountDaoImpl extends GenericDaoBase implements AccountDao { - 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 " + private static final String FIND_USER_ACCOUNT_BY_API_KEY = "SELECT u.id, u.username, u.account_id, u.secret_key, u.state, u.api_key_access, " + + "a.id, a.account_name, a.type, a.role_id, a.domain_id, a.state, a.api_key_access " + "FROM `cloud`.`user` u, `cloud`.`account` a " + "WHERE u.account_id = a.id AND u.api_key = ? and u.removed IS NULL"; protected final SearchBuilder AllFieldsSearch; @@ -148,13 +149,25 @@ public class AccountDaoImpl extends GenericDaoBase implements A u.setAccountId(rs.getLong(3)); u.setSecretKey(DBEncryptionUtil.decrypt(rs.getString(4))); u.setState(State.getValueOf(rs.getString(5))); + boolean apiKeyAccess = rs.getBoolean(6); + if (rs.wasNull()) { + u.setApiKeyAccess(null); + } else { + u.setApiKeyAccess(apiKeyAccess); + } - AccountVO a = new AccountVO(rs.getLong(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))); + AccountVO a = new AccountVO(rs.getLong(7)); + a.setAccountName(rs.getString(8)); + a.setType(Account.Type.getFromValue(rs.getInt(9))); + a.setRoleId(rs.getLong(10)); + a.setDomainId(rs.getLong(11)); + a.setState(State.getValueOf(rs.getString(12))); + apiKeyAccess = rs.getBoolean(13); + if (rs.wasNull()) { + a.setApiKeyAccess(null); + } else { + a.setApiKeyAccess(apiKeyAccess); + } userAcctPair = new Pair(u, a); } @@ -178,6 +191,16 @@ public class AccountDaoImpl extends GenericDaoBase implements A return searchAndCount(sc, filter); } + @Override + public List findAccountsByName(String accountName) { + SearchBuilder sb = createSearchBuilder(); + sb.and("accountName", sb.entity().getAccountName(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("accountName", accountName); + return search(sc, null); + } + @Override public Account findEnabledAccount(String accountName, Long domainId) { SearchCriteria sc = AllFieldsSearch.create("accountName", accountName); @@ -298,7 +321,7 @@ public class AccountDaoImpl extends GenericDaoBase implements A 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); } } } 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 81a11241e4b..ef777be2de9 100644 --- a/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java @@ -64,6 +64,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; } @@ -135,4 +141,20 @@ public class ConsoleSessionVO { public void setAcquired(Date acquired) { this.acquired = acquired; } + + public String getConsoleEndpointCreatorAddress() { + return consoleEndpointCreatorAddress; + } + + public void setConsoleEndpointCreatorAddress(String consoleEndpointCreatorAddress) { + this.consoleEndpointCreatorAddress = consoleEndpointCreatorAddress; + } + + public String getClientAddress() { + return clientAddress; + } + + public void setClientAddress(String clientAddress) { + this.clientAddress = clientAddress; + } } 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 4437af29bc1..d5bd8c5aaae 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 javax.persistence.Table; 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 @@ public class InstanceGroupVO implements InstanceGroup { 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 2d4a5e138fe..ab07d6989fa 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 ff727904dcb..0cc0a084443 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 @@ package com.cloud.vm; 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 class ItWorkDaoImpl extends GenericDaoBase implements I 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 936efd112b7..6c569e22dd9 100644 --- a/engine/schema/src/main/java/com/cloud/vm/NicVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/NicVO.java @@ -330,7 +330,10 @@ public class NicVO implements Nic { @Override public String toString() { - return String.format("Nic %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "instanceId", "deviceId", "broadcastUri", "reservationId", "iPv4Address")); + 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/UserVmVO.java b/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java index ce0bd2d5717..ce3a9a84a34 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 class UserVmVO extends VMInstanceVO implements UserVm { return updateParameters; } + @Override public String getUserVmType() { return userVmType; } diff --git a/engine/schema/src/main/java/com/cloud/vm/UserVmDetailVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceDetailVO.java similarity index 90% rename from engine/schema/src/main/java/com/cloud/vm/UserVmDetailVO.java rename to engine/schema/src/main/java/com/cloud/vm/VMInstanceDetailVO.java index 81bb6dd9d4f..7879aa24556 100755 --- a/engine/schema/src/main/java/com/cloud/vm/UserVmDetailVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceDetailVO.java @@ -26,8 +26,8 @@ import javax.persistence.Table; import org.apache.cloudstack.api.ResourceDetail; @Entity -@Table(name = "user_vm_details") -public class UserVmDetailVO implements ResourceDetail { +@Table(name = "vm_instance_details") +public class VMInstanceDetailVO implements ResourceDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @@ -45,10 +45,10 @@ public class UserVmDetailVO implements ResourceDetail { @Column(name = "display") private boolean display = true; - public UserVmDetailVO() { + public VMInstanceDetailVO() { } - public UserVmDetailVO(long vmId, String name, String value, boolean display) { + public VMInstanceDetailVO(long vmId, String name, String value, boolean display) { this.resourceId = vmId; this.name = name; this.value = value; diff --git a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java index a1600e04350..9d5e1b0ff50 100644 --- a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java @@ -167,10 +167,8 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject details; @@ -505,7 +503,7 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject 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 cb19748fda4..af32163b4c7 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 ef94a4d9f72..bc79194a10f 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.ArrayList; 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 class ConsoleProxyDaoImpl extends GenericDaoBase im } @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 71b1aed1938..95ced889b3d 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 @@ -23,6 +23,7 @@ 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 +33,7 @@ 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); } 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 8e7e229622e..3d117894670 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,9 @@ package com.cloud.vm.dao; import java.util.Date; +import java.util.List; + +import org.apache.commons.collections.CollectionUtils; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -59,11 +62,22 @@ public class ConsoleSessionDaoImpl extends GenericDaoBase 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/dao/NicDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java index 23c26ea0718..70a2558e2d4 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java @@ -46,8 +46,12 @@ public interface NicDao extends GenericDao { NicVO findByNetworkIdAndTypeIncludingRemoved(long networkId, VirtualMachine.Type vmType); + NicVO findNonPlaceHolderByNetworkIdAndType(long networkId, VirtualMachine.Type vmType); + NicVO findByIp4AddressAndNetworkId(String ip4Address, long networkId); + NicVO findNonPlaceHolderByIp4AddressAndNetworkId(String ip4Address, long networkId); + NicVO findByNetworkIdAndMacAddress(long networkId, String mac); NicVO findDefaultNicForVM(long instanceId); @@ -100,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 3eee1d4e749..3618785c1b8 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java @@ -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 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { AllFieldsSearch.and("secondaryip", AllFieldsSearch.entity().getSecondaryIp(), Op.EQ); AllFieldsSearch.and("nicid", AllFieldsSearch.entity().getId(), Op.EQ); AllFieldsSearch.and("strategy", AllFieldsSearch.entity().getReservationStrategy(), Op.EQ); + AllFieldsSearch.and("strategyNEQ", AllFieldsSearch.entity().getReservationStrategy(), Op.NEQ); AllFieldsSearch.and("reserverName",AllFieldsSearch.entity().getReserver(),Op.EQ); AllFieldsSearch.and("macAddress", AllFieldsSearch.entity().getMacAddress(), Op.EQ); AllFieldsSearch.and("deviceid", AllFieldsSearch.entity().getDeviceId(), Op.EQ); @@ -193,6 +196,15 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { return findByNetworkIdAndTypeInternal(networkId, vmType, true); } + @Override + public NicVO findNonPlaceHolderByNetworkIdAndType(long networkId, VirtualMachine.Type vmType) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("network", networkId); + sc.setParameters("vmType", vmType); + sc.setParameters("strategyNEQ", Nic.ReservationStrategy.PlaceHolder.toString()); + return findOneBy(sc); + } + @Override public NicVO findByNetworkIdTypeAndGateway(long networkId, VirtualMachine.Type vmType, String gateway) { SearchCriteria sc = AllFieldsSearch.create(); @@ -220,6 +232,16 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { return findOneBy(sc); } + @Override + public NicVO findNonPlaceHolderByIp4AddressAndNetworkId(String ip4Address, long networkId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("address", ip4Address); + sc.setParameters("network", networkId); + sc.setParameters("strategyNEQ", Nic.ReservationStrategy.PlaceHolder.toString()); + return findOneBy(sc); + } + + @Override public NicVO findByNetworkIdAndMacAddress(long networkId, String mac) { SearchCriteria sc = AllFieldsSearch.create(); @@ -428,4 +450,18 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { 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 69d9c00e1e0..7bae64a6acb 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 3056c73938e..0f3679d66a3 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 class NicExtraDhcpOptionDaoImpl extends GenericDaoBase 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 887b3d73087..44866c0a358 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 class NicIpAliasDaoImpl extends GenericDaoBase implem 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 cbb52e57282..ff7089ca427 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 a56d35d5a63..563b3279520 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 @@ package com.cloud.vm.dao; 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 class NicSecondaryIpDaoImpl extends GenericDaoBase 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 093434052bc..4c8208b4be8 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 javax.persistence.Table; 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 @@ public class NicSecondaryIpVO implements NicSecondaryIp { @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 39c65866658..7de543e69d3 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 536779125e2..41bcb3155e5 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java @@ -57,7 +57,7 @@ import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicVO; -import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.VMInstanceDetailVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; @@ -124,13 +124,13 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use + "left join security_group on security_group_vm_map.security_group_id=security_group.id " + "left join nics on vm_instance.id=nics.instance_id " + "left join networks on nics.network_id=networks.id " + "left join user_ip_address on user_ip_address.vm_id=vm_instance.id " + "where vm_instance.id in ("; - private static final String VMS_DETAIL_BY_NAME = "select vm_instance.instance_name, vm_instance.vm_type, vm_instance.id , user_vm_details.value, user_vm_details.name from vm_instance " - + "left join user_vm_details on vm_instance.id = user_vm_details.vm_id where (user_vm_details.name is null or user_vm_details.name = ? ) and vm_instance.instance_name in ("; + private static final String VMS_DETAIL_BY_NAME = "select vm_instance.instance_name, vm_instance.vm_type, vm_instance.id , vm_instance_details.value, vm_instance_details.name from vm_instance " + + "left join vm_instance_details on vm_instance.id = vm_instance_details.vm_id where (vm_instance_details.name is null or vm_instance_details.name = ? ) and vm_instance.instance_name in ("; private static final int VM_DETAILS_BATCH_SIZE = 100; @Inject - protected UserVmDetailsDao _detailsDao; + protected VMInstanceDetailsDao _detailsDao; @Inject protected NicDao _nicDao; @@ -274,8 +274,11 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use } @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); @@ -285,6 +288,7 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use vo.setUserDataDetails(userDataDetails); vo.setDisplayVm(displayVm); vo.setDynamicallyScalable(isDynamicallyScalable); + vo.setDeleteProtection(deleteProtection); if (hostName != null) { vo.setHostName(hostName); } @@ -441,10 +445,10 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use final Map visibilityMap = _detailsDao.listDetailsVisibility(vm.getId()); - List details = new ArrayList(); + List details = new ArrayList(); for (Map.Entry entry : detailsStr.entrySet()) { boolean display = !hiddenDetails.contains(entry.getKey()) && visibilityMap.getOrDefault(entry.getKey(), true); - details.add(new UserVmDetailVO(vm.getId(), entry.getKey(), entry.getValue(), display)); + details.add(new VMInstanceDetailVO(vm.getId(), entry.getKey(), entry.getValue(), display)); } _detailsDao.saveDetails(details); @@ -751,7 +755,7 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use while (rs.next()) { vmsDetailByNames.add(new Pair, Pair>(new Pair( rs.getString("vm_instance.instance_name"), VirtualMachine.Type.valueOf(rs.getString("vm_type"))), - new Pair(rs.getLong("vm_instance.id"), rs.getString("user_vm_details.value")))); + new Pair(rs.getLong("vm_instance.id"), rs.getString("vm_instance_details.value")))); } } } catch (SQLException e) { @@ -817,6 +821,7 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use reservationDao.setResourceId(Resource.ResourceType.user_vm, userVM.getId()); reservationDao.setResourceId(Resource.ResourceType.cpu, userVM.getId()); reservationDao.setResourceId(Resource.ResourceType.memory, userVM.getId()); + reservationDao.setResourceId(Resource.ResourceType.gpu, userVM.getId()); return userVM; }); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java index 42c00231aac..f722b4c54e4 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java @@ -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,47 @@ 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); + + 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 b7b787b0045..2f19d36c37a 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -20,25 +20,31 @@ import java.sql.PreparedStatement; 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; @@ -70,6 +76,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected SearchBuilder LHVMClusterSearch; protected SearchBuilder IdStatesSearch; protected SearchBuilder AllFieldsSearch; + protected SearchBuilder IdServiceOfferingIdSelectSearch; protected SearchBuilder ZoneTemplateNonExpungedSearch; protected SearchBuilder TemplateNonExpungedSearch; protected SearchBuilder NameLikeSearch; @@ -95,11 +102,17 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected SearchBuilder NotMigratingSearch; protected SearchBuilder BackupSearch; protected SearchBuilder LastHostAndStatesSearch; + protected SearchBuilder VmsNotInClusterUsingPool; + protected SearchBuilder IdsPowerStateSelectSearch; @Inject - ResourceTagDao _tagsDao; + ResourceTagDao tagsDao; @Inject - NicDao _nicDao; + NicDao nicDao; + @Inject + VolumeDao volumeDao; + @Inject + protected HostDao hostDao; protected Attribute _updateTimeAttr; @@ -118,20 +131,28 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem private static final String ORDER_HOSTS_NUMBER_OF_VMS_FOR_ACCOUNT_PART2 = " GROUP BY host.id ORDER BY 2 ASC "; - private static final String COUNT_VMS_BASED_ON_VGPU_TYPES1 = + private static final String COUNT_VMS_BASED_ON_VGPU_TYPES1_LEGACY = "SELECT pci, type, SUM(vmcount) FROM (SELECT MAX(IF(offering.name = 'pciDevice',value,'')) AS pci, MAX(IF(offering.name = 'vgpuType', value,'')) " + "AS type, COUNT(DISTINCT vm.id) AS vmcount FROM service_offering_details offering INNER JOIN vm_instance vm ON offering.service_offering_id = vm.service_offering_id " + "INNER JOIN `cloud`.`host` ON vm.host_id = host.id WHERE vm.state = 'Running' AND host.data_center_id = ? "; + private static final String COUNT_VMS_BASED_ON_VGPU_TYPES2_LEGACY = + "GROUP BY vm.service_offering_id) results GROUP BY pci, type"; + + private static final String COUNT_VMS_BASED_ON_VGPU_TYPES1 = + "SELECT CONCAT(gpu_card.vendor_name, ' ', gpu_card.device_name), vgpu_profile.name, COUNT(gpu_device.vm_id) " + + "FROM `cloud`.`gpu_device` " + + "INNER JOIN `cloud`.`host` ON gpu_device.host_id = host.id " + + "INNER JOIN `cloud`.`gpu_card` ON gpu_device.card_id = gpu_card.id " + + "INNER JOIN `cloud`.`vgpu_profile` ON vgpu_profile.id = gpu_device.vgpu_profile_id " + + "WHERE vm_id IS NOT NULL AND host.data_center_id = ? "; private static final String COUNT_VMS_BASED_ON_VGPU_TYPES2 = - "GROUP BY offering.service_offering_id) results GROUP BY pci, type"; + "GROUP BY gpu_card.name, vgpu_profile.name"; private static final String UPDATE_SYSTEM_VM_TEMPLATE_ID_FOR_HYPERVISOR = "UPDATE `cloud`.`vm_instance` SET vm_template_id = ? WHERE type <> 'User' AND hypervisor_type = ? AND removed is NULL"; 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 +166,13 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem 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 +188,14 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem 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 +295,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem 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 +306,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem _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 +335,25 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem 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(); } @Override @@ -440,10 +489,10 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem } @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 +606,13 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem 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 +661,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem } @Override - public List listByZoneWithBackups(Long zoneId, Long backupOfferingId) { + public List listByZoneAndBackupOffering(Long zoneId, Long backupOfferingId) { SearchCriteria sc = BackupSearch.create(); sc.setParameters("zone_id", zoneId); if (backupOfferingId != null) { @@ -755,40 +804,52 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem @Override public HashMap countVgpuVMs(Long dcId, Long podId, Long clusterId) { + StringBuilder finalQueryLegacy = new StringBuilder(); StringBuilder finalQuery = new StringBuilder(); TransactionLegacy txn = TransactionLegacy.currentTxn(); + PreparedStatement pstmtLegacy = null; PreparedStatement pstmt = null; List resourceIdList = new ArrayList(); HashMap result = new HashMap(); resourceIdList.add(dcId); + finalQueryLegacy.append(COUNT_VMS_BASED_ON_VGPU_TYPES1_LEGACY); finalQuery.append(COUNT_VMS_BASED_ON_VGPU_TYPES1); if (podId != null) { + finalQueryLegacy.append("AND host.pod_id = ? "); finalQuery.append("AND host.pod_id = ? "); resourceIdList.add(podId); } if (clusterId != null) { + finalQueryLegacy.append("AND host.cluster_id = ? "); finalQuery.append("AND host.cluster_id = ? "); resourceIdList.add(clusterId); } + finalQueryLegacy.append(COUNT_VMS_BASED_ON_VGPU_TYPES2_LEGACY); finalQuery.append(COUNT_VMS_BASED_ON_VGPU_TYPES2); try { + pstmtLegacy = txn.prepareAutoCloseStatement(finalQueryLegacy.toString()); pstmt = txn.prepareAutoCloseStatement(finalQuery.toString()); for (int i = 0; i < resourceIdList.size(); i++) { + pstmtLegacy.setLong(1 + i, resourceIdList.get(i)); pstmt.setLong(1 + i, resourceIdList.get(i)); } - ResultSet rs = pstmt.executeQuery(); + ResultSet rs = pstmtLegacy.executeQuery(); + while (rs.next()) { + result.put(rs.getString(1).concat(rs.getString(2)), rs.getLong(3)); + } + rs = pstmt.executeQuery(); while (rs.next()) { result.put(rs.getString(1).concat(rs.getString(2)), rs.getLong(3)); } return result; } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + finalQuery, e); + throw new CloudRuntimeException("DB Exception on: " + finalQueryLegacy, e); } catch (Throwable e) { - throw new CloudRuntimeException("Caught: " + finalQuery, e); + throw new CloudRuntimeException("Caught: " + finalQueryLegacy, e); } } @@ -834,8 +895,9 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem 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 +933,24 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem 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,59 +966,124 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem return listBy(sc); } - @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 != powerHostId - || !isPowerStateInSyncWithInstanceState(powerState, powerHostId, instance)) { - 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); - } - } - } - return needToUpdate; + 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((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; + } + instance.setPowerStateUpdateTime(DateUtil.currentGMTTime()); + update(instanceId, instance); + return true; // Return true since an update occurred + } + return false; }); } + @Override + 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)) { - logger.debug(String.format("VM id: %d on host id: %d and power host id: %d is in %s state, but power state is %s", - instance.getId(), instance.getHostId(), powerHostId, instanceState, powerState)); + 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 long instanceId) { - VMInstanceVO instance = findById(instanceId); - if(instance == null) { - throw new CloudRuntimeException("checking power state update count on non existing instance " + instanceId); - } + public boolean isPowerStateUpToDate(final VMInstanceVO instance) { return instance.getPowerStateUpdateCount() < MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT; } @@ -964,6 +1102,25 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem }); } + @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() { @@ -1016,4 +1173,87 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem 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 List listByIdsIncludingRemoved(List ids) { + SearchBuilder idsSearch = createSearchBuilder(); + idsSearch.and("ids", idsSearch.entity().getId(), SearchCriteria.Op.IN); + idsSearch.done(); + SearchCriteria sc = idsSearch.create(); + sc.setParameters("ids", ids.toArray()); + return listIncludingRemovedBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java similarity index 84% rename from engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDao.java rename to engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java index c22da6b4ff5..ea9ac5afba6 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java @@ -19,7 +19,7 @@ package com.cloud.vm.dao; import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; import com.cloud.utils.db.GenericDao; -import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.VMInstanceDetailVO; -public interface UserVmDetailsDao extends GenericDao, ResourceDetailsDao { +public interface VMInstanceDetailsDao extends GenericDao, ResourceDetailsDao { } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java similarity index 81% rename from engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDaoImpl.java rename to engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java index d8f751842d5..ca11b005fb2 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java @@ -21,14 +21,14 @@ import org.springframework.stereotype.Component; import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; -import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.VMInstanceDetailVO; @Component -public class UserVmDetailsDaoImpl extends ResourceDetailsDaoBase implements UserVmDetailsDao { +public class VMInstanceDetailsDaoImpl extends ResourceDetailsDaoBase implements VMInstanceDetailsDao { @Override public void addDetail(long resourceId, String key, String value, boolean display) { - super.addDetail(new UserVmDetailVO(resourceId, key, value, display)); + super.addDetail(new VMInstanceDetailVO(resourceId, key, value, display)); } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDao.java index 879faaf5c90..0d7aa703a8c 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 1bef8f0626c..327acec0c17 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 java.util.List; 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 @@ import com.cloud.vm.VmStatsVO; @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 class VmStatsDaoImpl extends GenericDaoBase implements V } @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 c48396ad021..5b6f97b82e7 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 javax.persistence.Transient; 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 class VMSnapshotVO implements VMSnapshot { 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 31999ef15d6..4045af58d4b 100644 --- a/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDao.java @@ -27,6 +27,8 @@ public interface VMSnapshotDao extends GenericDao, StateDao< List findByVm(Long vmId); + List findByVmAndByType(Long vmId, VMSnapshot.Type type); + List listExpungingSnapshot(); List listByInstanceId(Long vmId, VMSnapshot.State... status); @@ -35,7 +37,11 @@ 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 062960130ac..83411b3cf8f 100644 --- a/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java @@ -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 @@ public class VMSnapshotDaoImpl extends GenericDaoBase implem SnapshotStatusSearch.and("vm_id", SnapshotStatusSearch.entity().getVmId(), SearchCriteria.Op.EQ); SnapshotStatusSearch.and("state", SnapshotStatusSearch.entity().getState(), SearchCriteria.Op.IN); SnapshotStatusSearch.done(); + + parentIdEqAndStateIn = createSearchBuilder(); + parentIdEqAndStateIn.and(PARENT, parentIdEqAndStateIn.entity().getParent(), Op.EQ); + parentIdEqAndStateIn.and(STATE, parentIdEqAndStateIn.entity().getState(), Op.IN); + parentIdEqAndStateIn.done(); } @Override @@ -78,6 +91,14 @@ public class VMSnapshotDaoImpl extends GenericDaoBase implem return listBy(sc, null); } + @Override + public List findByVmAndByType(Long vmId, VMSnapshot.Type type) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vm_id", vmId); + sc.setParameters("vm_snapshot_type", type); + return listBy(sc, null); + } + @Override public List listExpungingSnapshot() { SearchCriteria sc = ExpungingSnapshotSearch.create(); @@ -109,6 +130,14 @@ public class VMSnapshotDaoImpl extends GenericDaoBase implem return listBy(sc, null); } + @Override + public List listByParentAndStateIn(Long vmSnapshotId, State... states) { + SearchCriteria sc = parentIdEqAndStateIn.create(); + sc.setParameters(PARENT, vmSnapshotId); + sc.setParameters(STATE, (Object[])states); + return listBy(sc, null); + } + @Override public VMSnapshotVO findByName(Long vmId, String name) { SearchCriteria sc = AllFieldsSearch.create(); @@ -174,10 +203,35 @@ public class VMSnapshotDaoImpl extends GenericDaoBase implem .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 VM 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/RoleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java index d4647255fc6..cff139a9263 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 class RoleVO implements Role { @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 class RoleVO implements Role { 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/RoleDao.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java index 2d4151afc7d..f4fdb6a2b16 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,15 +28,15 @@ import java.util.List; 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 2e8fdd5fcc2..48c0d828a41 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 @@ -50,11 +50,13 @@ public class RoleDaoImpl extends GenericDaoBase implements RoleDao 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(); @@ -65,16 +67,17 @@ public class RoleDaoImpl extends GenericDaoBase implements RoleDao 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)) { @@ -83,19 +86,25 @@ public class RoleDaoImpl extends GenericDaoBase implements RoleDao 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)); } @@ -117,8 +126,11 @@ public class RoleDaoImpl extends GenericDaoBase implements RoleDao } @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)); } 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 536b96c6567..9b8fc598171 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.Id; 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 class AffinityGroupVO implements AffinityGroup { @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 010720ba33a..859b2921504 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 3bd7c6d082b..5bd598f36a0 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.SearchBuilder; 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 class AffinityGroupDaoImpl extends GenericDaoBase @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 class AffinityGroupDaoImpl extends GenericDaoBase 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 00000000000..aaf63518708 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupDetailVO.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.backup; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.ResourceDetail; + +@Entity +@Table(name = "backup_details") +public class BackupDetailVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "backup_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value", length = 65536) + private String value; + + @Column(name = "display") + private boolean display = true; + + public BackupDetailVO() { + } + + public BackupDetailVO(long backupId, String name, String value, boolean display) { + this.resourceId = backupId; + this.name = name; + this.value = value; + this.display = display; + } + + @Override + public long getId() { + return id; + } + + @Override + public long getResourceId() { + return resourceId; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public boolean isDisplay() { + return display; + } + + public void setId(long id) { + this.id = id; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + public void setName(String name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java new file mode 100644 index 00000000000..98efa94ceca --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.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.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 = "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) { + this(); + this.zoneId = zoneId; + this.provider = provider; + this.name = name; + this.type = type; + this.address = address; + this.mountOptions = mountOptions; + this.capacityBytes = capacityBytes; + 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 Long getUsedBytes() { + return usedBytes; + } + + @Override + public void setUsedBytes(Long usedBytes) { + this.usedBytes = usedBytes; + } + + @Override + public Long getCapacityBytes() { + return capacityBytes; + } + + @Override + public void setCapacityBytes(Long capacityBytes) { + this.capacityBytes = capacityBytes; + } + + public Date getCreated() { + return created; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java index ba31dc59d39..37e8105e3d5 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.backup; import java.util.Date; +import java.util.UUID; import javax.persistence.Column; import javax.persistence.Entity; @@ -29,6 +30,7 @@ import javax.persistence.Temporal; 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,29 @@ 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; + 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) { this.vmId = vmId; this.scheduleType = (short) scheduleType.ordinal(); this.schedule = schedule; this.timezone = timezone; this.scheduledTimestamp = scheduledTimestamp; + this.maxBackups = maxBackups; + this.quiesceVM = quiesceVM; + } + + @Override + public String toString() { + return String.format("BackupSchedule %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "vmId", "schedule", "scheduleType")); } @Override @@ -73,6 +92,11 @@ public class BackupScheduleVO implements BackupSchedule { return id; } + @Override + public String getUuid() { + return uuid; + } + public Long getVmId() { return vmId; } @@ -121,4 +145,20 @@ public class BackupScheduleVO implements BackupSchedule { 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; + } } 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 3e5db0443d8..0f8a10fb7be 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java @@ -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.Id; 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 class BackupVO implements Backup { } @Override - public long getVmId() { + public Long getVmId() { return vmId; } - public void setVmId(long vmId) { + public void setVmId(Long vmId) { this.vmId = vmId; } @@ -158,6 +188,7 @@ public class BackupVO implements Backup { this.status = status; } + @Override public long getBackupOfferingId() { return backupOfferingId; } @@ -199,14 +230,62 @@ public class BackupVO implements Backup { @Override public String getName() { - return null; + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + public List getBackedUpVolumes() { + 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 5d2f5ac64d6..e60e49e1a0c 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.backup.dao; import java.util.List; -import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.backup.BackupVO; @@ -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 fefbb68ae77..fd29da72c71 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java @@ -19,27 +19,31 @@ package org.apache.cloudstack.backup.dao; 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 org.apache.cloudstack.backup.Backup; -import org.apache.cloudstack.backup.BackupOffering; -import org.apache.cloudstack.backup.BackupVO; +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.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 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac 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 class BackupDaoImpl extends GenericDaoBase implements Bac } @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 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac return findOneBy(sc); } + @Override + public List searchByVmIds(List vmIds) { + if (CollectionUtils.isEmpty(vmIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getVmId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return search(sc, null); + } + public BackupVO getBackupVO(Backup backup) { BackupVO backupVO = new BackupVO(); backupVO.setExternalId(backup.getExternalId()); @@ -123,6 +186,13 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac 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 class BackupDaoImpl extends GenericDaoBase implements Bac expunge(sc); } + @Override + public BackupVO persist(BackupVO backup) { + return Transaction.execute((TransactionCallback) status -> { + BackupVO backupDb = super.persist(backup); + saveDetails(backup); + loadDetails(backupDb); + return backupDb; + }); + } + + @Override + public boolean update(Long id, BackupVO backup) { + return Transaction.execute((TransactionCallback) status -> { + boolean result = super.update(id, backup); + if (result) { + saveDetails(backup); + } + return result; + }); + } + @Override public List syncBackups(Long zoneId, Long vmId, List externalBackups) { for (Backup backup : externalBackups) { @@ -140,33 +231,53 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac } @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()); + 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); + } - 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; + @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 00000000000..664650074bc --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDao.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.backup.dao; + +import org.apache.cloudstack.backup.BackupDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import com.cloud.utils.db.GenericDao; + +public interface BackupDetailsDao extends GenericDao, ResourceDetailsDao { + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDaoImpl.java new file mode 100644 index 00000000000..08c7192af90 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDaoImpl.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.backup.dao; + + +import org.apache.cloudstack.backup.BackupDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.springframework.stereotype.Component; + +@Component +public class BackupDetailsDaoImpl extends ResourceDetailsDaoBase implements BackupDetailsDao { + + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new BackupDetailVO(resourceId, key, value, display)); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java index 0568a0185bb..9d67d07fe5e 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java @@ -58,6 +58,7 @@ public class BackupOfferingDaoImpl extends GenericDaoBase { + 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 00000000000..ea969988e2b --- /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 516b0112c98..ee1783a9c89 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 @@ package org.apache.cloudstack.backup.dao; 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 @@ import com.cloud.utils.db.GenericDao; public interface BackupScheduleDao extends GenericDao { BackupScheduleVO findByVM(Long vmId); + List listByVM(Long vmId); + + BackupScheduleVO findByVMAndIntervalType(Long vmId, DateUtil.IntervalType intervalType); + List getSchedulesToExecute(Date currentTimestamp); BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java index 7a58679e7e5..d9cf7b63680 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 java.util.List; import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.utils.DateUtil; import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.backup.BackupSchedule; import org.apache.cloudstack.backup.BackupScheduleVO; @@ -49,6 +50,7 @@ public class BackupScheduleDaoImpl extends GenericDaoBase 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 class BackupScheduleDaoImpl extends GenericDaoBase { + + List listByManagementServerId(long managementServerId); + + List listByHostId(long hostId); + + List listByState(Command.State... states); + + void removeCommand(long commandId, String commandName, Command.State state); + + ReconcileCommandVO findCommand(long reqSequence, String commandName); + + void updateCommandsToInterruptedByManagementServerId(long managementServerId); + + void updateCommandsToInterruptedByHostId(long hostId); + + List listByResourceIdAndTypeAndStates(long resourceId, ApiCommandResourceType resourceType, Command.State... states); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDaoImpl.java new file mode 100644 index 00000000000..593f464f9ad --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDaoImpl.java @@ -0,0 +1,134 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command.dao; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.command.ReconcileCommandVO; +import org.springframework.stereotype.Component; + +import com.cloud.agent.api.Command; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@DB +public class ReconcileCommandDaoImpl extends GenericDaoBase implements ReconcileCommandDao { + + final SearchBuilder updateCommandSearch; + final SearchBuilder resourceSearch; + + public ReconcileCommandDaoImpl() { + + updateCommandSearch = createSearchBuilder(); + updateCommandSearch.and("managementServerId", updateCommandSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); + updateCommandSearch.and("stateByManagement", updateCommandSearch.entity().getStateByManagement(), SearchCriteria.Op.IN); + updateCommandSearch.and("hostId", updateCommandSearch.entity().getHostId(), SearchCriteria.Op.EQ); + updateCommandSearch.and("stateByAgent", updateCommandSearch.entity().getStateByAgent(), SearchCriteria.Op.IN); + updateCommandSearch.and("reqSequence", updateCommandSearch.entity().getRequestSequence(), SearchCriteria.Op.EQ); + updateCommandSearch.and("commandName", updateCommandSearch.entity().getCommandName(), SearchCriteria.Op.EQ); + updateCommandSearch.done(); + + resourceSearch = createSearchBuilder(); + resourceSearch.and("resourceId", resourceSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + resourceSearch.and("resourceType", resourceSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + resourceSearch.and("stateByManagement", resourceSearch.entity().getStateByManagement(), SearchCriteria.Op.IN); + resourceSearch.done(); + } + + @Override + public List listByManagementServerId(long managementServerId) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getManagementServerId(), SearchCriteria.Op.EQ, managementServerId); + return sc.list(); + } + + @Override + public List listByHostId(long hostId) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getHostId(), SearchCriteria.Op.EQ, hostId); + return sc.list(); + } + + @Override + public List listByState(Command.State... states) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getStateByManagement(), SearchCriteria.Op.IN, (Object[]) states); + return sc.list(); + } + + @Override + public void removeCommand(long reqSequence, String commandName, Command.State state) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("reqSequence", reqSequence); + sc.setParameters("commandName", commandName); + + ReconcileCommandVO vo = createForUpdate(); + if (state != null) { + vo.setStateByManagement(state); + } + vo.setRemoved(new Date()); + update(vo, sc); + } + + @Override + public ReconcileCommandVO findCommand(long reqSequence, String commandName) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("reqSequence", reqSequence); + sc.setParameters("commandName", commandName); + return findOneBy(sc); + } + + @Override + public void updateCommandsToInterruptedByManagementServerId(long managementServerId) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("managementServerId", managementServerId); + sc.setParameters("stateByManagement", Command.State.CREATED, Command.State.RECONCILING); + + ReconcileCommandVO vo = createForUpdate(); + vo.setStateByManagement(Command.State.INTERRUPTED); + + update(vo, sc); + } + + @Override + public void updateCommandsToInterruptedByHostId(long hostId) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("stateByAgent", Command.State.STARTED, Command.State.PROCESSING, Command.State.PROCESSING_IN_BACKEND); + + ReconcileCommandVO vo = createForUpdate(); + vo.setStateByAgent(Command.State.INTERRUPTED); + + update(vo, sc); + } + + @Override + public List listByResourceIdAndTypeAndStates(long resourceId, ApiCommandResourceType resourceType, Command.State... states) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getResourceId(), SearchCriteria.Op.EQ, resourceId); + sc.and(sc.entity().getResourceType(), SearchCriteria.Op.EQ, resourceType); + sc.and(sc.entity().getStateByManagement(), SearchCriteria.Op.IN, (Object[]) states); + return sc.list(); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnetVO.java b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnetVO.java new file mode 100644 index 00000000000..828e7b39e9a --- /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 00000000000..e231b267fda --- /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 00000000000..49e8a6ef662 --- /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 36aefa201f3..3c35f59659f 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 @@ -18,6 +18,7 @@ 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; @@ -57,6 +58,13 @@ public class DirectDownloadCertificateVO implements DirectDownloadCertificate { 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 af48e5e04ac..917f8bb800a 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 @@ -50,6 +50,7 @@ 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") @@ -460,7 +461,7 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject { + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java new file mode 100644 index 00000000000..bc58c5f80f3 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.db.GenericDaoBase; +import org.apache.cloudstack.gui.theme.GuiThemeVO; +import org.springframework.stereotype.Component; + +@Component +public class GuiThemeDaoImpl extends GenericDaoBase implements GuiThemeDao { +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java new file mode 100644 index 00000000000..af243b1ffa4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeDetailsVO; + +import java.util.List; + +public interface GuiThemeDetailsDao extends GenericDao { + List listGuiThemeIdsByCommonName(String commonName); + + List listGuiThemeIdsByDomainUuids(String domainUuid); + + void expungeByGuiThemeId(long guiThemeId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java new file mode 100644 index 00000000000..b0969833eb0 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java @@ -0,0 +1,126 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.gui.theme.GuiThemeDetailsVO; +import org.apache.cloudstack.gui.theme.GuiThemeVO; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Component +public class GuiThemeDetailsDaoImpl extends GenericDaoBase implements GuiThemeDetailsDao { + + @Inject + DomainDao domainDao; + + @Inject + GuiThemeDao guiThemeDao; + + public List listGuiThemeIdsByCommonName(String commonName) { + GenericSearchBuilder detailsDaoSearchBuilder = createSearchBuilder(Long.class); + detailsDaoSearchBuilder.selectFields(detailsDaoSearchBuilder.entity().getGuiThemeId()); + detailsDaoSearchBuilder.and("commonNameType", detailsDaoSearchBuilder.entity().getType(), SearchCriteria.Op.EQ); + detailsDaoSearchBuilder.and().op("firstReplace", detailsDaoSearchBuilder.entity().getValue(), SearchCriteria.Op.LIKE_REPLACE); + detailsDaoSearchBuilder.or("secondReplace", detailsDaoSearchBuilder.entity().getValue(), SearchCriteria.Op.LIKE_REPLACE).cp(); + detailsDaoSearchBuilder.done(); + + SearchCriteria searchCriteria = detailsDaoSearchBuilder.create(); + searchCriteria.setParameters("commonNameType", "commonName"); + searchCriteria.setParameters("firstReplace", commonName, "*", "%"); + searchCriteria.setParameters("secondReplace", commonName, "*.", "%"); + + return customSearch(searchCriteria, null); + } + + public List listGuiThemeIdsByDomainUuids(String domainUuid) { + List guiThemeIds = new ArrayList<>(); + String requestedDomainPath = domainDao.findByUuid(domainUuid).getPath(); + + SearchBuilder domainSearchBuilderPathLike = domainDao.createSearchBuilder(); + domainSearchBuilderPathLike.and("pathLike", domainSearchBuilderPathLike.entity().getPath(), SearchCriteria.Op.LIKE_CONCAT); + + SearchBuilder domainSearchBuilderPathEq = domainDao.createSearchBuilder(); + domainSearchBuilderPathEq.and("pathEq", domainSearchBuilderPathEq.entity().getPath(), SearchCriteria.Op.EQ); + + GenericSearchBuilder detailsSearchBuilderPathLike = createDetailsSearchBuilder(domainSearchBuilderPathLike); + SearchCriteria searchCriteriaDomainPathLike = setParametersDomainPathLike(detailsSearchBuilderPathLike, requestedDomainPath); + + GenericSearchBuilder detailsSearchBuilderPathEq = createDetailsSearchBuilder(domainSearchBuilderPathEq); + SearchCriteria searchCriteriaDomainPathEq = setParametersDomainPathEq(detailsSearchBuilderPathEq, requestedDomainPath); + + guiThemeIds.addAll(customSearch(searchCriteriaDomainPathLike, null)); + guiThemeIds.addAll(customSearch(searchCriteriaDomainPathEq, null)); + return guiThemeIds; + } + + private SearchCriteria setParametersDomainPathLike(GenericSearchBuilder detailsSearchBuilderPathLike, String requestedDomainPath) { + SearchCriteria searchCriteria = detailsSearchBuilderPathLike.create(); + searchCriteria.setParameters("domainUuidType", "domain"); + searchCriteria.setJoinParameters("domainJoin", "pathLike", requestedDomainPath, "%"); + searchCriteria.setJoinParameters("guiThemesJoin", "recursiveDomains", true); + + return searchCriteria; + } + + private SearchCriteria setParametersDomainPathEq(GenericSearchBuilder detailsSearchBuilderPathEq, String requestedDomainPath) { + SearchCriteria searchCriteria = detailsSearchBuilderPathEq.create(); + searchCriteria.setParameters("domainUuidType", "domain"); + searchCriteria.setJoinParameters("domainJoin", "pathEq", requestedDomainPath); + searchCriteria.setJoinParameters("guiThemesJoin", "recursiveDomains", false); + + return searchCriteria; + } + + private GenericSearchBuilder createDetailsSearchBuilder(SearchBuilder domainSearchBuilder) { + SearchBuilder guiThemeDaoSearchBuilder = guiThemeDao.createSearchBuilder(); + guiThemeDaoSearchBuilder.and("recursiveDomains", guiThemeDaoSearchBuilder.entity().isRecursiveDomains(), SearchCriteria.Op.EQ); + + GenericSearchBuilder guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder = createSearchBuilder(Long.class); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.selectFields(guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getGuiThemeId()); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.and("domainUuidType", guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getType(), SearchCriteria.Op.EQ); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.join("domainJoin", domainSearchBuilder, domainSearchBuilder.entity().getUuid(), + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getValue(), JoinBuilder.JoinType.INNER); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.join("guiThemesJoin", guiThemeDaoSearchBuilder, guiThemeDaoSearchBuilder.entity().getId(), + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getGuiThemeId(), JoinBuilder.JoinType.INNER); + + domainSearchBuilder.done(); + guiThemeDaoSearchBuilder.done(); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.done(); + + return guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder; + } + + public void expungeByGuiThemeId(long guiThemeId) { + SearchBuilder searchBuilder = createSearchBuilder(); + searchBuilder.and("guiThemeId", searchBuilder.entity().getGuiThemeId(), SearchCriteria.Op.EQ); + searchBuilder.done(); + + SearchCriteria searchCriteria = searchBuilder.create(); + searchCriteria.setParameters("guiThemeId", guiThemeId); + expunge(searchCriteria); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java new file mode 100644 index 00000000000..740199cfca7 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeJoinVO; + +import java.util.List; + +public interface GuiThemeJoinDao extends GenericDao { + GuiThemeJoinVO findDefaultTheme(); + + Pair, Integer> listGuiThemesWithNoAuthentication(String commonName); + + Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, boolean showRemoved, Boolean showPublic); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java new file mode 100644 index 00000000000..ce6f7055812 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java @@ -0,0 +1,139 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.gui.theme.GuiThemeJoinVO; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Component +public class GuiThemeJoinDaoImpl extends GenericDaoBase implements GuiThemeJoinDao { + @Inject + GuiThemeDetailsDao guiThemeDetailsDao; + + public static final Long INVALID_ID = -1L; + + public GuiThemeJoinVO findDefaultTheme() { + SearchBuilder searchBuilder = createSearchBuilder(); + searchBuilder.and("commonNames", searchBuilder.entity().getCommonNames(), SearchCriteria.Op.NULL); + searchBuilder.and("domainUuids", searchBuilder.entity().getDomains(), SearchCriteria.Op.NULL); + searchBuilder.and("accountUuids", searchBuilder.entity().getAccounts(), SearchCriteria.Op.NULL); + searchBuilder.done(); + + SearchCriteria searchCriteria = searchBuilder.create(); + + return findOneBy(searchCriteria); + } + + public Pair, Integer> listGuiThemesWithNoAuthentication(String commonName) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(null, null, commonName, null, null, null, false); + return searchOrderByCreatedDate(searchCriteria, false); + } + + public Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, + boolean showRemoved, Boolean showPublic) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(id, name, commonName, domainUuid, accountUuid, showPublic, listAll); + + if (listAll) { + showRemoved = false; + } + + return searchOrderByCreatedDate(searchCriteria, showRemoved); + } + + private Pair, Integer> searchOrderByCreatedDate(SearchCriteria searchCriteria, boolean showRemoved) { + Filter filter = new Filter(GuiThemeJoinVO.class, "created", false); + return searchAndCount(searchCriteria, filter, showRemoved); + } + + private SearchCriteria createGuiThemeSearchCriteria(Long id, String name, String commonName, String domainUuid, String accountUuid, Boolean showPublic, boolean listAll) { + SearchCriteria searchCriteria = createGuiThemeJoinSearchBuilder(listAll, showPublic).create(); + List idList = new ArrayList<>(); + + if (id != null) { + idList.add(id); + } + + searchCriteria.setParametersIfNotNull("name", name); + searchCriteria.setParametersIfNotNull("isPublic", showPublic); + + if (StringUtils.isNotBlank(accountUuid)) { + searchCriteria.setParameters("accountUuid", "%" + accountUuid + "%"); + } + + if (StringUtils.isNotBlank(commonName)) { + setGuiThemeIdsFilteredByType(idList, ApiConstants.COMMON_NAME, commonName); + } + + if (StringUtils.isNotBlank(domainUuid)) { + setGuiThemeIdsFilteredByType(idList, ApiConstants.DOMAIN, domainUuid); + } + + searchCriteria.setParametersIfNotNull("idIn", idList.toArray()); + + return searchCriteria; + } + + /** + * Sets the `id IN ( )` clause of the query. If the informed value of common name or domain ID does not retrieve any GUI theme ID; then, an invalid ID (-1) is passed to the + * list, as not a single entity has this ID. This is necessary as to set the parameter even if it did not find any GUI theme ID; otherwise, the query would not filter the + * common name or domain ID passed. + */ + public void setGuiThemeIdsFilteredByType(List idList, String type, String value) { + List guiThemeIdsFilteredByType = new ArrayList<>(); + + switch (type) { + case ApiConstants.COMMON_NAME: + guiThemeIdsFilteredByType = guiThemeDetailsDao.listGuiThemeIdsByCommonName(value); + break; + case ApiConstants.DOMAIN: + guiThemeIdsFilteredByType = guiThemeDetailsDao.listGuiThemeIdsByDomainUuids(value); + break; + } + + if (CollectionUtils.isNotEmpty(guiThemeIdsFilteredByType)) { + idList.addAll(guiThemeIdsFilteredByType); + return; + } + logger.trace(String.format("No GUI theme with the specified [%s] with UUID [%s] was found, adding an invalid ID for filtering.", type, value)); + idList.add(INVALID_ID); + } + + private SearchBuilder createGuiThemeJoinSearchBuilder(boolean listAll, Boolean showPublic) { + SearchBuilder guiThemeJoinSearchBuilder = createSearchBuilder(); + guiThemeJoinSearchBuilder.and("idIn", guiThemeJoinSearchBuilder.entity().getId(), SearchCriteria.Op.IN); + guiThemeJoinSearchBuilder.and("name", guiThemeJoinSearchBuilder.entity().getName(), SearchCriteria.Op.EQ); + guiThemeJoinSearchBuilder.and("accountUuid", guiThemeJoinSearchBuilder.entity().getAccounts(), SearchCriteria.Op.LIKE); + + if (!listAll && showPublic != null) { + guiThemeJoinSearchBuilder.and("isPublic", guiThemeJoinSearchBuilder.entity().getIsPublic(), SearchCriteria.Op.EQ); + } + + return guiThemeJoinSearchBuilder; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/lb/ApplicationLoadBalancerRuleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/lb/ApplicationLoadBalancerRuleVO.java index d8ee8631b0b..4fec96067a3 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 org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; 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 class ApplicationLoadBalancerRuleVO extends FirewallRuleVO implements App 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 00000000000..9e337887011 --- /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 00000000000..b520ecd5cd1 --- /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 00000000000..c60a3ec3868 --- /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 00000000000..cc726ba3d35 --- /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 00000000000..8ca4c2d86da --- /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 00000000000..0f95f7c3cd5 --- /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 00000000000..377bc45ebfe --- /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 00000000000..a974cf5e276 --- /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 00000000000..8d8ec8c998a --- /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 00000000000..a5e5f47684a --- /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 00000000000..c3f860009db --- /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 00000000000..95e53448907 --- /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 1c8d1cf48ff..e8b6322baee 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 @@ public class NetworkPermissionDaoImpl extends GenericDaoBase 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 1865b9a6783..4ce7033156f 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.Id; 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 class GlobalLoadBalancerRuleVO implements GlobalLoadBalancerRule { 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 df888312a92..df0ede6821a 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 @@ -25,10 +25,14 @@ import javax.persistence.GenerationType; 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") @@ -57,6 +61,12 @@ public class ReservationVO implements ResourceReservation { @Column(name = "amount") long amount; + @Column(name = "mgmt_server_id") + Long managementServerId; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + protected ReservationVO() { } @@ -69,6 +79,7 @@ public class ReservationVO implements ResourceReservation { this.resourceType = resourceType; this.tag = tag; this.amount = delta; + this.managementServerId = ManagementServerNode.getManagementServerId(); } public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, Long delta) { @@ -114,4 +125,16 @@ public class ReservationVO implements ResourceReservation { 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 0433dc8c57d..d6d494f61f9 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 @@ -23,13 +23,17 @@ 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, String tag); long getDomainReservation(Long domain, Resource.ResourceType resourceType, String tag); void setResourceId(Resource.ResourceType type, Long resourceId); - List getResourceIds(long accountId, Resource.ResourceType type); 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 8d6e0b6eee0..3b17f4e4294 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,8 +18,8 @@ // package org.apache.cloudstack.reservation.dao; +import java.util.Date; import java.util.List; -import java.util.stream.Collectors; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.reservation.ReservationVO; @@ -42,6 +42,8 @@ public class ReservationDaoImpl extends GenericDaoBase impl 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; @@ -50,6 +52,7 @@ public class ReservationDaoImpl extends GenericDaoBase impl private final SearchBuilder listDomainAndTypeAndNoTagSearch; private final SearchBuilder listResourceByAccountAndTypeAndNoTagSearch; private final SearchBuilder listIdsSearch; + private final SearchBuilder listMsIdSearch; public ReservationDaoImpl() { @@ -71,12 +74,14 @@ public class ReservationDaoImpl extends GenericDaoBase impl 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(); @@ -94,18 +99,24 @@ public class ReservationDaoImpl extends GenericDaoBase impl 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, String tag) { long total = 0; - SearchCriteria sc = tag == null ? - listAccountAndTypeAndNoTagSearch.create() : listAccountAndTypeSearch.create(); - sc.setParameters(ACCOUNT_ID, accountId); - sc.setParameters(RESOURCE_TYPE, resourceType); - if (tag != null) { + 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); for (ReservationVO reservation : reservations) { total += reservation.getReservedAmount(); @@ -116,13 +127,15 @@ public class ReservationDaoImpl extends GenericDaoBase impl @Override public long getDomainReservation(Long domainId, Resource.ResourceType resourceType, String tag) { long total = 0; - SearchCriteria sc = tag == null ? - listDomainAndTypeAndNoTagSearch.create() : listDomainAndTypeSearch.create(); - sc.setParameters(DOMAIN_ID, domainId); - sc.setParameters(RESOURCE_TYPE, resourceType); - if (tag != null) { + 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); for (ReservationVO reservation : reservations) { total += reservation.getReservedAmount(); @@ -149,23 +162,17 @@ public class ReservationDaoImpl extends GenericDaoBase impl } } - @Override - public List getResourceIds(long accountId, Resource.ResourceType type) { - SearchCriteria sc = listResourceByAccountAndTypeSearch.create(); - sc.setParameters(ACCOUNT_ID, accountId); - sc.setParameters(RESOURCE_TYPE, type); - return listBy(sc).stream().map(ReservationVO::getResourceId).collect(Collectors.toList()); - } - @Override public List getReservationsForAccount(long accountId, Resource.ResourceType type, String tag) { - SearchCriteria sc = tag == null ? - listResourceByAccountAndTypeAndNoTagSearch.create() : listResourceByAccountAndTypeSearch.create(); - sc.setParameters(ACCOUNT_ID, accountId); - sc.setParameters(RESOURCE_TYPE, type); - if (tag != null) { + 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); } @@ -177,4 +184,28 @@ public class ReservationDaoImpl extends GenericDaoBase impl 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/FirewallRuleDetailVO.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/FirewallRuleDetailVO.java index 636d889fafe..1149d0b13e7 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 class FirewallRuleDetailVO implements ResourceDetail { 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 5a173191be1..7c113a10af4 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java @@ -21,6 +21,7 @@ import java.util.Map; import org.apache.cloudstack.api.ResourceDetail; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; public interface ResourceDetailsDao extends GenericDao { @@ -53,7 +54,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 +77,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 +85,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); - public void addDetail(long resourceId, String key, String value, boolean display); + void saveDetails(List details); - public List findResourceIdsByNameAndValueIn(String name, Object[] values); + void addDetail(long resourceId, String key, String value, boolean display); + + List findResourceIdsByNameAndValueIn(String name, Object[] values); + + long batchExpungeForResources(List ids, Long batchSize); + + 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 37ebfebf5dd..58b60531e5a 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 @@ package org.apache.cloudstack.resourcedetail; 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() { @@ -74,8 +87,7 @@ public abstract class ResourceDetailsDaoBase extends G sc.setParameters("value", value); } - List results = search(sc, null); - return results; + return search(sc, null); } public Map listDetailsKeyPairs(long resourceId) { @@ -83,13 +95,27 @@ public abstract class ResourceDetailsDaoBase extends G 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 +128,24 @@ public abstract class ResourceDetailsDaoBase extends G return details; } + @Override + public Pair, Map> listDetailsKeyPairsWithVisibility(long resourceId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("resourceId", resourceId); + List results = search(sc, null); + Map> partitioned = results.stream() + .collect(Collectors.partitioningBy( + R::isDisplay, + Collectors.toMap(R::getName, R::getValue) + )); + return new Pair<>(partitioned.get(true), partitioned.get(false)); + } + public List listDetails(long resourceId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceId", resourceId); - List results = search(sc, null); - return results; + return search(sc, null); } public void removeDetails(long resourceId) { @@ -169,7 +207,7 @@ public abstract class ResourceDetailsDaoBase extends G 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 +219,7 @@ public abstract class ResourceDetailsDaoBase extends G sc.setParameters("resourceId", resourceId); sc.setParameters("display", forDisplay); - List results = search(sc, null); - return results; + return search(sc, null); } @Override @@ -201,4 +238,26 @@ public abstract class ResourceDetailsDaoBase extends G 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 1b430e806e2..d0cfcc3d439 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,8 @@ 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 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 b0da0c5e747..f647d0c8365 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 class HeuristicVO implements Heuristic { @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 7aab5bbf7b3..eda4bcfdaa1 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java @@ -44,7 +44,7 @@ public interface ImageStoreDao extends GenericDao { List listStoresByZoneId(long zoneId); - List listAllStoresInZone(Long zoneId, String provider, DataStoreRole role); + List listAllStoresInZoneExceptId(Long zoneId, String provider, DataStoreRole role, long storeId); List findByProtocol(String protocol); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDaoImpl.java index 84b88c215ca..4cb40b5eaf6 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDaoImpl.java @@ -41,7 +41,7 @@ public class ImageStoreDaoImpl extends GenericDaoBase implem private SearchBuilder nameSearch; private SearchBuilder providerSearch; private SearchBuilder regionSearch; - private SearchBuilder storeSearch; + private SearchBuilder storesExceptIdSearch; private SearchBuilder protocolSearch; private SearchBuilder zoneProtocolSearch; @@ -88,11 +88,12 @@ public class ImageStoreDaoImpl extends GenericDaoBase implem regionSearch.and("role", regionSearch.entity().getRole(), SearchCriteria.Op.EQ); regionSearch.done(); - storeSearch = createSearchBuilder(); - storeSearch.and("providerName", storeSearch.entity().getProviderName(), SearchCriteria.Op.EQ); - storeSearch.and("role", storeSearch.entity().getRole(), SearchCriteria.Op.EQ); - storeSearch.and("dataCenterId", storeSearch.entity().getDcId(), SearchCriteria.Op.EQ); - storeSearch.done(); + storesExceptIdSearch = createSearchBuilder(); + storesExceptIdSearch.and("providerName", storesExceptIdSearch.entity().getProviderName(), SearchCriteria.Op.EQ); + storesExceptIdSearch.and("role", storesExceptIdSearch.entity().getRole(), SearchCriteria.Op.EQ); + storesExceptIdSearch.and("dataCenterId", storesExceptIdSearch.entity().getDcId(), SearchCriteria.Op.EQ); + storesExceptIdSearch.and("id", storesExceptIdSearch.entity().getId(), SearchCriteria.Op.NEQ); + storesExceptIdSearch.done(); return true; } @@ -113,11 +114,12 @@ public class ImageStoreDaoImpl extends GenericDaoBase implem } @Override - public List listAllStoresInZone(Long zoneId, String provider, DataStoreRole role) { - SearchCriteria sc = storeSearch.create(); + public List listAllStoresInZoneExceptId(Long zoneId, String provider, DataStoreRole role, long id) { + SearchCriteria sc = storesExceptIdSearch.create(); sc.setParameters("providerName", provider); sc.setParameters("role", role); sc.setParameters("dataCenterId", zoneId); + sc.setParameters("id", id); return listBy(sc); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java index 8e5ce770f45..ec40dc0dd68 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.HashMap; 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; 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,7 +73,7 @@ public class ImageStoreDetailsDaoImpl extends ResourceDetailsDaoBase details = listBy(sc); - Map detailsMap = new HashMap(); + Map detailsMap = new HashMap<>(); for (ImageStoreDetailVO detail : details) { String name = detail.getName(); String value = detail.getValue(); @@ -105,10 +110,16 @@ public class ImageStoreDetailsDaoImpl extends ResourceDetailsDaoBase 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 class ImageStoreDetailsDaoImpl extends ResourceDetailsDaoBase 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 3ca9259c099..c13f5aac6d6 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.ImageStore; 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 class ImageStoreVO implements ImageStore { 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 94f6b5ec372..695742823eb 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java @@ -39,4 +39,6 @@ public interface ObjectStoreDao extends GenericDao { ObjectStoreResponse setObjectStoreResponse(ObjectStoreResponse storeData, ObjectStoreVO store); Integer countAllObjectStores(); + + Boolean updateAllocatedSize(ObjectStoreVO objectStoreVO, long delta); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java index 51abde013b6..891ac0996ac 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java @@ -21,6 +21,10 @@ package org.apache.cloudstack.storage.datastore.db; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.db.TransactionStatus; + import org.apache.cloudstack.api.response.ObjectStoreResponse; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.springframework.stereotype.Component; @@ -142,6 +146,19 @@ public class ObjectStoreDaoImpl extends GenericDaoBase impl ObjectStoreResponse osResponse = new ObjectStoreResponse(); osResponse.setId(store.getUuid()); osResponse.setName(store.getName()); + if (store.getTotalSize() != null && store.getTotalSize() != 0L) { + osResponse.setStorageTotal(store.getTotalSize()); + } + if (store.getUsedSize() == null) { + osResponse.setStorageUsed(0L); + } else { + osResponse.setStorageUsed(store.getUsedSize()); + } + if (store.getAllocatedSize() == null) { + osResponse.setStorageAllocated(0L); + } else { + osResponse.setStorageAllocated(store.getAllocatedSize()); + } osResponse.setProviderName(store.getProviderName()); String url = store.getUrl(); osResponse.setUrl(url); @@ -159,4 +176,19 @@ public class ObjectStoreDaoImpl extends GenericDaoBase impl SearchCriteria sc = createSearchCriteria(); return getCount(sc); } + + @Override + public Boolean updateAllocatedSize(ObjectStoreVO objectStoreVO, long delta) { + return Transaction.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(final TransactionStatus status) { + if (objectStoreVO.getAllocatedSize() != null) { + objectStoreVO.setAllocatedSize(objectStoreVO.getAllocatedSize() + delta); + } else { + objectStoreVO.setAllocatedSize(delta); + } + return update(objectStoreVO.getId(), objectStoreVO); + } + }); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java index 885cbfd98ab..23b650acc79 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.storage.datastore.db; 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 class ObjectStoreVO implements ObjectStore { this.totalSize = totalSize; } - public Long getUsedBytes() { - return usedBytes; + public Long getUsedSize() { + return usedSize; } - public void setUsedBytes(Long usedBytes) { - this.usedBytes = usedBytes; + public void setUsedSize(Long usedSize) { + this.usedSize = usedSize; } public void setDetails(Map details) { this.details = details; } + + public Long getAllocatedSize() { + return allocatedSize; + } + + public void setAllocatedSize(Long allocatedSize) { + this.allocatedSize = allocatedSize; + } + + @Override + public String toString() { + return String.format("ObjectStore %s", + 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 d42e863cbed..7600cdb9b81 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java @@ -58,7 +58,9 @@ public interface PrimaryDataStoreDao extends GenericDao { */ void updateCapacityIops(long id, long capacityIops); - StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule); + StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, List storageAccessGroups); + + StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, boolean displayDetails, List storageAccessGroups); /** * Find pool by name. @@ -82,7 +84,9 @@ public interface PrimaryDataStoreDao extends GenericDao { */ List findPoolsByDetails(long dcId, long podId, Long clusterId, Map details, ScopeType scope); - List findPoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, long ruleExecuteTimeout); + List findPoolsByTags(long dcId, long podId, Long clusterId, ScopeType scope, String[] tags, boolean validateTagRule, long ruleExecuteTimeout); + + List findPoolsByAccessGroupsForHostConnection(Long dcId, Long podId, Long clusterId, ScopeType scope, String[] storageAccessGroups); List findDisabledPoolsByScope(long dcId, Long podId, Long clusterId, ScopeType scope); @@ -103,6 +107,8 @@ public interface PrimaryDataStoreDao extends GenericDao { void updateDetails(long poolId, Map details); + void removeDetails(long poolId); + Map getDetails(long poolId); List searchForStoragePoolTags(long poolId); @@ -123,10 +129,18 @@ public interface PrimaryDataStoreDao extends GenericDao { List findZoneWideStoragePoolsByTags(long dcId, String[] tags, boolean validateTagRule); + List findZoneWideStoragePoolsByAccessGroupsForHostConnection(long dcId, String[] storageAccessGroups); + + List findZoneWideStoragePoolsByAccessGroupsAndHypervisorTypeForHostConnection(long dcId, String[] storageAccessGroups, HypervisorType type); + List findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType); List findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType, String keyword); + 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); @@ -135,17 +149,23 @@ public interface PrimaryDataStoreDao extends GenericDao { 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, String address, ScopeType scopeType, StoragePoolStatus status, - String keyword, Filter searchFilter); + String path, Long podId, Long clusterId, Long hostId, String address, ScopeType scopeType, StoragePoolStatus status, + String keyword, String storageAccessGroup, Filter searchFilter); List listByIds(List ids); + + List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType); } 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 e4dd66a86b8..71d5c93f027 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -28,8 +28,8 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.utils.Pair; -import com.cloud.utils.db.Filter; +import com.cloud.storage.StoragePoolAndAccessGroupMapVO; +import com.cloud.storage.dao.StoragePoolAndAccessGroupMapDao; import org.apache.commons.collections.CollectionUtils; import com.cloud.host.Status; @@ -41,7 +41,9 @@ import com.cloud.storage.StoragePoolStatus; 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; @@ -70,15 +72,25 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase private StoragePoolHostDao _hostDao; @Inject private StoragePoolTagsDao _tagsDao; + @Inject + StoragePoolAndAccessGroupMapDao _storagePoolAccessGroupMapDao; protected final String DetailsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_details ON storage_pool.id = storage_pool_details.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; protected final String DetailsSqlSuffix = ") GROUP BY storage_pool_details.pool_id HAVING COUNT(storage_pool_details.name) >= ?"; + protected final String DetailsForHostConnectionSqlSuffix = ") GROUP BY storage_pool_details.pool_id"; private final String ZoneWideTagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' AND storage_pool_tags.is_tag_a_rule = 0 and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; private final String ZoneWideTagsSqlSuffix = ") GROUP BY storage_pool_tags.pool_id HAVING COUNT(storage_pool_tags.tag) >= ?"; + private final String ZoneWideStorageAccessGroupsForHostConnectionSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; + private final String ZoneWideStorageAccessGroupsForHostConnectionSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; + private final String ZoneWideStorageAccessGroupsWithHypervisorTypeSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.hypervisor = ? and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; + private final String ZoneWideStorageAccessGroupsWithHypervisorTypeSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; // Storage tags are now separate from storage_pool_details, leaving only details on that table protected final String TagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' AND storage_pool_tags.is_tag_a_rule = 0 and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; protected final String TagsSqlSuffix = ") GROUP BY storage_pool_tags.pool_id HAVING COUNT(storage_pool_tags.tag) >= ?"; + protected final String SAGsForHostConnectionSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; + + protected final String SAGsForHostConnectionSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; private static final String GET_STORAGE_POOLS_OF_VOLUMES_WITHOUT_OR_NOT_HAVING_TAGS = "SELECT s.* " + "FROM volumes vol " + @@ -295,21 +307,29 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase return findOneIncludingRemovedBy(sc); } + @Override + public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, List storageAccessGroups) { + return persist(pool, details, tags, isTagARule, true, storageAccessGroups); + } + @Override @DB - public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule) { + public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, boolean displayDetails, List storageAccessGroups) { TransactionLegacy txn = TransactionLegacy.currentTxn(); txn.start(); pool = super.persist(pool); if (details != null) { for (Map.Entry detail : details.entrySet()) { - StoragePoolDetailVO vo = new StoragePoolDetailVO(pool.getId(), detail.getKey(), detail.getValue(), true); + StoragePoolDetailVO vo = new StoragePoolDetailVO(pool.getId(), detail.getKey(), detail.getValue(), displayDetails); _detailsDao.persist(vo); } } if (CollectionUtils.isNotEmpty(tags)) { _tagsDao.persist(pool.getId(), tags, isTagARule); } + if (CollectionUtils.isNotEmpty(storageAccessGroups)) { + _storagePoolAccessGroupMapDao.persist(pool.getId(), storageAccessGroups); + } txn.commit(); return pool; } @@ -333,6 +353,13 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase return searchStoragePoolsPreparedStatement(sql, dcId, podId, clusterId, scope, valuesLength); } + protected List findPoolsByDetailsOrTagsForHostConnectionInternal(long dcId, long podId, Long clusterId, ScopeType scope, String sqlValues, ValueType valuesType) { + String sqlPrefix = valuesType.equals(ValueType.DETAILS) ? DetailsSqlPrefix : SAGsForHostConnectionSqlPrefix; + String sqlSuffix = valuesType.equals(ValueType.DETAILS) ? DetailsForHostConnectionSqlSuffix : SAGsForHostConnectionSqlSuffix; + String sql = getSqlPreparedStatement(sqlPrefix, sqlSuffix, sqlValues, clusterId); + return searchStoragePoolsPreparedStatement(sql, dcId, podId, clusterId, scope, null); + } + /** * Search storage pools in a transaction * @param sql prepared statement sql @@ -344,7 +371,50 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase * @return storage pools matching criteria */ @DB - protected List searchStoragePoolsPreparedStatement(String sql, long dcId, Long podId, Long clusterId, ScopeType scope, int valuesLength) { + protected List searchStoragePoolsWithHypervisorTypesPreparedStatement(String sql, HypervisorType type, long dcId, Long podId, Long clusterId, ScopeType scope, Integer valuesLength) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + List pools = new ArrayList(); + try (PreparedStatement pstmt = txn.prepareStatement(sql);) { + if (pstmt != null) { + int i = 1; + pstmt.setString(i++, type.toString()); + pstmt.setLong(i++, dcId); + if (podId != null) { + pstmt.setLong(i++, podId); + } + pstmt.setString(i++, scope.toString()); + if (clusterId != null) { + pstmt.setLong(i++, clusterId); + } + if (valuesLength != null) { + pstmt.setInt(i++, valuesLength); + } + try (ResultSet rs = pstmt.executeQuery();) { + while (rs.next()) { + pools.add(toEntityBean(rs, false)); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to execute :" + e.getMessage(), e); + } + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to execute :" + e.getMessage(), e); + } + return pools; + } + + /** + * Search storage pools in a transaction + * @param sql prepared statement sql + * @param dcId data center id + * @param podId pod id + * @param clusterId cluster id + * @param scope scope + * @param valuesLength values length + * @return storage pools matching criteria + */ + @DB + protected List searchStoragePoolsPreparedStatement(String sql, long dcId, Long podId, Long clusterId, ScopeType scope, Integer valuesLength) { TransactionLegacy txn = TransactionLegacy.currentTxn(); List pools = new ArrayList(); try (PreparedStatement pstmt = txn.prepareStatement(sql);) { @@ -358,7 +428,9 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase if (clusterId != null) { pstmt.setLong(i++, clusterId); } - pstmt.setInt(i++, valuesLength); + if (valuesLength != null) { + pstmt.setInt(i++, valuesLength); + } try (ResultSet rs = pstmt.executeQuery();) { while (rs.next()) { pools.add(toEntityBean(rs, false)); @@ -415,6 +487,22 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase return sqlValues.toString(); } + /** + * Return SQL string from storage pool access group map, to be placed between SQL Prefix and SQL Suffix when creating storage tags PreparedStatement. + * @param storageAccessGroups storage tags array + * @return SQL string containing storage tag values to be placed between Prefix and Suffix when creating PreparedStatement. + * @throws NullPointerException if tags is null + * @throws IndexOutOfBoundsException if tags is not null, but empty + */ + protected String getSqlValuesFromStorageAccessGroups(String[] storageAccessGroups) throws NullPointerException, IndexOutOfBoundsException { + StringBuilder sqlValues = new StringBuilder(); + for (String tag : storageAccessGroups) { + sqlValues.append("(storage_pool_and_access_group_map.storage_access_group='").append(tag).append("') OR "); + } + sqlValues.delete(sqlValues.length() - 4, sqlValues.length()); + return sqlValues.toString(); + } + @DB @Override public List findPoolsByDetails(long dcId, long podId, Long clusterId, Map details, ScopeType scope) { @@ -423,10 +511,10 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase } @Override - public List findPoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, long ruleExecuteTimeout) { + public List findPoolsByTags(long dcId, long podId, Long clusterId, ScopeType scope, String[] tags, boolean validateTagRule, long ruleExecuteTimeout) { List storagePools = null; if (tags == null || tags.length == 0) { - storagePools = listBy(dcId, podId, clusterId, ScopeType.CLUSTER); + storagePools = listBy(dcId, podId, clusterId, scope); if (validateTagRule) { storagePools = getPoolsWithoutTagRule(storagePools); @@ -434,7 +522,20 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase } else { String sqlValues = getSqlValuesFromStorageTags(tags); - storagePools = findPoolsByDetailsOrTagsInternal(dcId, podId, clusterId, ScopeType.CLUSTER, sqlValues, ValueType.TAGS, tags.length); + storagePools = findPoolsByDetailsOrTagsInternal(dcId, podId, clusterId, scope, sqlValues, ValueType.TAGS, tags.length); + } + + return storagePools; + } + + @Override + public List findPoolsByAccessGroupsForHostConnection(Long dcId, Long podId, Long clusterId, ScopeType scope, String[] storageAccessGroups) { + List storagePools = null; + if (storageAccessGroups == null || storageAccessGroups.length == 0) { + storagePools = listBy(dcId, podId, clusterId, scope); + } else { + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + storagePools = findPoolsByDetailsOrTagsForHostConnectionInternal(dcId, podId, clusterId, scope, sqlValues, ValueType.TAGS); } return storagePools; @@ -551,6 +652,77 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase return storagePoolsToReturn; } + @Override + public List findZoneWideStoragePoolsByAccessGroupsForHostConnection(long dcId, String[] storageAccessGroups) { + if (storageAccessGroups == null || storageAccessGroups.length == 0) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId); + sc.and(sc.entity().getStatus(), Op.EQ, Status.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE); + return sc.list(); + } else { + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + String sql = getSqlPreparedStatement(ZoneWideStorageAccessGroupsForHostConnectionSqlPrefix, ZoneWideStorageAccessGroupsForHostConnectionSqlSuffix, sqlValues, null); + return searchStoragePoolsPreparedStatement(sql, dcId, null, null, ScopeType.ZONE, null); + } + } + + @Override + public List findZoneWideStoragePoolsByAccessGroupsAndHypervisorTypeForHostConnection(long dcId, String[] storageAccessGroups, HypervisorType type) { + if (storageAccessGroups == null || storageAccessGroups.length == 0) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId); + sc.and(sc.entity().getStatus(), Op.EQ, Status.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE); + sc.and(sc.entity().getHypervisor(), Op.EQ, type); + return sc.list(); + } else { + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + String sql = getSqlPreparedStatement(ZoneWideStorageAccessGroupsWithHypervisorTypeSqlPrefix, ZoneWideStorageAccessGroupsWithHypervisorTypeSqlSuffix, sqlValues, null); + return searchStoragePoolsWithHypervisorTypesPreparedStatement(sql, type, dcId, null, null, ScopeType.ZONE, null); + } + } + + @Override + public List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType) { + SearchBuilder poolSearch = createSearchBuilder(); + SearchBuilder storageAccessGroupsPoolSearch = _storagePoolAccessGroupMapDao.createSearchBuilder(); + // Set criteria for pools + poolSearch.and("scope", poolSearch.entity().getScope(), Op.EQ); + poolSearch.and("removed", poolSearch.entity().getRemoved(), Op.NULL); + poolSearch.and("status", poolSearch.entity().getStatus(), Op.EQ); + poolSearch.and("datacenterid", poolSearch.entity().getDataCenterId(), Op.EQ); + poolSearch.and("podid", poolSearch.entity().getPodId(), Op.EQ); + poolSearch.and("clusterid", poolSearch.entity().getClusterId(), Op.EQ); + poolSearch.and("hypervisortype", poolSearch.entity().getHypervisor(), Op.EQ); + + // Set StoragePoolAccessGroupMapVO.pool_id IS NULL. This ensures only pools without tags are returned + storageAccessGroupsPoolSearch.and("poolid", storageAccessGroupsPoolSearch.entity().getPoolId(), Op.NULL); + poolSearch.join("tagJoin", storageAccessGroupsPoolSearch, poolSearch.entity().getId(), storageAccessGroupsPoolSearch.entity().getPoolId(), JoinBuilder.JoinType.LEFT); + + SearchCriteria sc = poolSearch.create(); + sc.setParameters("scope", scope.toString()); + sc.setParameters("status", Status.Up.toString()); + + if (dcId != null) { + sc.setParameters("datacenterid", dcId); + } + + if (podId != null) { + sc.setParameters("podid", podId); + } + + if (clusterId != null) { + sc.setParameters("clusterid", clusterId); + } + + if (hypervisorType != null) { + sc.setParameters("hypervisortype", hypervisorType); + } + + return listBy(sc); + } + @Override public List searchForStoragePoolTags(long poolId) { return _tagsDao.getStoragePoolTags(poolId); @@ -570,6 +742,11 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase } } + @Override + public void removeDetails(long poolId) { + _detailsDao.removeDetails(poolId); + } + @Override public Map getDetails(long poolId) { return _detailsDao.listDetailsKeyPairs(poolId); @@ -622,11 +799,38 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase 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(); + } + @Override 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); @@ -660,6 +864,16 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase 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(); @@ -683,9 +897,10 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase @Override public Pair, Integer> searchForIdsAndCount(Long storagePoolId, String storagePoolName, Long zoneId, - String path, Long podId, Long clusterId, String address, ScopeType scopeType, StoragePoolStatus status, - String keyword, Filter searchFilter) { - SearchCriteria sc = createStoragePoolSearchCriteria(storagePoolId, storagePoolName, zoneId, path, podId, clusterId, address, scopeType, status, keyword); + String path, Long podId, Long clusterId, Long hostId, String address, ScopeType scopeType, StoragePoolStatus status, + String keyword, String storageAccessGroup, Filter searchFilter) { + SearchCriteria sc = createStoragePoolSearchCriteria(storagePoolId, storagePoolName, zoneId, path, podId, clusterId, + hostId, address, scopeType, status, keyword, storageAccessGroup); Pair, Integer> uniquePair = searchAndCount(sc, searchFilter); List idList = uniquePair.first().stream().map(StoragePoolVO::getId).collect(Collectors.toList()); return new Pair<>(idList, uniquePair.second()); @@ -702,8 +917,8 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase } private SearchCriteria createStoragePoolSearchCriteria(Long storagePoolId, String storagePoolName, - Long zoneId, String path, Long podId, Long clusterId, String address, ScopeType scopeType, - StoragePoolStatus status, String keyword) { + Long zoneId, String path, Long podId, Long clusterId, Long hostId, String address, ScopeType scopeType, + StoragePoolStatus status, String keyword, String storageAccessGroup) { SearchBuilder sb = createSearchBuilder(); sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getId()); // select distinct // ids @@ -718,12 +933,24 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase sb.and("status", sb.entity().getStatus(), SearchCriteria.Op.EQ); sb.and("parent", sb.entity().getParent(), SearchCriteria.Op.EQ); + if (hostId != null) { + SearchBuilder hostJoin = _hostDao.createSearchBuilder(); + hostJoin.and("hostId", hostJoin.entity().getHostId(), SearchCriteria.Op.EQ); + sb.join("poolHostJoin", hostJoin, sb.entity().getId(), hostJoin.entity().getPoolId(), JoinBuilder.JoinType.INNER); + } + + if (storageAccessGroup != null) { + SearchBuilder storageAccessGroupJoin = _storagePoolAccessGroupMapDao.createSearchBuilder(); + storageAccessGroupJoin.and("storageAccessGroup", storageAccessGroupJoin.entity().getStorageAccessGroup(), SearchCriteria.Op.EQ); + sb.join("poolStorageAccessGroupJoin", storageAccessGroupJoin, sb.entity().getId(), storageAccessGroupJoin.entity().getPoolId(), JoinBuilder.JoinType.INNER); + } + SearchCriteria sc = sb.create(); if (keyword != null) { SearchCriteria ssc = createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("poolType", SearchCriteria.Op.LIKE, new Storage.StoragePoolType("%" + keyword + "%")); + ssc.addOr("poolType", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("name", SearchCriteria.Op.SC, ssc); } @@ -766,6 +993,15 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase sc.setParameters("status", status.toString()); } sc.setParameters("parent", 0); + + if (hostId != null) { + sc.setJoinParameters("poolHostJoin", "hostId", hostId); + } + + if (storageAccessGroup != null) { + sc.setJoinParameters("poolStorageAccessGroupJoin", "storageAccessGroup", storageAccessGroup); + } + return sc; } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java index 344ff8b2a69..db4c64bd0ab 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.storage.datastore.db; import java.util.Date; import java.util.List; +import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; @@ -46,12 +47,28 @@ StateDao listBySnapshot(long snapshotId, DataStoreRole role); + SnapshotDataStoreVO findParent(DataStoreRole role, Long storeId, Long zoneId, Long volumeId, boolean kvmIncrementalSnapshot, Hypervisor.HypervisorType hypervisorType); + + SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapshotId, DataStoreRole role, ObjectInDataStoreStateMachine.State state); + + List listReadyByVolumeIdAndCheckpointPathNotNull(long volumeId); + + SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId); + + List listBySnapshotId(long snapshotId); + + List listBySnapshotAndDataStoreRole(long snapshotId, DataStoreRole role); + + List listExtractedSnapshotsBeforeDate(Date beforeDate); List listReadyBySnapshot(long snapshotId, DataStoreRole role); SnapshotDataStoreVO findBySourceSnapshot(long snapshotId, DataStoreRole role); + List findBySnapshotIdAndNotInDestroyedHiddenState(long snapshotId); + + SnapshotDataStoreVO findBySnapshotIdInAnyState(long snapshotId, DataStoreRole role); + List listDestroyed(long storeId); List findBySnapshotId(long snapshotId); @@ -106,4 +123,6 @@ StateDao snapshotIds, Long batchSize); } 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 c095f4222e7..b5faa6caedf 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 @@ -58,26 +58,42 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq; protected SearchBuilder searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq; private SearchBuilder stateSearch; - private SearchBuilder idStateNeqSearch; + private SearchBuilder idStateNinSearch; protected SearchBuilder snapshotVOSearch; private SearchBuilder snapshotCreatedSearch; private SearchBuilder dataStoreAndInstallPathSearch; private SearchBuilder storeAndSnapshotIdsSearch; private SearchBuilder storeSnapshotDownloadStatusSearch; + private SearchBuilder searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull; + private SearchBuilder searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore; + private SearchBuilder searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq; + protected static final List HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING = List.of(Hypervisor.HypervisorType.XenServer); @Inject protected SnapshotDao snapshotDao; + @Inject + protected ImageStoreDao imageStoreDao; + private static final String FIND_OLDEST_OR_LATEST_SNAPSHOT = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where " + " store_role = ? and volume_id = ? and state = 'Ready'" + " order by created %s " + " limit 1"; + private static final String FIND_SNAPSHOT_IN_ZONE = "SELECT ssr.* FROM " + + "snapshot_store_ref ssr, snapshots s " + + "WHERE ssr.snapshot_id=? AND ssr.snapshot_id = s.id AND s.data_center_id=?;"; + @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); @@ -119,10 +135,10 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + SearchCriteria sc; + if (kvmIncrementalSnapshot && Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.create(); + } else { + sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.create(); + } + sc.setParameters(VOLUME_ID, volumeId); - sc.setParameters(STORE_ROLE, role.toString()); + if (role != null) { + sc.setParameters(STORE_ROLE, role.toString()); + } sc.setParameters(STATE, ObjectInDataStoreStateMachine.State.Ready.name()); - sc.setParameters(STORE_ID, storeId); + if (storeId != null) { + sc.setParameters(STORE_ID, new Long[]{storeId}); + } else if (zoneId != null) { + List imageStores = imageStoreDao.listStoresByZoneId(zoneId); + Object[] imageStoreIds = imageStores.stream().map(ImageStoreVO::getId).toArray(); + sc.setParameters(STORE_ID, imageStoreIds); + } List snapshotList = listBy(sc, new Filter(SnapshotDataStoreVO.class, CREATED, false, null, null)); - if (CollectionUtils.isNotEmpty(snapshotList)) { - return snapshotList.get(0); + if (CollectionUtils.isEmpty(snapshotList)) { + return null; + } + + SnapshotDataStoreVO parent = snapshotList.get(0); + + if (kvmIncrementalSnapshot && parent.getKvmCheckpointPath() == null && Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + return null; + } + + return parent; + } + + @Override + public SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapshotId, DataStoreRole role, State state) { + SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); + sc.setParameters(STATE, state); + return findOneBy(sc); + } + + @Override + public SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId) { + try (TransactionLegacy transactionLegacy = TransactionLegacy.currentTxn()) { + try (PreparedStatement preparedStatement = transactionLegacy.prepareStatement(FIND_SNAPSHOT_IN_ZONE)) { + preparedStatement.setLong(1, snapshotId); + preparedStatement.setLong(2, zoneId); + + try (ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + return toEntityBean(resultSet, false); + } + } + } + } catch (SQLException e) { + logger.warn(String.format("Failed to find %s snapshot in zone %s due to [%s].", snapshotId, zoneId, e.getMessage()), e); } return null; } @Override - public List listBySnapshot(long snapshotId, DataStoreRole role) { + public List listBySnapshotId(long snapshotId) { + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + return listBy(sc); + } + + @Override + public List listBySnapshotAndDataStoreRole(long snapshotId, DataStoreRole role) { SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); return listBy(sc); } @@ -321,6 +417,12 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); + return findOneBy(sc); + } + @Override public List listAllByVolumeAndDataStore(long volumeId, DataStoreRole role) { SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); @@ -340,9 +442,17 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase findBySnapshotId(long snapshotId) { - SearchCriteria sc = idStateNeqSearch.create(); + SearchCriteria sc = idStateNinSearch.create(); sc.setParameters(SNAPSHOT_ID, snapshotId); - sc.setParameters(STATE, State.Destroyed); + sc.setParameters(STATE, State.Destroyed.name()); + return listBy(sc); + } + + @Override + public List findBySnapshotIdAndNotInDestroyedHiddenState(long snapshotId) { + SearchCriteria sc = idStateNinSearch.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STATE, State.Destroyed.name(), State.Hidden.name()); return listBy(sc); } @@ -485,13 +595,35 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = snapshotVOSearch.create(); sc.setParameters(VOLUME_ID, volumeId); SnapshotVO snapshot = snapshotDao.findOneBy(sc); - return snapshot != null && HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING.contains(snapshot.getHypervisorType()); + if (snapshot == null) { + return false; + } + + Hypervisor.HypervisorType hypervisorType = snapshot.getHypervisorType(); + return HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING.contains(hypervisorType) || (Hypervisor.HypervisorType.KVM.equals(hypervisorType) && kvmIncrementalSnapshot); + } + + @Override + public List listReadyByVolumeIdAndCheckpointPathNotNull(long volumeId) { + SearchCriteria sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.create(); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STATE, State.Ready); + return listBy(sc); + } + + @Override + public List listExtractedSnapshotsBeforeDate(Date beforeDate) { + SearchCriteria sc = searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.create(); + sc.setParameters(URL_CREATED_BEFORE, beforeDate); + sc.setParameters(STATE, State.Ready); + + return listBy(sc); } @Override @@ -559,4 +691,16 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase 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); + } } 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 a1dc05fce58..44eb7e6c02c 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java @@ -29,6 +29,8 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -80,12 +82,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/sharedfs/SharedFSVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSVO.java new file mode 100644 index 00000000000..8870bf6d4d8 --- /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 00000000000..4735202a762 --- /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 00000000000..da622071671 --- /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 00000000000..8e56cce739d --- /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/vm/schedule/VMScheduleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java index 176f88c5f6b..e0065db1e77 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 class VMScheduleVO implements VMSchedule { 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 0c2dd94cce5..775e9cfe40c 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 class VMScheduledJobVO implements VMScheduledJob { 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/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 80a92426f17..d308a9e5aaf 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml @@ -62,11 +62,14 @@ - + + + + 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 5df32fb4731..7e9d6c3b54f 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -117,6 +117,7 @@ + @@ -137,6 +138,7 @@ + @@ -187,7 +189,6 @@ - @@ -197,12 +198,14 @@ + + @@ -268,6 +271,8 @@ + + @@ -288,4 +293,20 @@ + + + + + + + + + + + + + + + + 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 5c1253143f4..1a03e9b7998 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 00000000000..efe56bccf2d --- /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 00000000000..7872f60b2db --- /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 00000000000..754c02acb93 --- /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 00000000000..8083080088e --- /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 00000000000..22f490ad0fa --- /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 00000000000..5d4cbf6c770 --- /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 00000000000..d63c92e6b81 --- /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 00000000000..167b17412f1 --- /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_foreign_key.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_foreign_key.sql new file mode 100644 index 00000000000..0ba0a411ca9 --- /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 00000000000..1e1afd26597 --- /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_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 00000000000..7af96f56145 --- /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 00000000000..c53e0067061 --- /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/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 00000000000..e257b64bd8a --- /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 00000000000..a47b6a1fbf9 --- /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 00000000000..f824ebad98e --- /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 7013046ca43..000d0f077cc 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 f2b6b291f78..006a3f1cd7b 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 c8757833fab..8a3ca39d5e7 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 45202840565..a34b65dc8ab 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 eb473cfc7f6..8da29caae53 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 01bec020d63..f352f5ef44d 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 0cb187e2b9d..eae13acc78c 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 94c3d75a29f..00d0fcaac76 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 2e86599f792..c69809e8205 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 6c0cc4b6c70..41e3944dc18 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 c90707c75b7..844280d29b6 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 22fda616649..8be481e4736 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 d999b939103..5ed5f834f68 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 5477fd8a348..01603a40658 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 0c663b1ca03..d07b71fb6cb 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 1bcd5d4c5f0..31f6eefb5fa 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 9a5f62794c4..33d954d3a88 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 8be64169b44..abbaf447bba 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 b018d7f280e..5f24290a1fd 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 65334af306f..735c30a943c 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 ec1baae2e69..3527921af84 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 40fcbfa6838..2e21f0c249e 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 343c7663fd2..ac3c014894d 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 9d5baa4c403..2496dd4c472 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 9c5c46242af..1d2980f6564 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 1b7c6a64eb0..dd03e61e17e 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 c73d16537ac..a93d6d5d7e5 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 7922d98ea99..d32644f471c 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 4532757d052..99a555dedca 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 b475a8e9958..07faf98cf74 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 4d89a078b2d..540c7716189 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 ca99f0106d2..47e4c3fd6d5 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 3b5c8f5a356..1184c98e182 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 dfeff3f683b..cb2efb3edd0 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 f15ad4fbc5c..850d48b6526 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 b1294a21054..e9a620bb4cc 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 a43833efa43..0ddee9ec5db 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 944d910fec4..55d78b59437 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 @@ -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 0cc51e7d08f..81339bf2a53 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 1b3a29b27a4..845b31ca04a 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 6148ee11bcf..0b1779d7e2b 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 3556e7e1b4a..35f73b35d3c 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` @@ -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 fbbf0a2aef8..603e7712ebc 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 a414f244b08..2464a8a57ce 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 d53e2181cef..2a2ae668dae 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 8417ec29640..ca07c25aaa5 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 c51d5a43045..9f38de11819 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 index b580d42686f..2d57db2b778 100644 --- 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 @@ -18,3 +18,7 @@ --; -- 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 index e704d61d70c..0cb10f4a0ef 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41900to41910.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41900to41910.sql @@ -30,3 +30,43 @@ 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 1bb1905443a..00000000000 --- a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql +++ /dev/null @@ -1,81 +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 ---; - --- 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" '); -ALTER TABLE `resource_count` -DROP INDEX `i_resource_count__type_accountId`, -DROP INDEX `i_resource_count__type_domaintId`, -ADD UNIQUE INDEX `i_resource_count__type_tag_accountId` (`type`,`tag`,`account_id`), -ADD UNIQUE INDEX `i_resource_count__type_tag_domaintId` (`type`,`tag`,`domain_id`); - - -ALTER TABLE `cloud`.`resource_reservation` - ADD COLUMN `resource_id` bigint unsigned NULL; - -ALTER TABLE `cloud`.`resource_reservation` - MODIFY COLUMN `amount` bigint NOT 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 `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`)); 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 00000000000..cb317c69b79 --- /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 00000000000..12ead739d84 --- /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 00000000000..c36b71c2f25 --- /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 `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 00000000000..a00d50a7e10 --- /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 00000000000..3dd6c18f57c --- /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 00000000000..5f257f2965b --- /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 00000000000..4677c8bbd3f --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql @@ -0,0 +1,747 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT 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 `cloud`.`netris_providers` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40), + `zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID', + `host_id` bigint unsigned NOT NULL COMMENT 'Host ID', + `name` varchar(40), + `url` varchar(255) NOT NULL, + `username` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `site_name` varchar(255) NOT NULL, + `tenant_name` varchar(255) NOT NULL, + `netris_tag` varchar(255) NOT NULL, + `created` datetime NOT NULL COMMENT 'created date', + `removed` datetime COMMENT 'removed date if not null', + PRIMARY KEY (`id`), + CONSTRAINT `fk_netris_providers__zone_id` FOREIGN KEY `fk_netris_providers__zone_id` (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + INDEX `i_netris_providers__zone_id`(`zone_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Drop the Tungsten and NSX columns from the network offerings (replaced by checking the provider on the ntwk_offering_service_map table) +ALTER TABLE `cloud`.`network_offerings` DROP COLUMN `for_tungsten`; +ALTER TABLE `cloud`.`network_offerings` DROP COLUMN `for_nsx`; + +-- Drop the Tungsten and NSX columns from the VPC offerings (replaced by checking the provider on the vpc_offering_service_map table) +ALTER TABLE `cloud`.`vpc_offerings` DROP COLUMN `for_nsx`; + +-- Add next_hop to the static_routes table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.static_routes', 'next_hop', 'varchar(50) COMMENT "next hop of the static route" AFTER `vpc_gateway_id`'); + +-- Add `for_router` to `user_ip_address` table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_ip_address', 'for_router', 'tinyint(1) DEFAULT 0 COMMENT "True if the ip address is used by Domain Router to expose services"'); + +-- Add Netris Autoscaling rules +INSERT IGNORE INTO `cloud`.`counter` (uuid, provider, source, name, value, created) VALUES (UUID(), 'Netris', 'cpu', 'VM CPU - average percentage', 'vm.cpu.average.percentage', NOW()); +INSERT IGNORE INTO `cloud`.`counter` (uuid, provider, source, name, value, created) VALUES (UUID(), 'Netris', 'memory', 'VM Memory - average percentage', 'vm.memory.average.percentage', NOW()); + +-- Rename user_vm_details to vm_instance_details +ALTER TABLE `cloud`.`user_vm_details` RENAME TO `cloud`.`vm_instance_details`; +ALTER TABLE `cloud`.`vm_instance_details` DROP FOREIGN KEY `fk_user_vm_details__vm_id`; +ALTER TABLE `cloud`.`vm_instance_details` ADD CONSTRAINT `fk_vm_instance_details__vm_id` FOREIGN KEY (vm_id) REFERENCES vm_instance(id) ON DELETE CASCADE; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule', 'uuid', 'VARCHAR(40) NOT NULL'); +UPDATE `cloud`.`backup_schedule` SET uuid = UUID(); + +-- Extension framework +UPDATE `cloud`.`configuration` SET value = CONCAT(value, ',External') WHERE name = 'hypervisor.list'; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) NOT NULL UNIQUE, + `name` varchar(255) NOT NULL, + `description` varchar(4096), + `type` varchar(255) NOT NULL COMMENT 'Type of the extension: Orchestrator, etc', + `relative_path` varchar(2048) NOT NULL COMMENT 'Path for the extension relative to the root extensions directory', + `path_ready` tinyint(1) DEFAULT '0' COMMENT 'True if the extension path is in ready state across management servers', + `is_user_defined` tinyint(1) DEFAULT '0' COMMENT 'True if the extension is added by admin', + `state` char(32) NOT NULL COMMENT 'State of the extension - Enabled or Disabled', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_details` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `extension_id` bigint unsigned NOT NULL COMMENT 'extension to which the detail is related to', + `name` varchar(255) NOT NULL COMMENT 'name of the detail', + `value` varchar(255) NOT NULL COMMENT 'value of the detail', + `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_details__extension_id` FOREIGN KEY (`extension_id`) + REFERENCES `extension` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_resource_map` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `extension_id` bigint(20) unsigned NOT NULL, + `resource_id` bigint(20) unsigned NOT NULL, + `resource_type` char(255) NOT NULL, + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_resource_map__extension_id` FOREIGN KEY (`extension_id`) + REFERENCES `cloud`.`extension`(`id`) ON DELETE CASCADE, + INDEX `idx_extension_resource` (`resource_id`, `resource_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_resource_map_details` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `extension_resource_map_id` bigint unsigned NOT NULL COMMENT 'mapping to which the detail is related', + `name` varchar(255) NOT NULL COMMENT 'name of the detail', + `value` varchar(255) NOT NULL COMMENT 'value of the detail', + `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_resource_map_details__map_id` FOREIGN KEY (`extension_resource_map_id`) + REFERENCES `extension_resource_map` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_custom_action` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(255) NOT NULL UNIQUE, + `name` varchar(255) NOT NULL, + `description` varchar(4096), + `extension_id` bigint(20) unsigned NOT NULL, + `resource_type` varchar(255), + `allowed_role_types` int unsigned NOT NULL DEFAULT '1', + `success_message` varchar(4096), + `error_message` varchar(4096), + `enabled` boolean DEFAULT true, + `timeout` int unsigned NOT NULL DEFAULT '5' COMMENT 'The timeout in seconds to wait for the action to complete before failing', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_custom_action__extension_id` FOREIGN KEY (`extension_id`) + REFERENCES `cloud`.`extension`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_custom_action_details` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `extension_custom_action_id` bigint(20) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `value` TEXT NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + CONSTRAINT `fk_custom_action_details__action_id` FOREIGN KEY (`extension_custom_action_id`) + REFERENCES `cloud`.`extension_custom_action`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template', 'extension_id', 'bigint unsigned DEFAULT NULL COMMENT "id of the extension"'); + +-- Add built-in Extensions and Custom Actions + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`( + IN ext_name VARCHAR(255), + IN ext_desc VARCHAR(255), + IN ext_path VARCHAR(255) +) +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension` WHERE `name` = ext_name + ) THEN + INSERT INTO `cloud`.`extension` ( + `uuid`, `name`, `description`, `type`, + `relative_path`, `path_ready`, + `is_user_defined`, `state`, `created`, `removed` + ) + VALUES ( + UUID(), ext_name, ext_desc, 'Orchestrator', + ext_path, 1, 0, 'Enabled', NOW(), NULL + ) +; END IF +;END; + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`( + IN ext_name VARCHAR(255), + IN detail_key VARCHAR(255), + IN detail_value TEXT, + IN display TINYINT(1) +) +BEGIN + DECLARE ext_id BIGINT +; SELECT `id` INTO ext_id FROM `cloud`.`extension` WHERE `name` = ext_name LIMIT 1 +; IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension_details` + WHERE `extension_id` = ext_id AND `name` = detail_key + ) THEN + INSERT INTO `cloud`.`extension_details` ( + `extension_id`, `name`, `value`, `display` + ) + VALUES ( + ext_id, detail_key, detail_value, display + ) +; END IF +;END; + +CALL `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`('Proxmox', 'Sample extension for Proxmox written in bash', 'Proxmox/proxmox.sh'); +CALL `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`('Proxmox', 'orchestratorrequirespreparevm', 'true', 0); + +CALL `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`('HyperV', 'Sample extension for HyperV written in python', 'HyperV/hyperv.py'); + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`( + IN ext_name VARCHAR(255), + IN action_name VARCHAR(255), + IN action_desc VARCHAR(4096), + IN resource_type VARCHAR(255), + IN allowed_roles INT UNSIGNED, + IN success_msg VARCHAR(4096), + IN error_msg VARCHAR(4096), + IN timeout_seconds INT UNSIGNED +) +BEGIN + DECLARE ext_id BIGINT +; SELECT `id` INTO ext_id FROM `cloud`.`extension` WHERE `name` = ext_name LIMIT 1 +; IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension_custom_action` WHERE `name` = action_name AND `extension_id` = ext_id + ) THEN + INSERT INTO `cloud`.`extension_custom_action` ( + `uuid`, `name`, `description`, `extension_id`, `resource_type`, + `allowed_role_types`, `success_message`, `error_message`, + `enabled`, `timeout`, `created`, `removed` + ) + VALUES ( + UUID(), action_name, action_desc, ext_id, resource_type, + allowed_roles, success_msg, error_msg, + 1, timeout_seconds, NOW(), NULL + ) +; END IF +;END; + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS` ( + IN ext_name VARCHAR(255), + IN action_name VARCHAR(255), + IN param_json TEXT +) +BEGIN + DECLARE action_id BIGINT UNSIGNED +; SELECT `eca`.`id` INTO action_id FROM `cloud`.`extension_custom_action` `eca` + JOIN `cloud`.`extension` `e` ON `e`.`id` = `eca`.`extension_id` + WHERE `eca`.`name` = action_name AND `e`.`name` = ext_name LIMIT 1 +; IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension_custom_action_details` + WHERE `extension_custom_action_id` = action_id + AND `name` = 'parameters' + ) THEN + INSERT INTO `cloud`.`extension_custom_action_details` ( + `extension_custom_action_id`, + `name`, + `value`, + `display` + ) VALUES ( + action_id, + 'parameters', + param_json, + 0 + ) +; END IF +;END; + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'ListSnapshots', 'List Instance snapshots', 'VirtualMachine', 15, 'Snapshots fetched for {{resourceName}} in {{extensionName}}', 'List Snapshots failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'CreateSnapshot', 'Create an Instance snapshot', 'VirtualMachine', 15, 'Snapshot created for {{resourceName}} in {{extensionName}}', 'Snapshot creation failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'RestoreSnapshot', 'Restore Instance to the specific snapshot', 'VirtualMachine', 15, 'Successfully restored snapshot for {{resourceName}} in {{extensionName}}', 'Restore snapshot failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'DeleteSnapshot', 'Delete the specified snapshot', 'VirtualMachine', 15, 'Successfully deleted snapshot for {{resourceName}} in {{extensionName}}', 'Delete snapshot failed for {{resourceName}}', 60); + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'ListSnapshots', + '[]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'CreateSnapshot', + '[ + { + "name": "snap_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + }, + { + "name": "snap_description", + "type": "STRING", + "validationformat": "NONE", + "required": false + }, + { + "name": "snap_save_memory", + "type": "BOOLEAN", + "validationformat": "NONE", + "required": false + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'RestoreSnapshot', + '[ + { + "name": "snap_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'DeleteSnapshot', + '[ + { + "name": "snap_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'ListSnapshots', 'List checkpoints/snapshots for the Instance', 'VirtualMachine', 15, 'Snapshots fetched for {{resourceName}} in {{extensionName}}', 'List Snapshots failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'CreateSnapshot', 'Create a checkpoint/snapshot for the Instance', 'VirtualMachine', 15, 'Snapshot created for {{resourceName}} in {{extensionName}}', 'Snapshot creation failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'RestoreSnapshot', 'Restore Instance to the specified snapshot', 'VirtualMachine', 15, 'Successfully restored snapshot for {{resourceName}} in {{extensionName}}', 'Restore snapshot failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'DeleteSnapshot', 'Delete the specified snapshot', 'VirtualMachine', 15, 'Successfully deleted snapshot for {{resourceName}} in {{extensionName}}', 'Delete snapshot failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'Suspend', 'Suspend the Instance by freezing its current state in RAM', 'VirtualMachine', 15, 'Successfully suspended {{resourceName}} in {{extensionName}}', 'Suspend failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'Resume', 'Resumes a suspended Instance, restoring CPU execution from memory.', 'VirtualMachine', 15, 'Successfully resumed {{resourceName}} in {{extensionName}}', 'Resume failed for {{resourceName}}', 60); + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'ListSnapshots', + '[]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'CreateSnapshot', + '[ + { + "name": "snapshot_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'RestoreSnapshot', + '[ + { + "name": "snapshot_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'DeleteSnapshot', + '[ + { + "name": "snapshot_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'Suspend', + '[]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'Resume', + '[]' +); + +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `cidr` varchar(255) DEFAULT NULL COMMENT 'CloudStack managed vms get IP address from cidr.In general this cidr also serves as the network CIDR. But in case IP reservation feature is being used by a Guest network, networkcidr is the Effective network CIDR for that network'; +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `gateway` varchar(255) DEFAULT NULL COMMENT 'gateway(s) for this network configuration'; +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `ip6_cidr` varchar(1024) DEFAULT NULL COMMENT 'IPv6 cidr(s) for this network'; +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `ip6_gateway` varchar(1024) DEFAULT NULL COMMENT 'IPv6 gateway(s) for this network'; + +-- Add columns name, description and backup_interval_type to backup table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'name', 'VARCHAR(255) NULL COMMENT "name of the backup"'); +UPDATE `cloud`.`backups` backup INNER JOIN `cloud`.`vm_instance` vm ON backup.vm_id = vm.id SET backup.name = vm.name; +ALTER TABLE `cloud`.`backups` MODIFY COLUMN `name` VARCHAR(255) NOT NULL; +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'description', 'VARCHAR(1024) COMMENT "description for the backup"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'backup_interval_type', 'int(5) COMMENT "type of backup, e.g. manual, recurring - hourly, daily, weekly or monthly"'); + +-- Create backup details table +CREATE TABLE IF NOT EXISTS `cloud`.`backup_details` ( + `id` bigint unsigned NOT NULL auto_increment, + `backup_id` bigint unsigned NOT NULL COMMENT 'backup id', + `name` varchar(255) NOT NULL, + `value` TEXT NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should detail be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_backup_details__backup_id` FOREIGN KEY `fk_backup_details__backup_id`(`backup_id`) REFERENCES `backups`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Add diskOfferingId, deviceId, minIops and maxIops to backed_volumes in backups table +UPDATE `cloud`.`backups` b +INNER JOIN `cloud`.`vm_instance` vm ON b.vm_id = vm.id +SET b.backed_volumes = ( + SELECT CONCAT("[", + GROUP_CONCAT( + CONCAT( + "{\"uuid\":\"", v.uuid, "\",", + "\"type\":\"", v.volume_type, "\",", + "\"size\":", v.`size`, ",", + "\"path\":\"", IFNULL(v.path, 'null'), "\",", + "\"deviceId\":", IFNULL(v.device_id, 'null'), ",", + "\"diskOfferingId\":\"", doff.uuid, "\",", + "\"minIops\":", IFNULL(v.min_iops, 'null'), ",", + "\"maxIops\":", IFNULL(v.max_iops, 'null'), + "}" + ) + SEPARATOR "," + ), + "]") + FROM `cloud`.`volumes` v + LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id + WHERE v.instance_id = vm.id +); + +-- Add diskOfferingId, deviceId, minIops and maxIops to backup_volumes in vm_instance table +UPDATE `cloud`.`vm_instance` vm +SET vm.backup_volumes = ( + SELECT CONCAT("[", + GROUP_CONCAT( + CONCAT( + "{\"uuid\":\"", v.uuid, "\",", + "\"type\":\"", v.volume_type, "\",", + "\"size\":", v.`size`, ",", + "\"path\":\"", IFNULL(v.path, 'null'), "\",", + "\"deviceId\":", IFNULL(v.device_id, 'null'), ",", + "\"diskOfferingId\":\"", doff.uuid, "\",", + "\"minIops\":", IFNULL(v.min_iops, 'null'), ",", + "\"maxIops\":", IFNULL(v.max_iops, 'null'), + "}" + ) + SEPARATOR "," + ), + "]") + FROM `cloud`.`volumes` v + LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id + WHERE v.instance_id = vm.id +) +WHERE vm.backup_offering_id IS NOT NULL; + +-- Add column allocated_size to object_store table. Rename column 'used_bytes' to 'used_size' +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.object_store', 'allocated_size', 'bigint unsigned COMMENT "allocated size in bytes"'); +ALTER TABLE `cloud`.`object_store` CHANGE COLUMN `used_bytes` `used_size` BIGINT UNSIGNED COMMENT 'used size in bytes'; +ALTER TABLE `cloud`.`object_store` MODIFY COLUMN `total_size` bigint unsigned COMMENT 'total size in bytes'; +UPDATE `cloud`.`object_store` +JOIN ( + SELECT object_store_id, SUM(quota) AS total_quota + FROM `cloud`.`bucket` + WHERE removed IS NULL + GROUP BY object_store_id +) buckets_quota_sum_view ON `object_store`.id = buckets_quota_sum_view.object_store_id +SET `object_store`.allocated_size = buckets_quota_sum_view.total_quota; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100cleanup.sql new file mode 100644 index 00000000000..9165565f544 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100cleanup.sql @@ -0,0 +1,20 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade from 4.20.1.0 to 4.21.0.0 +--; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-420to421.sql b/engine/schema/src/main/resources/META-INF/db/schema-420to421.sql index b99af287bc5..25c025c5651 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-442to450.sql b/engine/schema/src/main/resources/META-INF/db/schema-442to450.sql index 90a52bd4273..d2ba408241e 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 1868a090800..b8dd0477db9 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 49cfc8346c5..bac3b1e6fab 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 a910a8b7799..1aa63020124 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 dc0cd6d4d75..23670757247 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 72aade4e501..fef961502fa 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 7320bda5906..5e29435855d 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 668cbb692b9..5c27eed68f0 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_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql index 87546a9d118..edc164c40cb 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql @@ -31,6 +31,7 @@ select `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`, @@ -63,10 +64,20 @@ select `cpucount`.`count` AS `cpuTotal`, `memorylimit`.`max` AS `memoryLimit`, `memorycount`.`count` AS `memoryTotal`, + `gpulimit`.`max` AS `gpuLimit`, + `gpucount`.`count` AS `gpuTotal`, `primary_storage_limit`.`max` AS `primaryStorageLimit`, `primary_storage_count`.`count` AS `primaryStorageTotal`, `secondary_storage_limit`.`max` AS `secondaryStorageLimit`, `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`, @@ -147,6 +158,12 @@ from `cloud`.`resource_count` memorycount ON account.id = memorycount.account_id and memorycount.type = 'memory' and memorycount.tag IS NULL left join + `cloud`.`resource_limit` gpulimit ON account.id = gpulimit.account_id + and gpulimit.type = 'gpu' and gpulimit.tag IS NULL + left join + `cloud`.`resource_count` gpucount ON account.id = gpucount.account_id + and gpucount.type = 'gpu' and gpucount.tag IS NULL + left join `cloud`.`resource_limit` primary_storage_limit ON account.id = primary_storage_limit.account_id and primary_storage_limit.type = 'primary_storage' and primary_storage_limit.tag IS NULL left join @@ -159,6 +176,30 @@ from `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.data_center_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql index c34df4f1cbf..46aea863fc5 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql @@ -42,6 +42,7 @@ select data_center.type, data_center.removed, data_center.sort_key, + data_center.storage_access_groups, domain.id domain_id, domain.uuid domain_uuid, domain.name domain_name, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql index 70394e8fd6d..a12e02bcfdb 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql @@ -58,6 +58,7 @@ select host.resource_state host_resource_state, vm_template.id template_id, vm_template.uuid template_uuid, + vm_template.arch arch, service_offering.id service_offering_id, service_offering.uuid service_offering_uuid, service_offering.name service_offering_name, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql index 201ece95023..14fd87536aa 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql @@ -55,10 +55,20 @@ select `cpucount`.`count` AS `cpuTotal`, `memorylimit`.`max` AS `memoryLimit`, `memorycount`.`count` AS `memoryTotal`, + `gpulimit`.`max` AS `gpuLimit`, + `gpucount`.`count` AS `gpuTotal`, `primary_storage_limit`.`max` AS `primaryStorageLimit`, `primary_storage_count`.`count` AS `primaryStorageTotal`, `secondary_storage_limit`.`max` AS `secondaryStorageLimit`, - `secondary_storage_count`.`count` AS `secondaryStorageTotal` + `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 @@ -122,6 +132,12 @@ from `cloud`.`resource_count` memorycount ON domain.id = memorycount.domain_id and memorycount.type = 'memory' and memorycount.tag IS NULL left join + `cloud`.`resource_limit` gpulimit ON domain.id = gpulimit.domain_id + and gpulimit.type = 'gpu' and gpulimit.tag IS NULL + left join + `cloud`.`resource_count` gpucount ON domain.id = gpucount.domain_id + and gpucount.type = 'gpu' and gpucount.tag IS NULL + left join `cloud`.`resource_limit` primary_storage_limit ON domain.id = primary_storage_limit.domain_id and primary_storage_limit.type = 'primary_storage' and primary_storage_limit.tag IS NULL left join @@ -132,4 +148,28 @@ from 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'; + 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.gui_themes_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql new file mode 100644 index 00000000000..3173274623e --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql @@ -0,0 +1,38 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- VIEW `cloud`.`gui_themes_view`; + +DROP VIEW IF EXISTS `cloud`.`gui_themes_view`; + +CREATE VIEW `cloud`.`gui_themes_view` AS +SELECT + `cloud`.`gui_themes`.`id` AS `id`, + `cloud`.`gui_themes`.`uuid` AS `uuid`, + `cloud`.`gui_themes`.`name` AS `name`, + `cloud`.`gui_themes`.`description` AS `description`, + `cloud`.`gui_themes`.`css` AS `css`, + `cloud`.`gui_themes`.`json_configuration` AS `json_configuration`, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'commonName' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) common_names, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'domain' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) domains, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'account' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) accounts, + `cloud`.`gui_themes`.`recursive_domains` AS `recursive_domains`, + `cloud`.`gui_themes`.`is_public` AS `is_public`, + `cloud`.`gui_themes`.`created` AS `created`, + `cloud`.`gui_themes`.`removed` AS `removed` +FROM `cloud`.`gui_themes` LEFT JOIN `cloud`.`gui_themes_details` ON `cloud`.`gui_themes_details`.`gui_theme_id` = `cloud`.`gui_themes`.`id` +GROUP BY `cloud`.`gui_themes`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql index 5c6d4fd772b..d9f4e267159 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql @@ -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.mshost_peer_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_peer_view.sql new file mode 100644 index 00000000000..5f741449d85 --- /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.network_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql index bae73deda32..368566c32b3 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql @@ -59,10 +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`.`for_nsx` AS `for_nsx`, - `network_offerings`.`nsx_mode` AS `nsx_mode`, + `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, @@ -74,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.service_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql index c894429adf8..eb987af3ffb 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql @@ -71,6 +71,20 @@ SELECT `service_offering`.`dynamic_scaling_enabled` AS `dynamic_scaling_enabled`, `service_offering`.`disk_offering_strictness` AS `disk_offering_strictness`, `vsphere_storage_policy`.`value` AS `vsphere_storage_policy`, + `lease_duration_details`.`value` AS `lease_duration`, + `lease_expiry_action_details`.`value` AS `lease_expiry_action`, + `gpu_card`.`id` AS `gpu_card_id`, + `gpu_card`.`uuid` AS `gpu_card_uuid`, + `gpu_card`.`name` AS `gpu_card_name`, + `vgpu_profile`.`id` AS `vgpu_profile_id`, + `vgpu_profile`.`uuid` AS `vgpu_profile_uuid`, + `vgpu_profile`.`name` AS `vgpu_profile_name`, + `vgpu_profile`.`video_ram` AS `vgpu_profile_video_ram`, + `vgpu_profile`.`max_heads` AS `vgpu_profile_max_heads`, + `vgpu_profile`.`max_resolution_x` AS `vgpu_profile_max_resolution_x`, + `vgpu_profile`.`max_resolution_y` AS `vgpu_profile_max_resolution_y`, + `service_offering`.`gpu_count` AS `gpu_count`, + `service_offering`.`gpu_display` AS `gpu_display`, GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, @@ -87,6 +101,10 @@ FROM INNER JOIN `cloud`.`disk_offering` ON service_offering.disk_offering_id = disk_offering.id LEFT JOIN + `cloud`.`vgpu_profile` ON service_offering.vgpu_profile_id = vgpu_profile.id + LEFT JOIN + `cloud`.`gpu_card` ON vgpu_profile.card_id = gpu_card.id + LEFT JOIN `cloud`.`service_offering_details` AS `domain_details` ON `domain_details`.`service_offering_id` = `service_offering`.`id` AND `domain_details`.`name`='domainid' LEFT JOIN `cloud`.`domain` AS `domain` ON FIND_IN_SET(`domain`.`id`, `domain_details`.`value`) @@ -109,5 +127,11 @@ FROM LEFT JOIN `cloud`.`service_offering_details` AS `vsphere_storage_policy` ON `vsphere_storage_policy`.`service_offering_id` = `service_offering`.`id` AND `vsphere_storage_policy`.`name` = 'storagepolicy' + LEFT JOIN + `cloud`.`service_offering_details` AS `lease_duration_details` ON `lease_duration_details`.`service_offering_id` = `service_offering`.`id` + AND `lease_duration_details`.`name` = 'leaseduration' + LEFT JOIN + `cloud`.`service_offering_details` AS `lease_expiry_action_details` ON `lease_expiry_action_details`.`service_offering_id` = `service_offering`.`id` + AND `lease_expiry_action_details`.`name` = 'leaseexpiryaction' GROUP BY `service_offering`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.shared_filesystem_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.shared_filesystem_view.sql new file mode 100644 index 00000000000..1f72babd1ce --- /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 c6b8d6b4d05..d0eddc1fc4b 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 e6cc9458208..641017bdd5b 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql @@ -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 40b416b16de..76a8be16bda 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql @@ -41,6 +41,7 @@ SELECT `vm_template`.`guest_os_id` AS `guest_os_id`, `guest_os`.`uuid` AS `guest_os_uuid`, `guest_os`.`display_name` AS `guest_os_name`, + `guest_os`.`category_id` AS `guest_os_category_id`, `vm_template`.`bootable` AS `bootable`, `vm_template`.`prepopulate` AS `prepopulate`, `vm_template`.`cross_zones` AS `cross_zones`, @@ -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 7eedc03712b..340cfa9055f 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 @@ -39,6 +39,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 7a057dc0330..94bc8640fd5 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql @@ -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 index fd21fff1494..ffeb93e8fa7 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql @@ -39,6 +39,8 @@ SELECT `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`, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql index 9aca869b510..751d8f91a25 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql @@ -28,8 +28,7 @@ select `vpc_offerings`.`display_text` AS `display_text`, `vpc_offerings`.`state` AS `state`, `vpc_offerings`.`default` AS `default`, - `vpc_offerings`.`for_nsx` AS `for_nsx`, - `vpc_offerings`.`nsx_mode` AS `nsx_mode`, + `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`, @@ -37,6 +36,8 @@ select `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`, 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 00000000000..54ba52fba4a --- /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 00000000000..443463eec4b --- /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 00000000000..f9528d5d57f --- /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 00000000000..f8673fa9c9f --- /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.listDistinctHypervisorsArchAcrossClusters(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.listDistinctHypervisorsArchAcrossClusters(zoneId); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(Hypervisor.HypervisorType.VMware, result.get(0).first()); + assertEquals(CPU.CPUArch.amd64, result.get(0).second()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/gpu/dao/GpuCardDaoImplTest.java b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuCardDaoImplTest.java new file mode 100644 index 00000000000..e0a283add99 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuCardDaoImplTest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.utils.db.SearchCriteria; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class GpuCardDaoImplTest { + + @Spy + @InjectMocks + GpuCardDaoImpl gpuCardDaoImpl = new GpuCardDaoImpl(); + + @Test + public void findByVendorIdAndDeviceId() { + doReturn(mock(GpuCardVO.class)).when(gpuCardDaoImpl).findOneBy(any(SearchCriteria.class)); + + GpuCardVO gpuCard = gpuCardDaoImpl.findByVendorIdAndDeviceId("0d1a", "1a3b"); + Assert.assertNotNull("Expected non-null gpu card", gpuCard); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuCardDaoImpl).findOneBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", + "gpu_card.vendor_id = ? AND gpu_card.device_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/gpu/dao/GpuDeviceDaoImplTest.java b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuDeviceDaoImplTest.java new file mode 100644 index 00000000000..1780fbd3df3 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuDeviceDaoImplTest.java @@ -0,0 +1,277 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuDeviceVO; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchCriteria; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class GpuDeviceDaoImplTest { + + @Spy + @InjectMocks + GpuDeviceDaoImpl gpuDeviceDao = new GpuDeviceDaoImpl(); + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void listByIds_emptyList() { + List devices = gpuDeviceDao.listByIds(null); + Assert.assertTrue("Expected empty list", devices.isEmpty()); + devices = gpuDeviceDao.listByIds(Collections.emptyList()); + Assert.assertTrue("Expected empty list", devices.isEmpty()); + } + + @Test + public void listByIds() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByIds(List.of(1L, 2L, 3L)); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + SearchCriteria sc = scCaptor.getValue(); + Assert.assertEquals("Expected correct where clause", "gpu_device.id IN (?,?,?)", sc.getWhereClause().trim()); + } + + @Test + public void findByHostIdAndBusAddress() { + doReturn(mock(GpuDeviceVO.class)).when(gpuDeviceDao).findOneBy(any(SearchCriteria.class)); + + GpuDeviceVO device = gpuDeviceDao.findByHostIdAndBusAddress(1L, "0000:00:1f.6"); + + Assert.assertNotNull("Expected non-null device", device); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).findOneBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.host_id = ? AND gpu_device.bus_address = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void listByHostId() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByHostId(1L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.host_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void listByVmId() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByVmId(1L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + + Assert.assertEquals("Expected correct where clause", "gpu_device.vm_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void isVgpuProfileInUse() { + doReturn(1).when(gpuDeviceDao).getCount(any(SearchCriteria.class)); + + boolean vgpuProfileInUse = gpuDeviceDao.isVgpuProfileInUse(1L); + + Assert.assertTrue("Expected vGPU profile to be in use", vgpuProfileInUse); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).getCount(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.vgpu_profile_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void isGpuCardInUse() { + doReturn(1).when(gpuDeviceDao).getCount(any(SearchCriteria.class)); + + boolean vgpuProfileInUse = gpuDeviceDao.isGpuCardInUse(1L); + + Assert.assertTrue("Expected GPU Card to be in use", vgpuProfileInUse); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).getCount(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.card_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void listByHostAndVm() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).search(any(SearchCriteria.class), any()); + + List devices = gpuDeviceDao.listByHostAndVm(1L, 2L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Filter.class); + verify(gpuDeviceDao).search(scCaptor.capture(), filterCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.host_id = ? AND gpu_device.vm_id = ?", + scCaptor.getValue().getWhereClause().trim()); + Assert.assertNull("Expected no filter", filterCaptor.getValue()); + } + + @Test + public void listDevicesForAllocation() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).search(any(SearchCriteria.class), any()); + + List devices = gpuDeviceDao.listDevicesForAllocation(1L, 2L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Filter.class); + verify(gpuDeviceDao).search(scCaptor.capture(), filterCaptor.capture()); + Assert.assertEquals("Expected correct where clause", + "gpu_device.host_id = ? AND gpu_device.vgpu_profile_id=? AND gpu_device.state = ? AND gpu_device" + + ".managed_state = ? AND gpu_device.type != ?", + scCaptor.getValue().getWhereClause().trim()); + Assert.assertNull("Expected no filter", filterCaptor.getValue()); + } + + @Test + public void searchAndCountGpuDevices() { + } + + @Test + public void getDistinctGpuCardIds_no_devices() { + doReturn(null).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctGpuCardIds(); + + Assert.assertTrue("Expected empty list", cardIds.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + + @Test + public void getDistinctGpuCardIds() { + GpuDeviceVO device1 = mock(GpuDeviceVO.class); + GpuDeviceVO device2 = mock(GpuDeviceVO.class); + GpuDeviceVO device3 = mock(GpuDeviceVO.class); + when(device1.getCardId()).thenReturn(1L); + when(device2.getCardId()).thenReturn(2L); + when(device3.getCardId()).thenReturn(1L); + + doReturn(List.of(device1, device2, device3)).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctGpuCardIds(); + + Assert.assertEquals("Expected 2 card IDs", 2, cardIds.size()); + + Assert.assertTrue("Expected card ID 1 in list", cardIds.contains(1L)); + Assert.assertTrue("Expected card ID 2 in list", cardIds.contains(2L)); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void getDistinctVgpuProfileIds_no_devices() { + doReturn(null).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctVgpuProfileIds(); + + Assert.assertTrue("Expected empty list", cardIds.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + + @Test + public void getDistinctVgpuProfileIds() { + GpuDeviceVO device1 = mock(GpuDeviceVO.class); + GpuDeviceVO device2 = mock(GpuDeviceVO.class); + GpuDeviceVO device3 = mock(GpuDeviceVO.class); + when(device1.getVgpuProfileId()).thenReturn(1L); + when(device2.getVgpuProfileId()).thenReturn(2L); + when(device3.getVgpuProfileId()).thenReturn(1L); + + doReturn(List.of(device1, device2, device3)).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctVgpuProfileIds(); + + Assert.assertEquals("Expected 2 VgpuProfile IDs", 2, cardIds.size()); + + Assert.assertTrue("Expected VgpuProfile ID 1 in list", cardIds.contains(1L)); + Assert.assertTrue("Expected VgpuProfile ID 2 in list", cardIds.contains(2L)); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + + @Test + public void listByParentGpuDeviceId() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByParentGpuDeviceId(1L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.parent_gpu_device_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/gpu/dao/VgpuProfileDaoImplTest.java b/engine/schema/src/test/java/com/cloud/gpu/dao/VgpuProfileDaoImplTest.java new file mode 100644 index 00000000000..cd7199d020f --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/gpu/dao/VgpuProfileDaoImplTest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.gpu.dao; + +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.db.SearchCriteria; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class VgpuProfileDaoImplTest { + + @Spy + @InjectMocks + VgpuProfileDaoImpl vgpuProfileDaoImpl = new VgpuProfileDaoImpl(); + + @Test + public void findByNameAndCardId() { + doReturn(mock(VgpuProfileVO.class)).when(vgpuProfileDaoImpl).findOneBy(any(SearchCriteria.class)); + + VgpuProfileVO vgpuProfile = vgpuProfileDaoImpl.findByNameAndCardId("test-profile", 1L); + Assert.assertNotNull("Expected non-null vgpu profile", vgpuProfile); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(vgpuProfileDaoImpl).findOneBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", + "vgpu_profile.name = ? AND vgpu_profile.card_id=?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void removeByCardId() { + doReturn(1).when(vgpuProfileDaoImpl).remove(any(SearchCriteria.class)); + + int removed = vgpuProfileDaoImpl.removeByCardId(123L); + Assert.assertEquals("Expected one vgpu profile removed", 1, removed); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(vgpuProfileDaoImpl).remove(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "vgpu_profile.card_id=?", + scCaptor.getValue().getWhereClause().trim()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/host/HostVOTest.java b/engine/schema/src/test/java/com/cloud/host/HostVOTest.java index cd9ac3cc172..3262c4cc291 100755 --- a/engine/schema/src/test/java/com/cloud/host/HostVOTest.java +++ b/engine/schema/src/test/java/com/cloud/host/HostVOTest.java @@ -20,14 +20,18 @@ import com.cloud.offering.ServiceOffering; import com.cloud.service.ServiceOfferingVO; import com.cloud.template.VirtualMachineTemplate; import com.cloud.vm.VirtualMachine; -import java.util.Arrays; -import java.util.List; +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; -import org.junit.Test; -import org.junit.Before; -import org.mockito.Mockito; public class HostVOTest { HostVO host; @@ -37,7 +41,7 @@ public class HostVOTest { public void setUp() throws Exception { host = new HostVO(); offering = new ServiceOfferingVO("TestSO", 0, 0, 0, 0, 0, - false, "TestSO", false,VirtualMachine.Type.User,false); + false, "TestSO", false, VirtualMachine.Type.User, false); } @Test @@ -52,14 +56,14 @@ public class HostVOTest { @Test public void testRightTag() { - host.setHostTags(Arrays.asList("tag1","tag2"), false); + 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); + host.setHostTags(Arrays.asList("tag1", "tag2"), false); offering.setHostTag("tag2,tag4"); assertFalse(host.checkHostServiceOfferingTags(offering)); } @@ -87,40 +91,59 @@ public class HostVOTest { @Test public void testEitherNoSOOrTemplate() { - assertFalse(host.checkHostServiceOfferingAndTemplateTags(null, Mockito.mock(VirtualMachineTemplate.class))); - assertFalse(host.checkHostServiceOfferingAndTemplateTags(Mockito.mock(ServiceOffering.class), null)); + 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))); + 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))); + 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)); + 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)); + 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); + 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)); + 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"); - assertFalse(host.checkHostServiceOfferingAndTemplateTags(offering, template)); + 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 00000000000..c76eedb01e9 --- /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 e13ad42ec80..6de8960ae74 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.Mock; 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 class AutoScaleVmGroupVmMapDaoImplTest { 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 00000000000..d8f6a08d8d3 --- /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 00000000000..8e06c7618f6 --- /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 00000000000..fa957194903 --- /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/OpRouterMonitorServiceDaoImplTest.java b/engine/schema/src/test/java/com/cloud/network/dao/OpRouterMonitorServiceDaoImplTest.java new file mode 100644 index 00000000000..7d0b1b069ba --- /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 00000000000..c60e9b1f1bf --- /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 00000000000..f86df6bdd36 --- /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 00000000000..7f151730c9c --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java @@ -0,0 +1,319 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.cpu.CPU; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.Storage; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VMTemplateZoneVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class VMTemplateDaoImplTest { + + @Mock + HostDao hostDao; + + @Mock + VMTemplateZoneDao templateZoneDao; + + @Spy + @InjectMocks + VMTemplateDaoImpl templateDao = new VMTemplateDaoImpl(); + + @Test + public void testFindLatestTemplateByName_ReturnsTemplate() { + VMTemplateVO expectedTemplate = new VMTemplateVO(); + List returnedList = Collections.singletonList(expectedTemplate); + doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", CPU.CPUArch.getDefault()); + assertNotNull("Expected a non-null template", result); + assertEquals("Expected the returned template to be the first element", expectedTemplate, result); + } + + @Test + public void testFindLatestTemplateByName_ReturnsNullWhenNoTemplateFound() { + List emptyList = Collections.emptyList(); + doReturn(emptyList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", CPU.CPUArch.getDefault()); + assertNull("Expected null when no templates are found", result); + } + + @Test + public void testFindLatestTemplateByName_NullArch() { + VMTemplateVO expectedTemplate = new VMTemplateVO(); + List returnedList = Collections.singletonList(expectedTemplate); + doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", null); + assertNotNull("Expected a non-null template even if arch is null", result); + assertEquals("Expected the returned template to be the first element", expectedTemplate, result); + } + + @Test + public void testGetSortedTemplatesListWithPreferredArch_PreferredProvided() { + VMTemplateVO templatePreferred = Mockito.mock(VMTemplateVO.class); + when(templatePreferred.getArch()).thenReturn(CPU.CPUArch.amd64); + VMTemplateVO templateOther = Mockito.mock(VMTemplateVO.class); + when(templateOther.getArch()).thenReturn(CPU.CPUArch.arm64); + + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), templatePreferred); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), templateOther); + List sortedList = templateDao.getSortedTemplatesListWithPreferredArch(uniqueTemplates, + CPU.CPUArch.amd64.getType()); + assertEquals(2, sortedList.size()); + assertEquals(templatePreferred, sortedList.get(0)); + assertEquals(templateOther, sortedList.get(1)); + } + + @Test + public void testGetSortedTemplatesListWithPreferredArch_NoPreferred() { + VMTemplateVO template1 = Mockito.mock(VMTemplateVO.class); + when(template1.getId()).thenReturn(1L); + VMTemplateVO template2 = Mockito.mock(VMTemplateVO.class); + when(template2.getId()).thenReturn(2L); + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), template1); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), template2); + List sortedList = templateDao.getSortedTemplatesListWithPreferredArch(uniqueTemplates, ""); + assertEquals(2, sortedList.size()); + assertEquals(template2, sortedList.get(0)); + assertEquals(template1, sortedList.get(1)); + } + + @Test + public void testFindSystemVMReadyTemplates() { + long zoneId = 1L; + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + String preferredArch = CPU.CPUArch.arm64.getType(); + List> availableHypervisors = new ArrayList<>(); + availableHypervisors.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64)); + availableHypervisors.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64)); + doReturn(availableHypervisors).when(hostDao).listDistinctHypervisorArchTypes(zoneId); + VMTemplateVO template1 = Mockito.mock(VMTemplateVO.class); + when(template1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(template1.getArch()).thenReturn(CPU.CPUArch.amd64); + VMTemplateVO template2 = Mockito.mock(VMTemplateVO.class); + when(template2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(template2.getArch()).thenReturn(CPU.CPUArch.arm64); + List templatesFromDb = Arrays.asList(template1, template2); + doReturn(templatesFromDb).when(templateDao).listBy(any(), any()); + SearchBuilder sb = mock(SearchBuilder.class); + templateDao.readySystemTemplateSearch = sb; + when(sb.create()).thenReturn(mock(SearchCriteria.class)); + List result = templateDao.findSystemVMReadyTemplates(zoneId, hypervisorType, preferredArch); + assertNotNull(result); + assertEquals(2, result.size()); + assertEquals(template2, result.get(0)); + assertEquals(template1, result.get(1)); + } + + @Test + public void testFindRoutingTemplates() { + Hypervisor.HypervisorType hType = Hypervisor.HypervisorType.KVM; + String templateName = "TestRouting"; + String preferredArch = CPU.CPUArch.amd64.getType(); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + when(template.getArch()).thenReturn(CPU.CPUArch.amd64); + List templatesFromDb = Collections.singletonList(template); + doReturn(templatesFromDb).when(templateDao).listBy(any(), any()); + SearchBuilder sb = mock(SearchBuilder.class); + when(sb.create()).thenReturn(mock(SearchCriteria.class)); + templateDao.tmpltTypeHyperSearch2 = sb; + List result = templateDao.findRoutingTemplates(hType, templateName, preferredArch); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(template, result.get(0)); + } + + @Test + public void testFindLatestTemplateByTypeAndHypervisorAndArch_Found() { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.amd64; + Storage.TemplateType type = Storage.TemplateType.SYSTEM; + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + List templatesFromDb = Collections.singletonList(template); + doReturn(templatesFromDb).when(templateDao).listBy(any(), any()); + VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type); + assertNotNull(result); + assertEquals(template, result); + } + + @Test + public void testFindLatestTemplateByTypeAndHypervisorAndArch_NotFound() { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.x86; + Storage.TemplateType type = Storage.TemplateType.SYSTEM; + doReturn(Collections.emptyList()).when(templateDao).listBy(any(), any()); + VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type); + assertNull(result); + } + + private void mockTemplateZoneJoin() { + VMTemplateZoneVO templateZoneVO = mock(VMTemplateZoneVO.class); + SearchBuilder templateZoneVOSearchBuilder = mock(SearchBuilder.class); + when(templateZoneVOSearchBuilder.entity()).thenReturn(templateZoneVO); + when(templateZoneDao.createSearchBuilder()).thenReturn(templateZoneVOSearchBuilder); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithDataCenterId() { + Long dataCenterId = 1L; + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + Boolean isIso = true; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + mockTemplateZoneJoin(); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithoutDataCenterId() { + Long dataCenterId = null; + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + Boolean isIso = false; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ)); + verify(searchBuilder, never()).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithoutArch() { + Long dataCenterId = 1L; + CPU.CPUArch arch = null; + Boolean isIso = true; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + mockTemplateZoneJoin(); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithoutIsIso() { + Long dataCenterId = 1L; + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + Boolean isIso = null; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + mockTemplateZoneJoin(); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ)); + verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListIdsByExtensionId_ReturnsIds() { + long extensionId = 42L; + List expectedIds = Arrays.asList(1L, 2L, 3L); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + SearchCriteria searchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.entity()).thenReturn(mock(VMTemplateVO.class)); + when(searchBuilder.create()).thenReturn(searchCriteria); + doReturn(expectedIds).when(templateDao).customSearchIncludingRemoved(eq(searchCriteria), isNull()); + List result = templateDao.listIdsByExtensionId(extensionId); + assertEquals(expectedIds, result); + verify(searchCriteria).setParameters("extensionId", extensionId); + verify(templateDao).customSearchIncludingRemoved(eq(searchCriteria), isNull()); + } +} 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 7968ee4a375..9445efeb089 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 static org.mockito.Mockito.when; 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 class VolumeDaoImplTest { 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 00000000000..bab36ef00cf --- /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/SystemVmTemplateRegistrationTest.java b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java new file mode 100644 index 00000000000..27656fd33b0 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java @@ -0,0 +1,427 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.upgrade; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; +import org.apache.cloudstack.utils.security.DigestHelper; +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.cpu.CPU; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.utils.HttpUtils; +import com.cloud.utils.Pair; +import com.cloud.utils.UriUtils; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +@RunWith(MockitoJUnitRunner.class) +public class SystemVmTemplateRegistrationTest { + + @Mock + ClusterDao clusterDao; + + @Mock + VMTemplateDao vmTemplateDao; + + @Spy + @InjectMocks + SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration(); + + private void setupMetadataFile(MockedStatic mockedStatic, String content) { + try { + String location = "metadata.ini"; + if (StringUtils.isNotBlank(content)) { + File tempFile = File.createTempFile("metadata", ".ini"); + location = tempFile.getAbsolutePath(); + Files.write(Paths.get(location), content.getBytes()); + tempFile.deleteOnExit(); + } + mockedStatic.when(SystemVmTemplateRegistration::getMetadataFilePath).thenReturn(location); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void test_parseMetadataFile_noFile() { + try (MockedStatic mockedStatic = + Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) { + setupMetadataFile(mockedStatic, null); + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, + SystemVmTemplateRegistration::parseMetadataFile); + assertTrue(exception.getMessage().contains("Failed to parse systemVM template metadata file")); + } + } + + @Test + public void test_parseMetadataFile_invalidContent() { + try (MockedStatic mockedStatic = + Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) { + setupMetadataFile(mockedStatic, "abc"); + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, + SystemVmTemplateRegistration::parseMetadataFile); + assertTrue(exception.getMessage().contains("Failed to parse systemVM template metadata file")); + } + } + + @Test + public void test_parseMetadataFile_success() { + String metadataFileContent = "[default]\n" + + "version = x.y.z.0\n" + + "\n" + + "[kvm-x86_64]\n" + + "templatename = systemvm-kvm-x.y.z\n" + + "checksum = abc1\n" + + "downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" + + "filename = systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" + + "\n" + + "[kvm-aarch64]\n" + + "templatename = systemvm-kvm-x.y.z\n" + + "checksum = abc2\n" + + "downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" + + "filename = systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" + + "\n" + + "[vmware]\n" + + "templatename = systemvm-vmware-x.y.z\n" + + "checksum = abc3\n" + + "downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-vmware.ova\n" + + "filename = systemvmtemplate-x.y.z-vmware.ova\n"; + try (MockedStatic mockedStatic = + Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) { + setupMetadataFile(mockedStatic, metadataFileContent); + String version = SystemVmTemplateRegistration.parseMetadataFile(); + assertEquals("x.y.z.0", version); + } + assertNull(SystemVmTemplateRegistration.NewTemplateMap.get("xenserver")); + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = + SystemVmTemplateRegistration.NewTemplateMap.get("kvm-x86_64"); + assertNotNull(templateDetails); + assertEquals(CPU.CPUArch.amd64, templateDetails.getArch()); + assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType()); + templateDetails = + SystemVmTemplateRegistration.NewTemplateMap.get("kvm-aarch64"); + assertNotNull(templateDetails); + assertEquals(CPU.CPUArch.arm64, templateDetails.getArch()); + assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType()); + templateDetails = + SystemVmTemplateRegistration.NewTemplateMap.get("vmware"); + assertNotNull(templateDetails); + assertNull(templateDetails.getArch()); + assertEquals(Hypervisor.HypervisorType.VMware, templateDetails.getHypervisorType()); + } + + @Test + public void testMountStore_nullStoreUrl() throws Exception { + try (MockedStatic + 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 ade9e8370a8..00746334e02 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 @@ package com.cloud.hypervisor.kvm.resource.wrapper; 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 final class StorPoolBackupSnapshotCommandWrapper extends CommandWrapper 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 final class StorPoolBackupSnapshotCommandWrapper extends CommandWrapper 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/StorPoolDownloadVolumeCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java index 37284b597d2..1679e646e18 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 @@ -114,11 +114,7 @@ public final class StorPoolDownloadVolumeCommandWrapper extends CommandWrapper 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 +150,9 @@ public class StorPoolStorageAdaptor implements StorageAdaptor { } 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 +163,10 @@ public class StorPoolStorageAdaptor implements StorageAdaptor { } 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 +267,7 @@ public class StorPoolStorageAdaptor implements StorageAdaptor { } @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 +363,85 @@ public class StorPoolStorageAdaptor implements StorageAdaptor { 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 +459,104 @@ public class StorPoolStorageAdaptor implements StorageAdaptor { 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 (isTemplateExtractable(templateFilePath)) { + srcTemplateFilePath = sourceFile.getParent() + "/" + templateName; + String extractCommand = getExtractCommandForDownloadedFile(templateFilePath, srcTemplateFilePath); + Script.runSimpleBashScript(extractCommand); + Script.runSimpleBashScript("rm -f " + templateFilePath); + } + return srcTemplateFilePath; + } + + private boolean isTemplateExtractable(String templatePath) { + String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'"); + return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip"); + } + + private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateFile) { + if (downloadedTemplateFile.endsWith(".zip")) { + return "unzip -p " + downloadedTemplateFile + " | cat > " + templateFile; + } else if (downloadedTemplateFile.endsWith(".bz2")) { + return "bunzip2 -c " + downloadedTemplateFile + " > " + templateFile; + } else if (downloadedTemplateFile.endsWith(".gz")) { + return "gunzip -c " + downloadedTemplateFile + " > " + templateFile; + } else { + throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile); + } + } + + 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 aa0a8849d90..ab5dc03d343 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.api.to.HostTO; 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 class StorPoolStoragePool implements KVMStoragePool { @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 @@ public class StorPoolStoragePool implements KVMStoragePool { 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/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 00000000000..26004205709 --- /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 00000000000..456f5b90639 --- /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 f7e643ca62b..d93990ee071 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,11 +18,13 @@ */ package org.apache.cloudstack.storage.datastore.driver; +import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; +import com.cloud.storage.dao.SnapshotDetailsVO; 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; @@ -31,17 +33,23 @@ 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.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; @@ -78,14 +86,22 @@ import com.cloud.agent.api.to.DataStoreTO; 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,8 +110,8 @@ import com.cloud.storage.VMTemplateStoragePoolVO; 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; @@ -133,9 +149,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 +166,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); @@ -199,7 +225,6 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { StorPoolUtil.volumeRemoveTags(StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true), conn); } } - } private void updateStoragePool(final long poolId, final long deltaUsedBytes) { @@ -251,15 +276,25 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { 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 +325,21 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { } } + 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,68 +378,120 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { 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); - - long maxIops = payload.newMaxIops == null ? Long.valueOf(0) : payload.newMaxIops; - - 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); - - 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); - - 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); - } - - 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())); - } - } - } catch (Exception e) { - logger.debug("sending resize command failed", e); - err = e.toString(); - } + err = resizeVolume(data, path, vol); } else { err = String.format("Invalid object type \"%s\" passed to resize", data.getType()); } - CreateCmdResult res = new CreateCmdResult(path, answer); + CreateCmdResult res = new CreateCmdResult(path, new Answer(null, err != null, err)); res.setResult(err); callback.complete(res); } + private String resizeVolume(DataObject data, String path, VolumeObject vol) { + String err = null; + ResizeVolumePayload payload = (ResizeVolumePayload)vol.getpayload(); + boolean needResize = vol.getSize() != payload.newSize; + + final String name = StorPoolStorageAdaptor.getVolumeNameFromPath(path, true); + final long oldSize = vol.getSize(); + Long oldMaxIops = vol.getMaxIops(); + + try { + SpConnectionDesc conn = StorPoolUtil.getSpConnection(data.getDataStore().getUuid(), data.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + + 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())); + } + } 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 { + 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; + } + + 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 public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback callback) { String err = null; @@ -402,7 +504,7 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { } 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()); @@ -432,6 +534,54 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { callback.complete(res); } + 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; @@ -474,13 +624,16 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { try { if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.VOLUME) { SnapshotInfo sinfo = (SnapshotInfo)srcData; - final String snapshotName = StorPoolHelper.getSnapshotName(srcData.getId(), srcData.getUuid(), snapshotDataStoreDao, _snapshotDetailsDao); + 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); + + StorPoolVolumeDef spVolume = createVolumeWithTags(sinfo, snapshotName, vinfo, volumeName, size, conn); + SpApiResponse resp = StorPoolUtil.volumeCreate(spVolume, conn); if (resp.getError() == null) { updateStoragePool(dstData.getDataStore().getId(), size); @@ -492,65 +645,30 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { StorPoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", StorPoolUtil.getNameFromResponse(resp, false), to.getUuid(), 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())); } } else { - err = String.format("The snapshot %s does not exists neither on primary, neither on secondary storage. Cannot create volume from snapshot", snapshotName); + answer = new Answer(cmd, false, String.format("The snapshot %s does not exists neither on primary, neither on secondary storage. Cannot create volume from snapshot", snapshotName)); } } 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 (StorPoolConfigurationManager.BypassSecondaryStorage.value() || snapshotDetail != null) { SnapshotObjectTO snapshot = (SnapshotObjectTO) srcData.getTO(); answer = new CopyCmdAnswer(snapshot); } else { @@ -642,22 +760,17 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { 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 +786,13 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { 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 +818,34 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { } 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); @@ -856,6 +989,62 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { 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 +1176,9 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { SnapshotObjectTO snapTo = (SnapshotObjectTO)snapshot.getTO(); snapTo.setPath(StorPoolUtil.devPath(name.split("~")[1])); answer = new CreateObjectAnswer(snapTo); - StorPoolHelper.addSnapshotDetails(snapshot.getId(), snapshot.getUuid(), snapTo.getPath(), _snapshotDetailsDao); + StorPoolHelper.addSnapshotDetails(snapshot.getId(), snapshot.getUuid(), snapTo.getPath(), snapshotDetailsDao); //add primary storage of snapshot - StorPoolHelper.addSnapshotDetails(snapshot.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, String.valueOf(snapshot.getDataStore().getId()), _snapshotDetailsDao); + StorPoolHelper.addSnapshotDetails(snapshot.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, String.valueOf(snapshot.getDataStore().getId()), snapshotDetailsDao); StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.takeSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", name, snapshot.getUuid(), volumeName, vinfo.getUuid()); } } catch (Exception e) { @@ -1004,7 +1193,7 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Override public void revertSnapshot(final SnapshotInfo snapshot, final SnapshotInfo snapshotOnPrimaryStore, final 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 +1248,7 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { } 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() : ""; } @@ -1141,18 +1330,33 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { 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 +1398,67 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { 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 a41ff66229c..56b150a04e6 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 java.util.concurrent.TimeUnit; 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 @@ public class StorPoolStatsCollector extends ManagerBase { 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 class StorPoolStatsCollector extends ManagerBase { 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; @@ -182,4 +205,90 @@ public class StorPoolStatsCollector extends ManagerBase { } } } + + 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 339ee625c58..d299fe34ffc 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 java.util.UUID; 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; 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.VMTemplateDetailsDao; 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 class StorPoolPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCy 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 b696990c533..7e0986bc63b 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 class StorPoolHostListener implements HypervisorHostListener { 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 class StorPoolHostListener implements HypervisorHostListener { 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 class StorPoolHostListener implements HypervisorHostListener { 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)); } } } @@ -173,7 +171,7 @@ public class StorPoolHostListener implements HypervisorHostListener { StorPoolHelper.setSpClusterIdIfNeeded(hostId, mspAnswer.getClusterId(), clusterDao, hostDao, clusterDetailsDao); - 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 5a84e699f52..f13d296af3b 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 @@ -163,11 +163,12 @@ public class StorPoolHelper { 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); } @@ -218,17 +219,17 @@ public class StorPoolHelper { } 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( 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 214f93f735f..fa9248033bf 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.JsonElement; 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,18 @@ public class StorPoolUtil { 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 OBJECT_DOES_NOT_EXIST = "objectDoesNotExist"; + public static enum StorpoolRights { RO("ro"), RW("rw"), DETACH("detach"); @@ -417,31 +432,35 @@ public class StorPoolUtil { 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 +473,7 @@ public class StorPoolUtil { 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 +481,7 @@ public class StorPoolUtil { 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 +490,7 @@ public class StorPoolUtil { 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 +503,27 @@ public class StorPoolUtil { 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 +543,7 @@ public class StorPoolUtil { 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 +571,8 @@ public class StorPoolUtil { 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 +580,7 @@ public class StorPoolUtil { 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 +588,14 @@ public class StorPoolUtil { 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 +606,28 @@ public class StorPoolUtil { 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 +648,12 @@ public class StorPoolUtil { 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); } @@ -639,7 +689,7 @@ public class StorPoolUtil { 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 +697,7 @@ public class StorPoolUtil { 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 bd5380cc160..aa972d44343 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 @@ -149,18 +149,21 @@ 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 (!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); - if (snapshotName != null) { + if (snapshotName != null && StorPoolConfigurationManager.BypassSecondaryStorage.value()) { return StrategyPriority.HIGHEST; } } @@ -190,7 +193,7 @@ public class StorPoolDataMotionStrategy implements DataMotionStrategy { 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 { @@ -216,24 +219,23 @@ public class StorPoolDataMotionStrategy implements DataMotionStrategy { } 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)); @@ -294,7 +296,7 @@ public class StorPoolDataMotionStrategy implements DataMotionStrategy { 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 +387,7 @@ public class StorPoolDataMotionStrategy implements DataMotionStrategy { 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); @@ -521,13 +523,13 @@ public class StorPoolDataMotionStrategy implements DataMotionStrategy { 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 +557,15 @@ public class StorPoolDataMotionStrategy implements DataMotionStrategy { 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 dcb2b226467..e4e930c8dee 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,16 @@ 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); + @Override public String getConfigComponentName() { return StorPoolConfigurationManager.class.getSimpleName(); @@ -51,6 +61,6 @@ public class StorPoolConfigurationManager implements Configurable { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] { BypassSecondaryStorage, StorPoolClusterId, AlternativeEndPointEnabled, AlternativeEndpoint, VolumesStatsInterval, StorageStatsInterval }; + return new ConfigKey[] { BypassSecondaryStorage, StorPoolClusterId, AlternativeEndPointEnabled, AlternativeEndpoint, VolumesStatsInterval, StorageStatsInterval, DeleteAfterInterval, ListSnapshotsWithDeleteAfterInterval }; } } 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 0b58247c661..5ec86df91e1 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,10 +16,19 @@ // under the License. package org.apache.cloudstack.storage.snapshot; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; +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 org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -40,23 +49,13 @@ 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 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.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 @@ -117,9 +116,12 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy { if (resp.getError() != null) { final String err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError()); StorPoolUtil.spLog(err); + markSnapshotAsDestroyedIfAlreadyRemoved(snapshotId, resp.getError().getName().equals(StorPoolUtil.OBJECT_DOES_NOT_EXIST)); + throw new CloudRuntimeException(err); } else { res = deleteSnapshotFromDbIfNeeded(snapshotVO, zoneId); - StorPoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfully=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name); + markSnapshotAsDestroyedIfAlreadyRemoved(snapshotId,true); + StorPoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfully=%s, snapshot %s, name=%s", res, snapshotVO, name); } } catch (Exception e) { String errMsg = String.format("Cannot delete snapshot due to %s", e.getMessage()); @@ -127,12 +129,30 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy { } } + List snapshots = _snapshotStoreDao.listBySnapshotIdAndState(snapshotId, State.Ready); + if (res || CollectionUtils.isEmpty(snapshots)) { + updateSnapshotToDestroyed(snapshotVO); + return true; + } return res; } + private void markSnapshotAsDestroyedIfAlreadyRemoved(Long snapshotId, boolean isSnapshotDeleted) { + if (!isSnapshotDeleted) { + return; + } + List snapshotsOnStore = _snapshotStoreDao.listBySnapshotIdAndState(snapshotId, State.Ready); + for (SnapshotDataStoreVO snapshot : snapshotsOnStore) { + if (snapshot.getInstallPath() != null && snapshot.getInstallPath().contains(StorPoolUtil.SP_DEV_PATH)) { + snapshot.setState(State.Destroyed); + _snapshotStoreDao.update(snapshot.getId(), snapshot); + } + } + } + @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) { return StrategyPriority.CANT_HANDLE; @@ -161,20 +181,20 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy { } 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) { @@ -196,7 +216,7 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy { 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(); } } @@ -315,7 +335,7 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy { 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); if (zoneId != null) { @@ -331,9 +351,21 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy { } else { snapshotZoneDao.removeSnapshotFromZones(snapshotVO.getId()); } + if (CollectionUtils.isNotEmpty(retrieveSnapshotEntries(snapshotId, null))) { + return true; + } + updateSnapshotToDestroyed(snapshotVO); 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) { 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 2596b6a5bde..e5b24a3f98c 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 class StorPoolVMSnapshotStrategy extends DefaultVMSnapshotStrategy { 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); } } } @@ -237,8 +237,8 @@ public class StorPoolVMSnapshotStrategy extends DefaultVMSnapshotStrategy { 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()); + err = String.format("Could not find StorPool's snapshot vm snapshot %s and volume [id: %s, uuid: %s, name: %s]", + vmSnapshot, volumeObjectTO.getId(), volumeObjectTO.getUuid(), volumeObjectTO.getName()); logger.error("Could not delete snapshot for vm:" + err); } StorPoolUtil.spLog("StorpoolVMSnapshotStrategy.deleteVMSnapshot snapshotName=%s", snapshotName); @@ -254,10 +254,9 @@ public class StorPoolVMSnapshotStrategy extends DefaultVMSnapshotStrategy { } 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 vm snapshot %s of vm %s failed due to %s", vmSnapshot, userVm, err)); } } vmSnapshotDetailsDao.removeDetails(vmSnapshot.getId()); @@ -344,7 +343,7 @@ public class StorPoolVMSnapshotStrategy extends DefaultVMSnapshotStrategy { 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 vm snapshot [%s] due to %s", vmSnapshot, e.getMessage()); logger.error(errMsg, e); throw new CloudRuntimeException(errMsg); } finally { 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 6451fc8fd39..b4e81f166f0 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.21.0.0-SNAPSHOT ../../pom.xml 2.0.0.AM25 - 1.5 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 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 087bd63c296..eada5f6df39 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 @@ -117,7 +117,7 @@ public class LdapImportUsersCmd extends BaseListCmd { _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()); 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 e5d434d3810..6bfc70ea139 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 @@ -140,10 +140,11 @@ public class LdapListUsersCmd extends BaseListCmd { 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()); 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 7e2114ea00f..52ece5c44f4 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.ServerApiException; 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; @@ -61,9 +62,12 @@ public class LinkAccountToLdapCmd extends BaseCmd { @Parameter(name = ApiConstants.ADMIN, type = CommandType.STRING, required = false, 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, required = false, 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, required = false, description = "Creates the account under the specified role.", since="4.19.1") + private Long roleId; @Inject private LdapManager _ldapManager; @@ -87,12 +91,12 @@ public class LinkAccountToLdapCmd extends BaseCmd { .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"); @@ -132,7 +136,14 @@ public class LinkAccountToLdapCmd extends BaseCmd { } 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.intValue())); + } + + 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 d5187f99c99..c351924de6d 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 @@ -107,12 +107,12 @@ public class LinkDomainToLdapCmd extends BaseCmd { 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"); 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 b8509881594..36c663566cb 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 @@ -45,6 +45,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(); } @@ -74,8 +76,8 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator return rc; } List ldapTrustMapVOs = getLdapTrustMapVOS(domainId); - if(ldapTrustMapVOs != null && ldapTrustMapVOs.size() > 0) { - if(ldapTrustMapVOs.size() == 1 && ldapTrustMapVOs.get(0).getAccountId() == 0) { + if (ldapTrustMapVOs != null && ldapTrustMapVOs.size() > 0) { + 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"); } @@ -125,11 +127,11 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator 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.size() < 1) { 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 +139,12 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator // 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 +164,7 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator 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 +176,21 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator } } 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 +208,7 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator if (logger.isInfoEnabled()) { logger.info(msg); } - if(remove) { + if (remove) { removeUserInCloudStack(userAccount); } else { disableUserInCloudStack(userAccount); @@ -229,23 +240,22 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator 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 { @@ -264,30 +274,34 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator */ 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"); } } 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) ? + private Pair processResultAndAction(UserAccount user, boolean result, boolean timedOut) { + return (!result && (user != null || timedOut)) ? new Pair(result, 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/LdapContextFactory.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapContextFactory.java index e6f23ef8ab3..1c759c6a45e 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.Context; 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; @@ -52,14 +54,14 @@ public class LdapContextFactory { 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); } @@ -73,8 +75,36 @@ public class LdapContextFactory { 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; } } 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 68f5580ed1b..05b8578bb42 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 @@ -166,7 +166,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag 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; } @@ -184,6 +184,11 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag } 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); } @@ -205,7 +210,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag // TODO return the right account for this user final LdapContext context = _ldapContextFactory.createUserContext(principal, password, domainId); closeContext(context); - if(logger.isTraceEnabled()) { + if (logger.isTraceEnabled()) { logger.trace(String.format("User(%s) authenticated for domain(%s)", principal, domainId)); } return true; @@ -229,7 +234,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag @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(); @@ -298,8 +303,8 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag 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 +324,8 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag 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 +338,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag 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 +352,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag 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); @@ -385,7 +390,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag 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); @@ -449,11 +454,12 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag 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()); + account = new AccountVO(cmd.getAccountName(), cmd.getDomainId(), null, cmd.getAccountType(), cmd.getRoleId(), UUID.randomUUID().toString()); accountDao.persist((AccountVO)account); } @@ -475,7 +481,7 @@ public class LdapManagerImpl extends ComponentLifecycleBase implements LdapManag 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) { AccountVO oldAcount = accountDao.findByIdIncludingRemoved(oldVo.getAccountId()); 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 62a3a809b16..adf0f98f294 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 @@ package org.apache.cloudstack.api.command; 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 class LinkAccountToLdapCmdTest implements LdapConfigurationChanger { linkAccountToLdapCmd = new LinkAccountToLdapCmd(); setHiddenField(linkAccountToLdapCmd, "_ldapManager", ldapManager); setHiddenField(linkAccountToLdapCmd, "_accountService", accountService); + setHiddenField(linkAccountToLdapCmd, "_domainService", domainService); } @Test 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 67d0e770522..080347fefd3 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 @@ package org.apache.cloudstack.api.command; 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 class LinkDomainToLdapCmdTest implements LdapConfigurationChanger 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/LdapDirectoryServerConnectionTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/LdapDirectoryServerConnectionTest.java index 2d2690f8e3a..a216084aa3a 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 class LdapDirectoryServerConnectionTest { 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 class LdapDirectoryServerConnectionTest { 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 47f201de479..4197bad4f2d 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 @@ package org.apache.cloudstack.ldap; 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 @@ public class LdapTestConfigTool { 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 c369c454640..6c0ffa264e1 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 e63f9774341..d8f1c14f3b9 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.21.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 132f1481bb6..a447e731743 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 5a1e49874a8..70144b99e6e 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.21.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 6d7123ebe8e..b65027d6a24 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 class OAuth2AuthManagerImpl extends ManagerBase implements OAuth2AuthMana 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 class OAuth2AuthManagerImpl extends ManagerBase implements OAuth2AuthMana 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 1f38adfd63b..dde50c8bb34 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 org.apache.cloudstack.auth.UserOAuth2Authenticator; 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 class OAuth2UserAuthenticator extends AdapterBase implements UserAuthenti 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 class OAuth2UserAuthenticator extends AdapterBase implements UserAuthenti public String encode(String password) { return null; } + + protected boolean isOAuthPluginEnabled() { + return OAuth2IsPluginEnabled.value(); + } } 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 c0d273a5fa5..d1c1889ba99 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.apache.cloudstack.auth.UserOAuth2Authenticator; 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 class OAuth2UserAuthenticatorTest { closeable.close(); } + @Test public void testAuthenticateWithValidCredentials() { String username = "testuser"; @@ -92,13 +102,13 @@ public class OAuth2UserAuthenticatorTest { 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 class OAuth2UserAuthenticatorTest { 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 class OAuth2UserAuthenticatorTest { 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 class OAuth2UserAuthenticatorTest { 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/pbkdf2/pom.xml b/plugins/user-authenticators/pbkdf2/pom.xml index f030e38a6a4..b9529d28873 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.21.0.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/plain-text/pom.xml b/plugins/user-authenticators/plain-text/pom.xml index e378ec8399b..54933571aba 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.21.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 fccff88c4cb..0b22b010e19 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 7a19768fab7..d20569f0418 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.21.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/ListAndSwitchSAMLAccountCmd.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java index 3e6b093abe1..5263ade6b37 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.api.response.UserResponse; 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.UserAccountDao; 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 class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic 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 class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic } 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 class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic || !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 class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic 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 class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic @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 fb4f4cc00a5..bfd47922142 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.UserAccountVO; 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 class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent 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 { @@ -182,7 +190,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent 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 +207,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent params, responseType)); } - String username = null; + String username; Issuer issuer = processedSAMLResponse.getIssuer(); SAMLProviderMetadata spMetadata = samlAuthManager.getSPMetadata(); SAMLProviderMetadata idpMetadata = samlAuthManager.getIdPMetadata(issuer.getValue()); @@ -218,11 +226,13 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent "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 +246,8 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent 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 +273,13 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent 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 +310,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent 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 +370,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent @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/saml/SAML2AuthManager.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java index e52a7e32695..523f694d80b 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,73 @@ import java.util.Collection; 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); + + + 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 0e8790d6558..93b7bc5be93 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 @@ -485,6 +485,13 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage 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 +540,7 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage SAMLServiceProviderSingleSignOnURL, SAMLServiceProviderSingleLogOutURL, SAMLCloudStackRedirectionUrl, SAMLUserAttributeName, SAMLIdentityProviderMetadataURL, SAMLDefaultIdentityProviderId, - SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout}; + SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout, SAMLCheckSignature, + SAMLForceAuthn, SAMLUserSessionKeyPathAttribute, SAMLRequirePasswordLogin}; } } 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 7ffe07a8609..1a9d677d43a 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.IOException; 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.Document; 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 class SAMLUtils { 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,13 +169,36 @@ public class SAMLUtils { 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 + // 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(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); + + return authnRequest; + } + + public static void setRequestedAuthnContext(AuthnRequest authnRequest, boolean requirePasswordAuthentication) { AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder(); AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject( SAMLConstants.SAML20_NS, @@ -183,23 +210,7 @@ public class SAMLUtils { RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject(); requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT); requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef); - - // Creation of AuthRequestObject - AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder(); - AuthnRequest authnRequest = authRequestBuilder.buildObject(); - authnRequest.setID(authnId); - authnRequest.setDestination(idpUrl); - authnRequest.setVersion(SAMLVersion.VERSION_20); - authnRequest.setForceAuthn(false); - 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 LogoutRequest buildLogoutRequest(String logoutUrl, String spId, String nameIdString) { @@ -281,23 +292,56 @@ public class SAMLUtils { } 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 752845edb64..891d028aebf 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 class SAMLUtilsTest extends TestCase { 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 class SAMLUtilsTest extends TestCase { 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 class SAMLUtilsTest extends TestCase { 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 9342a0c7f09..afc2f82b206 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.net.InetAddress; 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 class ListAndSwitchSAMLAccountCmdTest extends TestCase { 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 class ListAndSwitchSAMLAccountCmdTest extends TestCase { 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 class ListAndSwitchSAMLAccountCmdTest extends TestCase { 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 class ListAndSwitchSAMLAccountCmdTest extends TestCase { } 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 39c8c231bf0..426ec4e073f 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 class SAML2LoginAPIAuthenticatorCmdTest { 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 823ab510568..a1ff872551a 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.21.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 3e29fd9ddba..593c8282cb5 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 bde07b6c185..040e6a2fe50 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.21.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 ac27ba527db..6b30a98ef07 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 cda38336291..0a25d81a728 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.21.0.0-SNAPSHOT ../../pom.xml 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 84a0b0cfcb7..73ab7b636d4 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 b52a37a0fa8..06e9969a160 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.21.0.0-SNAPSHOT pom Apache CloudStack Apache CloudStack is an IaaS ("Infrastructure as a Service") cloud orchestration platform. @@ -50,7 +50,7 @@ UTF-8 UTF-8 https://download.cloudstack.org/systemvm - 4.20.0.0 + 4.20.2.0 apache https://sonarcloud.io @@ -58,7 +58,7 @@ 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-opens=java.base/javax.net.ssl=ALL-UNNAMED 1.8 @@ -79,6 +79,7 @@ 3.8.2 2.22.2 4.4.1 + 3.2.0 2.19.0 @@ -89,7 +90,7 @@ 1.15 1.5.0 4.4 - 1.21 + 1.26.0 1.3 1.4 3.1 @@ -101,6 +102,7 @@ 1.10 1.3.3 2.9.0 + 5.1.0 0.5 2.6 2.9.0 @@ -114,9 +116,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 @@ -137,9 +137,10 @@ 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 @@ -161,17 +162,18 @@ 2.12.5 2.2.1 0.1.55 - 20090211 + 20231013 1.2 2.7.0 0.5.3 1.5.0-b01 + 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 @@ -180,12 +182,12 @@ 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 @@ -291,6 +293,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 @@ -362,6 +369,7 @@ commons-daemon ${cs.daemon.version} + org.apache.commons commons-dbcp2 @@ -373,6 +381,11 @@ + + com.zaxxer + HikariCP + ${cs.hikaricp.version} + commons-discovery commons-discovery @@ -453,6 +466,12 @@ reload4j ${cs.reload4j.version} + + mysql + mysql-connector-java + ${cs.mysql.version} + test + log4j apache-log4j-extras @@ -729,7 +748,7 @@ org.mockito - mockito-inline + mockito-core ${cs.mockito.version} test @@ -768,6 +787,11 @@ javax.inject 1 + + com.github.ben-manes.caffeine + caffeine + ${cs.caffeine.version} +
@@ -933,20 +957,6 @@ - - - org.codehaus.gmaven - gmaven-plugin - [1.3,) - - compile - testCompile - - - - - - org.apache.maven.plugins @@ -1013,6 +1023,7 @@ .idea/ .metadata/** .git/** + .github/linters/codespell.txt .gitignore CHANGES.md CONTRIBUTING.md 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 4944fc0218e..546ec1cff77 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 28d5a115914..6811c2191c7 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 de68534f2e4..9d201a2a13e 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 28d5a115914..6811c2191c7 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 de68534f2e4..9d201a2a13e 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 4acb11a1f4f..f9a855b26d1 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 d424bf1f023..16d9f17e8b4 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 41e9c7a1e8e..c061071b96e 100644 --- a/python/lib/cloudutils/configFileOps.py +++ b/python/lib/cloudutils/configFileOps.py @@ -63,7 +63,7 @@ class configFileOps: 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": diff --git a/python/lib/cloudutils/networkConfig.py b/python/lib/cloudutils/networkConfig.py index 2d1f9936d39..ac51f92aa58 100644 --- a/python/lib/cloudutils/networkConfig.py +++ b/python/lib/cloudutils/networkConfig.py @@ -41,7 +41,7 @@ class networkConfig: 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") diff --git a/python/lib/cloudutils/syscfg.py b/python/lib/cloudutils/syscfg.py index 19032ce4aff..78cd58bfbd4 100755 --- a/python/lib/cloudutils/syscfg.py +++ b/python/lib/cloudutils/syscfg.py @@ -39,11 +39,11 @@ class sysConfigAgentFactory: 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 @@ class sysConfigAgent(sysConfig): 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 @@ class sysConfigAgent(sysConfig): 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 @@ class sysConfigEL5(sysConfigAgentRedhatBase): 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 5a6114f1011..ce50516193e 100755 --- a/python/lib/cloudutils/utilities.py +++ b/python/lib/cloudutils/utilities.py @@ -47,11 +47,11 @@ 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: logging.debug("Failed to execute:" + self.getErrMsg()) @@ -124,6 +124,10 @@ class Distribution: 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 c02b4932ee7..92d22c5d556 100644 --- a/quickcloud/pom.xml +++ b/quickcloud/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.21.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 6074ea1ceb2..a434bbe2412 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/test/selenium/cspages/__init__.py b/requirements-dev.txt similarity index 98% rename from test/selenium/cspages/__init__.py rename to requirements-dev.txt index 13a83393a91..99ad3943160 100644 --- a/test/selenium/cspages/__init__.py +++ b/requirements-dev.txt @@ -14,3 +14,5 @@ # 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 96f8c9c1e70..9711afad3d8 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/createtmplt.sh b/scripts/installer/createtmplt.sh index 2d164304604..db3cfd71ee1 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 @@ -92,12 +92,12 @@ uncompress() { ;; 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 +184,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 +203,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 +217,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 +234,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 diff --git a/scripts/installer/createvolume.sh b/scripts/installer/createvolume.sh index 15847c52375..52792e9fc3a 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 @@ -93,12 +93,12 @@ uncompress() { ;; 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 +185,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 +204,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 +218,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 +235,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 diff --git a/scripts/installer/installcentos.sh b/scripts/installer/installcentos.sh index e68e0577a8e..b16fadce048 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 79ea9fda45c..9c7ab72139e 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/run_installer.sh b/scripts/installer/run_installer.sh index 8452e3773cb..3a96c797540 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 945ca968d26..550ec6de91d 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 7990356edc4..f0744a9a822 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 e49d0fa175b..657fdfead20 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 70aa587e60f..5c496345e2c 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 177329a0359..72fca676ce2 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 4f6b5546a5c..69e5417a58b 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 2ef1df29acc..2f043d614f7 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 d0f52a0be44..9d4150958cc 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 f0ccb6e0e76..d291597a57a 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 474e6719366..bdbb19b412c 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 3ce8c55b242..ea6e01f6fb1 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 ae6dee0f239..118311fb620 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 3f23a22cd5a..3ec425c28fc 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 e43dc0bc5ea..3299dac9d0e 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 9aad4c23990..e8ffd7aad41 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 1d0dc8cba12..46b57b0bfcb 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 9b2d37278ac..5f6d4c0416a 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 4bc5c4bc0d2..2078c015ba4 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 5a189353467..02fe845392c 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 55122d4f3a0..4185a7c25df 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 fd301bc5ca1..89744ac3825 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 90b9bef5a8d..c1ded42943c 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/copyVolume.sh b/scripts/storage/multipath/copyVolume.sh index d169198251b..8e6609ea108 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 067e561f8a3..f894076927f 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/qcow2/create_private_template.sh b/scripts/storage/qcow2/create_private_template.sh index 8e9e26c4104..06bf79d44cb 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 b05550c14bb..4bb955e130c 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 @@ -70,12 +70,12 @@ uncompress() { ;; 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 +89,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 +97,7 @@ create_from_file() { cp -f $tmpltimg /$tmpltfs/$tmpltname fi fi - + if [ "$cleanup" == "true" ] then rm -f "$tmpltimg" @@ -161,17 +161,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 0971c9b9255..e3a83aed3bf 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/widgets/Status.vue b/ui/src/components/widgets/Status.vue index 6575d409a3c..6b9f71b5de8 100644 --- a/ui/src/components/widgets/Status.vue +++ b/ui/src/components/widgets/Status.vue @@ -44,6 +44,10 @@ export default { styles: { type: Object, default: () => {} + }, + vmState: { + type: Boolean, + default: false } }, methods: { @@ -84,9 +88,24 @@ export default { case 'ReadWrite': state = this.$t('state.readwrite') break + case 'partiallyallocated': + state = this.$t('state.partiallyallocated') + break case 'InProgress': state = this.$t('state.inprogress') break + case 'Down': + state = this.$t('state.down') + break + case 'Up': + state = this.$t('state.up') + break + case 'Yes': + state = this.$t('label.yes') + break + case 'no': + state = this.$t('label.no') + break } return state.charAt(0).toUpperCase() + state.slice(1) } @@ -113,6 +132,9 @@ export default { case 'up': case 'success': case 'poweron': + case 'primary': + case 'managed': + case 'yes': status = 'success' break case 'alert': @@ -127,6 +149,8 @@ export default { case 'poweroff': case 'stopped': case 'failed': + case 'unmanaged': + case 'no': status = 'error' break case 'migrating': @@ -149,6 +173,8 @@ export default { case 'pending': case 'unsecure': case 'warning': + case 'backup': + case 'partiallyallocated': status = 'warning' break } @@ -168,23 +194,28 @@ export default { if (!(state && this.displayText)) { return '' } + let result if (this.$route.path === '/vmsnapshot' || this.$route.path.includes('/vmsnapshot/')) { - return this.$t('message.vmsnapshot.state.' + state.toLowerCase()) + result = this.$t('message.vmsnapshot.state.' + state.toLowerCase()) + } else if (this.$route.path === '/vm' || this.$route.path.includes('/vm/')) { + result = this.$t('message.vm.state.' + state.toLowerCase()) + } else if (this.$route.path === '/volume' || this.$route.path.includes('/volume/')) { + if (this.vmState) { + result = this.$t('message.vm.state.' + state.toLowerCase()) + } else { + result = this.$t('message.volume.state.' + state.toLowerCase()) + } + } else if (this.$route.path === '/guestnetwork' || this.$route.path.includes('/guestnetwork/')) { + result = this.$t('message.guestnetwork.state.' + state.toLowerCase()) + } else if (this.$route.path === '/publicip' || this.$route.path.includes('/publicip/')) { + result = this.$t('message.publicip.state.' + state.toLowerCase()) } - if (this.$route.path === '/vm' || this.$route.path.includes('/vm/')) { - return this.$t('message.vm.state.' + state.toLowerCase()) + + if (!result || (result.startsWith('message.') && result.endsWith('.state.' + state.toLowerCase()))) { + // Nothing for snapshots, vpcs, gateways, vnpnconn, vpnuser, kubectl, event, project, account, infra. They're all self explanatory + result = this.$t(state) } - if (this.$route.path === '/volume' || this.$route.path.includes('/volume/')) { - return this.$t('message.volume.state.' + state.toLowerCase()) - } - if (this.$route.path === '/guestnetwork' || this.$route.path.includes('/guestnetwork/')) { - return this.$t('message.guestnetwork.state.' + state.toLowerCase()) - } - if (this.$route.path === '/publicip' || this.$route.path.includes('/publicip/')) { - return this.$t('message.publicip.state.' + state.toLowerCase()) - } - // Nothing for snapshots, vpcs, gateways, vnpnconn, vpnuser, kubectl, event, project, account, infra. They're all self explanatory - return this.$t(state) + return result }, getStyle () { let styles = { display: 'inline-flex' } diff --git a/ui/src/config/router.js b/ui/src/config/router.js index c3b69bde23a..582fbaaf2f3 100644 --- a/ui/src/config/router.js +++ b/ui/src/config/router.js @@ -19,6 +19,7 @@ import { UserLayout, BasicLayout, RouteView } from '@/layouts' import AutogenView from '@/views/AutogenView.vue' import IFramePlugin from '@/views/plugins/IFramePlugin.vue' +import ApiDocsPlugin from '@/views/plugins/ApiDocsPlugin.vue' import { shallowRef } from 'vue' import { vueProps } from '@/vue-app' @@ -37,6 +38,8 @@ import infra from '@/config/section/infra' import zone from '@/config/section/zone' import offering from '@/config/section/offering' import config from '@/config/section/config' +import extension from '@/config/section/extension' +import customaction from '@/config/section/extension/customaction' import tools from '@/config/section/tools' import quota from '@/config/section/plugin/quota' import cloudian from '@/config/section/plugin/cloudian' @@ -51,14 +54,13 @@ function generateRouterMap (section) { icon: section.icon, docHelp: vueProps.$applyDocHelpMappings(section.docHelp), searchFilters: section.searchFilters, - related: section.related + related: section.related, + section: true }, component: shallowRef(RouteView) } if (section.children && section.children.length > 0) { - map.redirect = '/' + section.children[0].name - map.meta.permission = section.children[0].permission map.children = [] for (const child of section.children) { if ('show' in child && !child.show()) { @@ -83,7 +85,8 @@ function generateRouterMap (section) { searchFilters: child.searchFilters, related: child.related, actions: child.actions, - tabs: child.tabs + tabs: child.tabs, + customParamHandler: child.customParamHandler }, component: component, hideChildrenInMenu: true, @@ -220,10 +223,11 @@ export function asyncRouterMap () { generateRouterMap(zone), generateRouterMap(offering), generateRouterMap(config), + generateRouterMap(extension), + generateRouterMap(customaction), generateRouterMap(tools), generateRouterMap(quota), generateRouterMap(cloudian), - { path: '/exception', name: 'exception', @@ -275,6 +279,16 @@ export function asyncRouterMap () { }) } + const apidocs = vueProps.$config.apidocs + if (apidocs !== false) { + routerMap[0].children.push({ + path: '/apidocs/', + name: 'apidocs', + component: shallowRef(ApiDocsPlugin), + meta: { title: 'label.api.docs', icon: 'read-outlined' } + }) + } + return routerMap } @@ -289,6 +303,16 @@ export const constantRouterMap = [ path: 'login', name: 'login', component: () => import(/* webpackChunkName: "auth" */ '@/views/auth/Login') + }, + { + path: 'forgotPassword', + name: 'forgotPassword', + component: () => import(/* webpackChunkName: "auth" */ '@/views/auth/ForgotPassword') + }, + { + path: 'resetPassword', + name: 'resetPassword', + component: () => import(/* webpackChunkName: "auth" */ '@/views/auth/ResetPassword') } ] }, diff --git a/ui/src/config/section/account.js b/ui/src/config/section/account.js index 716417587c4..55b950d3901 100644 --- a/ui/src/config/section/account.js +++ b/ui/src/config/section/account.js @@ -24,12 +24,47 @@ export default { icon: 'team-outlined', docHelp: 'adminguide/accounts.html', permission: ['listAccounts'], + searchFilters: () => { + var filters = ['name', 'accounttype', 'domainid'] + if (store.getters.userInfo.roletype === 'Admin') { + filters.push('apikeyaccess') + } + return filters + }, columns: ['name', 'state', 'rolename', 'roletype', 'domainpath'], - details: ['name', 'id', 'rolename', 'roletype', 'domainpath', 'networkdomain', 'iptotal', 'vmtotal', 'volumetotal', 'receivedbytes', 'sentbytes', 'created'], + details: ['name', 'id', 'rolename', 'roletype', 'domainpath', 'networkdomain', 'apikeyaccess', 'iptotal', 'vmtotal', 'volumetotal', 'receivedbytes', 'sentbytes', 'created'], related: [{ name: 'accountuser', title: 'label.users', param: 'account' + }, { + name: 'vm', + title: 'label.vms', + param: 'account' + }, { + name: 'volume', + title: 'label.volumes', + param: 'account' + }, { + name: 'guestnetwork', + title: 'label.networks', + param: 'account' + }, { + name: 'ssh', + title: 'label.sshkeypairs', + param: 'account' + }, { + name: 'userdata', + title: 'label.user.data', + param: 'account' + }, { + name: 'template', + title: 'label.templates', + param: 'account' + }, { + name: 'iso', + title: 'label.isos', + param: 'account' }], filters: () => { const filters = ['enabled', 'disabled', 'locked'] @@ -91,15 +126,8 @@ export default { icon: 'edit-outlined', label: 'label.action.edit.account', dataView: true, - args: ['newname', 'account', 'domainid', 'networkdomain'], - mapping: { - account: { - value: (record) => { return record.name } - }, - domainid: { - value: (record) => { return record.domainid } - } - } + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/iam/EditAccount.vue'))) }, { api: 'updateResourceCount', @@ -200,11 +228,10 @@ export default { message: 'message.delete.account', dataView: true, disabled: (record, store) => { - return record.id !== 'undefined' && store.userInfo.accountid === record.id + return store.userInfo.accountid === record?.id }, - groupAction: true, popup: true, - groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + component: shallowRef(defineAsyncComponent(() => import('@/views/iam/DeleteAccountWrapper.vue'))) } ] } diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index db17b20ef9d..7688566df42 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -17,6 +17,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' +import { isZoneCreated } from '@/utils/zone' export default { name: 'compute', @@ -31,9 +32,9 @@ export default { permission: ['listVirtualMachinesMetrics'], resourceType: 'UserVm', params: () => { - var params = { details: 'servoff,tmpl,iso,nics,backoff' } + var params = { details: 'group,nics,secgrp,tmpl,servoff,diskoff,iso,volume,affgrp,backoff' } if (store.getters.metrics) { - params = { details: 'servoff,tmpl,iso,nics,backoff,stats' } + params = { details: 'all,stats' } } params.isvnf = false return params @@ -43,6 +44,9 @@ export default { if (!(store.getters.project && store.getters.project.id)) { filters.unshift('self') } + if (store.getters.features.instanceleaseenabled) { + filters.push('leased') + } return filters }, columns: () => { @@ -50,8 +54,8 @@ export default { const metricsFields = ['cpunumber', 'cputotal', 'cpuused', 'memorytotal', { memoryused: (record) => { - if (record.memoryintfreekbs <= 0 || record.memorykbs <= 0) { - return '-' + if (!record.memoryintfreekbs || record.memoryintfreekbs <= 0 || record.memorykbs <= 0) { + return '' } return parseFloat(100.0 * (record.memorykbs - record.memoryintfreekbs) / record.memorykbs).toFixed(2) + '%' } @@ -61,6 +65,7 @@ export default { if (store.getters.metrics) { fields.push(...metricsFields) } + fields.push('arch') if (store.getters.userInfo.roletype === 'Admin') { fields.splice(2, 0, 'instancename') fields.push('hostname') @@ -76,11 +81,12 @@ export default { fields.push('zonename') return fields }, - searchFilters: ['name', 'zoneid', 'domainid', 'account', 'groupid', 'tags'], + searchFilters: ['name', 'gpuenabled', 'zoneid', 'domainid', 'account', 'groupid', 'arch', 'extensionid', 'tags'], details: () => { var fields = ['name', 'displayname', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename', - 'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account', - 'domain', 'zonename', 'userdataid', 'userdataname', 'userdataparams', 'userdatadetails', 'userdatapolicy', 'hostcontrolstate'] + 'serviceofferingname', 'gpucount', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'arch', 'boottype', 'bootmode', 'account', + 'domain', 'zonename', 'userdataid', 'userdataname', 'userdataparams', 'userdatadetails', 'userdatapolicy', + 'hostcontrolstate', 'deleteprotection', 'leaseexpirydate', 'leaseexpiryaction'] const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true) if (!listZoneHaveSGEnabled || listZoneHaveSGEnabled.length === 0) { return fields @@ -98,6 +104,7 @@ export default { label: 'label.vm.add', docHelp: 'adminguide/virtual_machines.html#creating-vms', listView: true, + show: isZoneCreated, component: () => import('@/views/compute/DeployVM.vue') }, { @@ -107,6 +114,7 @@ export default { docHelp: 'adminguide/virtual_machines.html#changing-the-vm-name-os-or-group', dataView: true, popup: true, + show: (record) => { return record.vmtype !== 'sharedfsvm' }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/EditVM.vue'))) }, { @@ -118,8 +126,14 @@ export default { dataView: true, groupAction: true, popup: true, - groupMap: (selection, values) => { return selection.map(x => { return { id: x, considerlasthost: values.considerlasthost } }) }, - args: ['considerlasthost'], + groupMap: (selection, values) => { return selection.map(x => { return { id: x, considerlasthost: values.considerlasthost === true } }) }, + args: (record, store) => { + if (['Admin'].includes(store.userInfo.roletype)) { + return ['considerlasthost'] + } + + return [] + }, show: (record) => { return ['Stopped'].includes(record.state) }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/StartVirtualMachine.vue'))) }, @@ -132,7 +146,10 @@ export default { dataView: true, groupAction: true, groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }, - args: ['forced'], + args: (record, store, group) => { + return (['Admin'].includes(store.userInfo.roletype) || store.features.allowuserforcestopvm) + ? ['forced'] : [] + }, show: (record) => { return ['Running'].includes(record.state) } }, { @@ -165,7 +182,7 @@ export default { message: 'message.reinstall.vm', dataView: true, popup: true, - show: (record) => { return ['Running', 'Stopped'].includes(record.state) }, + show: (record) => { return record.hypervisor !== 'External' && ['Running', 'Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' }, disabled: (record) => { return record.hostcontrolstate === 'Offline' }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ReinstallVm.vue'))) }, @@ -175,11 +192,17 @@ export default { label: 'label.action.vmsnapshot.create', docHelp: 'adminguide/virtual_machines.html#virtual-machine-snapshots', dataView: true, - args: ['virtualmachineid', 'name', 'description', 'snapshotmemory', 'quiescevm'], + args: (record, store) => { + var args = ['virtualmachineid', 'name', 'description', 'snapshotmemory'] + if (['KVM', 'VMware'].includes(record.hypervisor)) { + args.push('quiescevm') + } + return args + }, show: (record) => { - return ((['Running'].includes(record.state) && record.hypervisor !== 'LXC') || - (['Stopped'].includes(record.state) && ((record.hypervisor !== 'KVM' && record.hypervisor !== 'LXC') || - (record.hypervisor === 'KVM' && record.pooltype === 'PowerFlex')))) + return record.hypervisor !== 'External' && (((['Running'].includes(record.state) && record.hypervisor !== 'LXC') || + (['Stopped'].includes(record.state) && (!['KVM', 'LXC'].includes(record.hypervisor) || + (record.hypervisor === 'KVM' && ['PowerFlex', 'Filesystem', 'NetworkFilesystem', 'SharedMountPoint'].includes(record.pooltype))))) && record.vmtype !== 'sharedfsvm') }, disabled: (record) => { return record.hostcontrolstate === 'Offline' && record.hypervisor === 'KVM' }, mapping: { @@ -195,9 +218,10 @@ export default { docHelp: 'adminguide/virtual_machines.html#virtual-machine-snapshots', dataView: true, popup: true, - show: (record) => { - return ((['Running'].includes(record.state) && record.hypervisor !== 'LXC') || - (['Stopped'].includes(record.state) && !['KVM', 'LXC'].includes(record.hypervisor))) + show: (record, store) => { + return record.hypervisor !== 'External' && ((record.hypervisor !== 'KVM') || + ['Stopped', 'Destroyed'].includes(record.state) || + store.features.kvmsnapshotenabled) }, disabled: (record) => { return record.hostcontrolstate === 'Offline' && record.hypervisor === 'KVM' }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/CreateSnapshotWizard.vue'))) @@ -210,8 +234,12 @@ export default { docHelp: 'adminguide/virtual_machines.html#backup-offerings', dataView: true, args: ['virtualmachineid', 'backupofferingid'], - show: (record) => { return !record.backupofferingid }, + show: (record) => { return ['Running', 'Stopped', 'Shutdown'].includes(record.state) && record.hypervisor !== 'External' && !record.backupofferingid }, mapping: { + backupofferingid: { + api: 'listBackupOfferings', + params: (record) => { return { zoneid: record.zoneid } } + }, virtualmachineid: { value: (record, params) => { return record.id } } @@ -224,13 +252,9 @@ export default { message: 'message.backup.create', docHelp: 'adminguide/virtual_machines.html#creating-vm-backups', dataView: true, - args: ['virtualmachineid'], show: (record) => { return record.backupofferingid }, - mapping: { - virtualmachineid: { - value: (record, params) => { return record.id } - } - } + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/StartBackup.vue'))) }, { api: 'createBackupSchedule', @@ -272,7 +296,7 @@ export default { docHelp: 'adminguide/templates.html#attaching-an-iso-to-a-vm', dataView: true, popup: true, - show: (record) => { return ['Running', 'Stopped'].includes(record.state) && !record.isoid }, + show: (record) => { return record.hypervisor !== 'External' && ['Running', 'Stopped'].includes(record.state) && !record.isoid && record.vmtype !== 'sharedfsvm' }, disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AttachIso.vue'))) }, @@ -289,7 +313,7 @@ export default { } return args }, - show: (record) => { return ['Running', 'Stopped'].includes(record.state) && 'isoid' in record && record.isoid }, + show: (record) => { return record.hypervisor !== 'External' && ['Running', 'Stopped'].includes(record.state) && 'isoid' in record && record.isoid && record.vmtype !== 'sharedfsvm' }, disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' }, mapping: { virtualmachineid: { @@ -304,7 +328,7 @@ export default { docHelp: 'adminguide/virtual_machines.html#change-affinity-group-for-an-existing-vm', dataView: true, args: ['affinitygroupids'], - show: (record) => { return ['Stopped'].includes(record.state) }, + show: (record) => { return record.hypervisor !== 'External' && ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ChangeAffinity'))), popup: true }, @@ -314,7 +338,7 @@ export default { label: 'label.scale.vm', docHelp: 'adminguide/virtual_machines.html#how-to-dynamically-scale-cpu-and-ram', dataView: true, - show: (record) => { return ['Stopped'].includes(record.state) || (['Running'].includes(record.state) && record.hypervisor !== 'LXC') }, + show: (record) => { return record.hypervisor !== 'External' && (['Stopped'].includes(record.state) || (['Running'].includes(record.state) && record.hypervisor !== 'LXC')) && record.vmtype !== 'sharedfsvm' }, disabled: (record) => { return record.state === 'Running' && !record.isdynamicallyscalable }, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ScaleVM.vue'))) @@ -325,7 +349,7 @@ export default { label: 'label.migrate.instance.to.host', docHelp: 'adminguide/virtual_machines.html#moving-vms-between-hosts-manual-live-migration', dataView: true, - show: (record, store) => { return ['Running'].includes(record.state) && ['Admin'].includes(store.userInfo.roletype) }, + show: (record, store) => { return record.hypervisor !== 'External' && ['Running'].includes(record.state) && ['Admin'].includes(store.userInfo.roletype) }, disabled: (record) => { return record.hostcontrolstate === 'Offline' }, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateWizard.vue'))) @@ -337,7 +361,7 @@ export default { message: 'message.migrate.instance.to.ps', docHelp: 'adminguide/virtual_machines.html#moving-vms-between-hosts-manual-live-migration', dataView: true, - show: (record, store) => { return ['Stopped'].includes(record.state) && ['Admin'].includes(store.userInfo.roletype) }, + show: (record, store) => { return record.hypervisor !== 'External' && ['Stopped'].includes(record.state) && ['Admin'].includes(store.userInfo.roletype) }, disabled: (record) => { return record.hostcontrolstate === 'Offline' }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateVMStorage'))), popup: true @@ -349,7 +373,7 @@ export default { message: 'message.action.instance.reset.password', dataView: true, args: ['password'], - show: (record) => { return ['Stopped'].includes(record.state) && record.passwordenabled }, + show: (record) => { return record.hypervisor !== 'External' && ['Stopped'].includes(record.state) && record.passwordenabled }, response: (result) => { return { message: result.virtualmachine && result.virtualmachine.password ? `The password of VM ${result.virtualmachine.displayname} is ${result.virtualmachine.password}` : null, @@ -365,18 +389,18 @@ export default { message: 'message.desc.reset.ssh.key.pair', docHelp: 'adminguide/virtual_machines.html#resetting-ssh-keys', dataView: true, - show: (record) => { return ['Stopped'].includes(record.state) }, + show: (record) => { return record.hypervisor !== 'External' && ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' }, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ResetSshKeyPair'))) }, { api: 'resetUserDataForVirtualMachine', icon: 'solution-outlined', - label: 'label.reset.userdata.on.vm', + label: 'label.reset.user.data.on.vm', message: 'message.desc.reset.userdata', docHelp: 'adminguide/virtual_machines.html#resetting-userdata', dataView: true, - show: (record) => { return ['Stopped'].includes(record.state) }, + show: (record) => { return record.hypervisor !== 'External' && ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' }, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ResetUserData'))) }, @@ -387,7 +411,7 @@ export default { dataView: true, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AssignInstance'))), popup: true, - show: (record) => { return ['Stopped'].includes(record.state) } + show: (record) => { return record.hypervisor !== 'External' && ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' } }, { api: 'recoverVirtualMachine', @@ -395,7 +419,16 @@ export default { label: 'label.recover.vm', message: 'message.recover.vm', dataView: true, - show: (record, store) => { return ['Destroyed'].includes(record.state) && store.features.allowuserexpungerecovervm } + show: (record, store) => { return record.hypervisor !== 'External' && ['Destroyed'].includes(record.state) && store.features.allowuserexpungerecovervm && record.vmtype !== 'sharedfsvm' } + }, + { + api: 'runCustomAction', + icon: 'play-square-outlined', + label: 'label.run.custom.action', + dataView: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/RunCustomAction'))), + popup: true, + show: (record) => { return ['External'].includes(record.hypervisor) } }, { api: 'unmanageVirtualMachine', @@ -403,7 +436,7 @@ export default { label: 'label.action.unmanage.virtualmachine', message: 'message.action.unmanage.virtualmachine', dataView: true, - show: (record) => { return ['Running', 'Stopped'].includes(record.state) && ['VMware', 'KVM'].includes(record.hypervisor) } + show: (record) => { return record.hypervisor !== 'External' && ['Running', 'Stopped'].includes(record.state) && ['VMware', 'KVM'].includes(record.hypervisor) && record.vmtype !== 'sharedfsvm' } }, { api: 'expungeVirtualMachine', @@ -412,7 +445,7 @@ export default { message: (record) => { return record.backupofferingid ? 'message.action.expunge.instance.with.backups' : 'message.action.expunge.instance' }, docHelp: 'adminguide/virtual_machines.html#deleting-vms', dataView: true, - show: (record, store) => { return ['Destroyed', 'Expunging'].includes(record.state) && store.features.allowuserexpungerecovervm } + show: (record, store) => { return ['Destroyed', 'Expunging'].includes(record.state) && store.features.allowuserexpungerecovervm && record.vmtype !== 'sharedfsvm' } }, { api: 'destroyVirtualMachine', @@ -428,7 +461,7 @@ export default { }, popup: true, groupMap: (selection, values) => { return selection.map(x => { return { id: x, expunge: values.expunge } }) }, - show: (record) => { return ['Running', 'Stopped', 'Error'].includes(record.state) }, + show: (record) => { return ['Running', 'Stopped', 'Error'].includes(record.state) && record.vmtype !== 'sharedfsvm' }, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/DestroyVM.vue'))) } ] @@ -520,6 +553,7 @@ export default { title: 'label.kubernetes', icon: ['fa-solid', 'fa-dharmachakra'], docHelp: 'plugins/cloudstack-kubernetes-service.html', + searchFilters: ['name', 'domainid', 'account', 'state'], permission: ['listKubernetesClusters'], columns: (store) => { var fields = ['name', 'state', 'clustertype', 'size', 'cpunumber', 'memory', 'kubernetesversionname'] @@ -529,7 +563,7 @@ export default { if (store.listAllProjects) { fields.push('project') } - if (store.apis.scaleKubernetesCluster.params.filter(x => x.name === 'autoscalingenabled').length > 0) { + if (store.apis.scaleKubernetesCluster?.params?.filter(x => x.name === 'autoscalingenabled').length > 0) { fields.splice(2, 0, 'autoscalingenabled') } fields.push('zonename') @@ -539,11 +573,13 @@ export default { const filters = ['cloud.managed', 'external.managed'] return filters }, - details: ['name', 'description', 'zonename', 'kubernetesversionname', 'autoscalingenabled', 'minsize', 'maxsize', 'size', 'controlnodes', 'cpunumber', 'memory', 'keypair', 'associatednetworkname', 'account', 'domain', 'zonename', 'clustertype', 'created'], - tabs: [{ - name: 'k8s', - component: shallowRef(defineAsyncComponent(() => import('@/views/compute/KubernetesServiceTab.vue'))) - }], + details: ['name', 'description', 'zonename', 'kubernetesversionname', 'autoscalingenabled', 'minsize', 'maxsize', 'size', 'controlnodes', 'etcdnodes', 'cpunumber', 'memory', 'keypair', 'cniconfigname', 'associatednetworkname', 'account', 'domain', 'zonename', 'clustertype', 'created'], + tabs: [ + { + name: 'k8s', + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/KubernetesServiceTab.vue'))) + } + ], resourceType: 'KubernetesCluster', actions: [ { @@ -553,6 +589,7 @@ export default { docHelp: 'plugins/cloudstack-kubernetes-service.html#creating-a-new-kubernetes-cluster', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/CreateKubernetesCluster.vue'))) }, { @@ -601,6 +638,26 @@ export default { popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/UpgradeKubernetesCluster.vue'))) }, + { + api: 'addNodesToKubernetesCluster', + icon: 'plus-outlined', + label: 'label.kubernetes.cluster.add.nodes.to.cluster', + message: 'message.kubernetes.cluster.add.nodes', + dataView: true, + show: (record) => { return ['Running', 'Alert'].includes(record.state) && record.clustertype === 'CloudManaged' }, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/KubernetesAddNodes.vue'))) + }, + { + api: 'removeNodesFromKubernetesCluster', + icon: 'minus-outlined', + label: 'label.kubernetes.cluster.remove.nodes.from.cluster', + message: 'message.kubernetes.cluster.remove.nodes', + dataView: true, + show: (record) => { return ['Running', 'Alert'].includes(record.state) && record.clustertype === 'CloudManaged' && (record.virtualmachines.filter(vm => vm.isexternalnode) || []).length > 0 }, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/KubernetesRemoveNodes.vue'))) + }, { api: 'deleteKubernetesCluster', icon: 'delete-outlined', @@ -612,6 +669,9 @@ export default { groupAction: true, popup: true, args: (record, store, group) => { + if (record.clustertype === 'CloudManaged') { + return [] + } return (['Admin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervm) ? ['cleanup', 'expunge'] : ['cleanup'] }, @@ -623,9 +683,10 @@ export default { name: 'autoscalevmgroup', title: 'label.autoscale.vm.groups', icon: 'fullscreen-outlined', - docHelp: 'adminguide/autoscale_without_netscaler.html', + docHelp: 'adminguide/autoscale_with_virtual_router.html', resourceType: 'AutoScaleVmGroup', permission: ['listAutoScaleVmGroups'], + searchFilters: ['name', 'zoneid', 'domainid', 'account'], columns: (store) => { var fields = ['name', 'state', 'associatednetworkname', 'publicip', 'publicport', 'privateport', 'minmembers', 'maxmembers', 'availablevirtualmachinecount', 'account'] if (store.listAllProjects) { @@ -677,6 +738,7 @@ export default { icon: 'plus-outlined', label: 'label.new.autoscale.vmgroup', listView: true, + show: isZoneCreated, component: () => import('@/views/compute/CreateAutoScaleVmGroup.vue') }, { @@ -736,7 +798,7 @@ export default { docHelp: 'adminguide/virtual_machines.html#changing-the-vm-name-os-or-group', resourceType: 'VMInstanceGroup', permission: ['listInstanceGroups'], - + searchFilters: ['name', 'zoneid', 'domainid', 'account'], columns: (store) => { var fields = ['name', 'account'] if (store.listAllProjects) { @@ -767,6 +829,7 @@ export default { icon: 'plus-outlined', label: 'label.new.instance.group', listView: true, + show: isZoneCreated, args: ['name'] }, { @@ -794,6 +857,7 @@ export default { icon: 'key-outlined', docHelp: 'adminguide/virtual_machines.html#using-ssh-keys-for-authentication', permission: ['listSSHKeyPairs'], + searchFilters: ['name', 'domainid', 'account', 'fingerprint'], columns: () => { var fields = ['name', 'fingerprint'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { @@ -873,7 +937,7 @@ export default { }, { name: 'userdata', - title: 'label.user.data', + title: 'label.user.data.library', icon: 'solution-outlined', docHelp: 'adminguide/virtual_machines.html#user-data-and-meta-data', permission: ['listUserData'], @@ -892,6 +956,89 @@ export default { }, resourceType: 'UserData', details: ['id', 'name', 'userdata', 'account', 'domain', 'params'], + related: [{ + name: 'vm', + title: 'label.instances', + param: 'userdataid' + }], + tabs: [ + { + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'comments', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/AnnotationsTab.vue'))) + } + ], + actions: [ + { + api: 'registerUserData', + icon: 'plus-outlined', + label: 'label.register.user.data', + docHelp: 'adminguide/virtual_machines.html#user-data-and-meta-data', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/compute/RegisterUserData.vue'))) + }, + { + api: 'deleteUserData', + icon: 'delete-outlined', + label: 'label.remove.user.data', + message: 'message.please.confirm.remove.user.data', + dataView: true, + args: ['id', 'account', 'domainid', 'projectid'], + mapping: { + id: { + value: (record, params) => { return record.id } + }, + projectid: { + value: (record, params) => { return record.projectid } + }, + account: { + value: (record, params) => { return record.account } + }, + domainid: { + value: (record, params) => { return record.domainid } + } + }, + groupAction: true, + popup: true, + groupMap: (selection, values, record) => { + return selection.map(x => { + const data = record.filter(y => { return y.id === x }) + return { + id: x, + account: data[0].account, + domainid: data[0].domainid, + projectid: data[0].projectid + } + }) + } + } + ] + }, + { + name: 'cniconfiguration', + title: 'label.cniconfiguration', + icon: 'solution-outlined', + docHelp: 'adminguide/virtual_machines.html#user-data-and-meta-data', + permission: ['listCniConfiguration'], + columns: () => { + var fields = ['name', 'id'] + if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { + fields.push('account') + if (store.getters.listAllProjects) { + fields.push('project') + } + fields.push('domain') + } else if (store.getters.listAllProjects) { + fields.push('project') + } + return fields + }, + resourceType: 'UserData', + details: ['id', 'name', 'userdata', 'account', 'domain', 'params'], related: [{ name: 'vm', title: 'label.instances', @@ -909,25 +1056,28 @@ export default { ], actions: [ { - api: 'registerUserData', + api: 'registerCniConfiguration', icon: 'plus-outlined', - label: 'label.register.user.data', + label: 'label.register.cni.config', docHelp: 'adminguide/virtual_machines.html#creating-the-ssh-keypair', listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/RegisterUserData.vue'))) }, { - api: 'deleteUserData', + api: 'deleteCniConfiguration', icon: 'delete-outlined', - label: 'label.remove.user.data', - message: 'message.please.confirm.remove.user.data', + label: 'label.remove.cni.configuration', + message: 'message.please.confirm.remove.cni.configuration', dataView: true, - args: ['id', 'account', 'domainid'], + args: ['id', 'account', 'domainid', 'projectid'], mapping: { id: { value: (record, params) => { return record.id } }, + projectid: { + value: (record, params) => { return record.projectid } + }, account: { value: (record, params) => { return record.account } }, @@ -941,7 +1091,10 @@ export default { return selection.map(x => { const data = record.filter(y => { return y.id === x }) return { - id: x, account: data[0].account, domainid: data[0].domainid + id: x, + account: data[0].account, + domainid: data[0].domainid, + projectid: data[0].projectid } }) } @@ -954,6 +1107,7 @@ export default { icon: 'swap-outlined', docHelp: 'adminguide/virtual_machines.html#affinity-groups', permission: ['listAffinityGroups'], + searchFilters: ['name', 'zoneid', 'domainid', 'account', 'type'], columns: () => { var fields = ['name', 'type', 'description'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { @@ -967,7 +1121,7 @@ export default { } return fields }, - details: ['name', 'id', 'description', 'type', 'account', 'domain'], + details: ['name', 'id', 'description', 'type', 'account', 'domain', 'dedicatedresources'], related: [{ name: 'vm', title: 'label.instances', diff --git a/ui/src/config/section/config.js b/ui/src/config/section/config.js index 6fa33e3ed31..8f70104eaf1 100644 --- a/ui/src/config/section/config.js +++ b/ui/src/config/section/config.js @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +import { shallowRef, defineAsyncComponent } from 'vue' + export default { name: 'config', title: 'label.configuration', @@ -37,6 +39,7 @@ export default { icon: 'team-outlined', docHelp: 'adminguide/accounts.html#using-an-ldap-server-for-user-authentication', permission: ['listLdapConfigurations'], + searchFilters: ['domainid', 'hostname', 'port'], columns: ['hostname', 'port', 'domainid'], details: ['hostname', 'port', 'domainid'], actions: [ @@ -100,7 +103,25 @@ export default { label: 'label.edit', dataView: true, popup: true, - args: ['description', 'clientid', 'redirecturi', 'secretkey', 'enabled'] + args: ['description', 'clientid', 'redirecturi', 'secretkey'] + }, + { + api: 'updateOauthProvider', + icon: 'play-circle-outlined', + label: 'label.enable.provider', + message: 'message.confirm.enable.provider', + dataView: true, + defaultArgs: { enabled: true }, + show: (record) => { return record.enabled === false } + }, + { + api: 'updateOauthProvider', + icon: 'pause-circle-outlined', + label: 'label.disable.provider', + message: 'message.confirm.disable.provider', + dataView: true, + defaultArgs: { enabled: false }, + show: (record) => { return record.enabled === true } }, { api: 'deleteOauthProvider', @@ -112,12 +133,50 @@ export default { } ] }, + { + name: 'backuprepository', + title: 'label.backup.repository', + icon: 'inbox-outlined', + docHelp: 'adminguide/backup_and_recovery.html', + permission: ['listBackupRepositories'], + searchFilters: ['zoneid'], + columns: ['name', 'provider', 'type', 'address', 'zonename'], + details: ['name', 'type', 'address', 'provider', 'zonename'], + actions: [ + { + api: 'addBackupRepository', + icon: 'plus-outlined', + label: 'label.backup.repository.add', + listView: true, + args: [ + 'name', 'provider', 'address', 'type', 'mountopts', 'zoneid' + ], + mapping: { + type: { + options: ['nfs', 'cifs', 'ceph'] + }, + provider: { + value: (record) => { return 'nas' } + } + } + }, + { + api: 'deleteBackupRepository', + icon: 'delete-outlined', + label: 'label.backup.repository.remove', + message: 'message.action.delete.backup.repository', + dataView: true, + popup: true + } + ] + }, { name: 'hypervisorcapability', title: 'label.hypervisor.capabilities', icon: 'database-outlined', docHelp: 'adminguide/hosts.html?highlight=Hypervisor%20capabilities#hypervisor-capabilities', permission: ['listHypervisorCapabilities'], + searchFilters: ['hypervisor'], columns: ['hypervisor', 'hypervisorversion', 'maxguestslimit', 'maxhostspercluster'], details: ['hypervisor', 'hypervisorversion', 'maxguestslimit', 'maxdatavolumeslimit', 'maxhostspercluster', 'securitygroupenabled', 'storagemotionenabled'], actions: [ @@ -130,6 +189,56 @@ export default { } ] }, + { + name: 'guestoscategory', + title: 'label.guest.os.categories', + docHelp: 'adminguide/guest_os.html#guest-os-categories', + icon: 'group-outlined', + permission: ['listOsCategories', 'addOsCategory'], + columns: ['name', 'isfeatured', 'created', 'order'], + details: ['name', 'isfeatured', 'created'], + related: [{ + name: 'guestos', + title: 'label.guest.os', + param: 'oscategoryid' + }, + { + name: 'template', + title: 'label.templates', + param: 'oscategoryid' + }, + { + name: 'iso', + title: 'label.isos', + param: 'oscategoryid' + }], + actions: [ + { + api: 'addOsCategory', + icon: 'plus-outlined', + label: 'label.add.guest.os.category', + listView: true, + dataView: false, + args: ['name', 'isfeatured'] + }, + { + api: 'updateOsCategory', + icon: 'edit-outlined', + label: 'label.edit', + dataView: true, + popup: true, + args: ['name', 'isfeatured'] + }, + { + api: 'deleteOsCategory', + icon: 'delete-outlined', + label: 'label.action.delete.guest.os.category', + message: 'message.action.delete.guest.os.category', + dataView: true, + popup: true + } + ] + }, { name: 'guestos', title: 'label.guest.os', @@ -138,6 +247,7 @@ export default { permission: ['listOsTypes', 'listOsCategories'], columns: ['name', 'oscategoryname', 'isuserdefined'], details: ['name', 'oscategoryname', 'isuserdefined'], + searchFilters: ['oscategoryid'], related: [{ name: 'guestoshypervisormapping', title: 'label.guest.os.hypervisor.mappings', @@ -164,7 +274,14 @@ export default { label: 'label.edit', dataView: true, popup: true, - args: ['osdisplayname'] + groupAction: true, + groupMap: (selection, values) => { return selection.map(x => { return { id: x, oscategoryid: values.oscategoryid } }) }, + args: (record, store, isGroupAction) => { + if (isGroupAction) { + return ['oscategoryid'] + } + return ['osdisplayname', 'oscategoryid'] + } }, { api: 'addGuestOsMapping', @@ -225,6 +342,95 @@ export default { popup: true } ] + }, + { + name: 'gpucard', + title: 'label.gpu.card.types', + icon: 'laptop-outlined', + permission: ['listGpuCards'], + columns: ['name', 'deviceid', 'devicename', 'vendorid', 'vendorname'], + details: ['name', 'deviceid', 'devicename', 'vendorid', 'vendorname'], + related: [{ + name: 'gpudevices', + title: 'label.gpu.device', + param: 'gpucardid' + }, { + name: 'vgpuprofile', + title: 'label.vgpu.profile', + param: 'gpucardid' + }], + tabs: [{ + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, { + name: 'vgpu', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/VgpuProfilesTab.vue'))) + }], + actions: [ + { + api: 'createGpuCard', + icon: 'plus-outlined', + label: 'label.add.gpu.card', + listView: true, + dataView: false, + args: ['name', 'deviceid', 'devicename', 'vendorid', 'vendorname', 'videoram'] + }, + { + api: 'updateGpuCard', + icon: 'edit-outlined', + label: 'label.edit', + dataView: true, + popup: true, + args: ['name', 'devicename', 'vendorname'] + }, + { + api: 'deleteGpuCard', + icon: 'delete-outlined', + label: 'label.action.delete.gpu.card', + message: 'message.action.delete.gpu.card', + dataView: true, + popup: true, + groupAction: true, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + } + ] + }, + { + name: 'vgpuprofile', + title: 'label.vgpu.profile', + icon: 'laptop-outlined', + permission: ['listVgpuProfiles'], + hidden: true, + columns: ['name', 'gpucardname', 'description', 'videoram', 'maxheads', 'resolution', 'maxvgpuperphysicalgpu'], + details: ['gpucardname', 'name', 'description', 'videoram', 'maxheads', 'maxresolutionx', 'maxresolutiony', 'maxvgpuperphysicalgpu'], + actions: [ + { + api: 'createVgpuProfile', + icon: 'plus-outlined', + label: 'label.add.vgpu.profile', + listView: true, + dataView: false, + args: ['name', 'description', 'gpucardid', 'videoram', 'maxheads', 'maxresolutionx', 'maxresolutiony', 'maxvgpuperphysicalgpu'] + }, + { + api: 'updateVgpuProfile', + icon: 'edit-outlined', + label: 'label.edit', + dataView: true, + popup: true, + args: ['name', 'description', 'videoram', 'maxheads', 'maxresolutionx', 'maxresolutiony', 'maxvgpuperphysicalgpu'] + }, + { + api: 'deleteVgpuProfile', + icon: 'delete-outlined', + label: 'label.action.delete.vgpu.profile', + message: 'message.action.delete.vgpu.profile', + dataView: true, + popup: true, + groupAction: true, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + } + ] } ] } diff --git a/ui/src/config/section/domain.js b/ui/src/config/section/domain.js index a8648b10f76..fbe20ef8891 100644 --- a/ui/src/config/section/domain.js +++ b/ui/src/config/section/domain.js @@ -32,6 +32,27 @@ export default { name: 'account', title: 'label.accounts', param: 'domainid' + }, { + name: 'vm', + title: 'label.vms', + param: 'domainid' + }, { + name: 'volume', + title: 'label.volumes', + param: 'domainid' + }, { + name: 'guestnetwork', + title: 'label.networks', + param: 'domainid' + }, { + name: 'template', + title: 'label.templates', + param: 'domainid' + }, + { + name: 'iso', + title: 'label.isos', + param: 'domainid' }], tabs: [ { diff --git a/ui/src/config/section/extension.js b/ui/src/config/section/extension.js new file mode 100644 index 00000000000..4c6d9ebf076 --- /dev/null +++ b/ui/src/config/section/extension.js @@ -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. + +import { shallowRef, defineAsyncComponent } from 'vue' +import store from '@/store' + +export default { + name: 'extension', + title: 'label.extensions', + icon: 'appstore-add-outlined', + docHelp: 'adminguide/extensions.html', + permission: ['listExtensions'], + params: (dataView) => { + const params = {} + if (!dataView) { + params.details = 'min' + } + return params + }, + resourceType: 'Extension', + columns: () => { + var fields = ['name', 'state', 'type', 'path', + { + availability: (record) => { + if (record.pathready) { + return 'Ready' + } + return 'Not Ready' + } + }, 'created'] + return fields + }, + details: ['name', 'description', 'id', 'type', 'details', 'path', 'pathready', 'isuserdefined', 'orchestratorrequirespreparevm', 'created'], + filters: ['orchestrator'], + tabs: [{ + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'resources', + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/ExtensionResourcesTab.vue'))) + }, + { + name: 'customactions', + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/ExtensionCustomActionsTab.vue'))) + }, + { + name: 'events', + resourceType: 'Extension', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/EventsTab.vue'))), + show: () => { return 'listEvents' in store.getters.apis } + }], + related: [ + { + name: 'vm', + title: 'label.instances', + param: 'extensionid' + }, + { + name: 'template', + title: 'label.templates', + param: 'extensionid' + } + ], + actions: [ + { + api: 'createExtension', + icon: 'plus-outlined', + label: 'label.create.extension', + docHelp: 'adminguide/extensions.html', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/CreateExtension.vue'))) + }, + { + api: 'updateExtension', + icon: 'edit-outlined', + label: 'label.update.extension', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/UpdateExtension.vue'))) + }, + { + api: 'registerExtension', + icon: 'api-outlined', + label: 'label.register.extension', + message: 'message.action.register.extension', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/RegisterExtension.vue'))) + }, + { + api: 'updateExtension', + icon: 'play-circle-outlined', + label: 'label.enable.extension', + message: 'message.confirm.enable.extension', + dataView: true, + groupAction: true, + popup: true, + defaultArgs: { state: 'Enabled' }, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + show: (record) => { return ['Disabled'].includes(record.state) } + }, + { + api: 'updateExtension', + icon: 'pause-circle-outlined', + label: 'label.disable.extension', + message: 'message.confirm.disable.extension', + dataView: true, + groupAction: true, + popup: true, + defaultArgs: { state: 'Disabled' }, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + show: (record) => { return ['Enabled'].includes(record.state) } + }, + { + api: 'deleteExtension', + icon: 'delete-outlined', + label: 'label.delete.extension', + message: 'message.action.delete.extension', + dataView: true, + popup: true, + args: ['id', 'cleanup'], + mapping: { + id: { + value: (record, params) => { return record.id } + }, + cleanup: false + }, + show: (record) => { return record.isuserdefined } + } + ] +} diff --git a/ui/src/config/section/extension/customaction.js b/ui/src/config/section/extension/customaction.js new file mode 100644 index 00000000000..e01f9bc944b --- /dev/null +++ b/ui/src/config/section/extension/customaction.js @@ -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. + +import { shallowRef, defineAsyncComponent } from 'vue' +import store from '@/store' + +export default { + name: 'customaction', + title: 'label.custom.actions', + icon: 'play-square-outlined', + docHelp: 'adminguide/extensions.html#custom-actions', + permission: ['listCustomActions'], + resourceType: 'ExtensionCustomAction', + hidden: true, + columns: ['name', 'extensionname', 'enabled', 'created'], + details: ['name', 'id', 'description', 'extensionname', 'allowedroletypes', 'resourcetype', 'parameters', 'timeout', 'successmessage', 'errormessage', 'details', 'created'], + tabs: [{ + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'events', + resourceType: 'ExtensionCustomAction', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/EventsTab.vue'))), + show: () => { return 'listEvents' in store.getters.apis } + }], + actions: [ + { + api: 'addCustomAction', + icon: 'plus-outlined', + label: 'label.add.custom.action', + docHelp: 'adminguide/extensions.html#custom-actions', + listView: true, + popup: true, + show: (record) => { return false }, // Hidden for now + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/AddCustomAction.vue'))) + }, + { + api: 'updateCustomAction', + icon: 'edit-outlined', + label: 'label.update.custom.action', + message: 'message.action.update.extension', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/extension/UpdateCustomAction.vue'))) + }, + { + api: 'updateCustomAction', + icon: 'play-circle-outlined', + label: 'label.enable.custom.action', + message: 'message.confirm.enable.custom.action', + dataView: true, + groupAction: true, + popup: true, + defaultArgs: { enabled: true }, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + show: (record) => { return !record.enabled } + }, + { + api: 'updateCustomAction', + icon: 'pause-circle-outlined', + label: 'label.disable.custom.action', + message: 'message.confirm.disable.custom.action', + dataView: true, + groupAction: true, + popup: true, + defaultArgs: { enabled: false }, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + show: (record) => { return record.enabled } + }, + { + api: 'deleteCustomAction', + icon: 'delete-outlined', + label: 'label.delete.custom.action', + message: 'message.action.delete.custom.action', + dataView: true, + popup: true + } + ] +} diff --git a/ui/src/config/section/image.js b/ui/src/config/section/image.js index b01d657627a..f0458088b24 100644 --- a/ui/src/config/section/image.js +++ b/ui/src/config/section/image.js @@ -17,6 +17,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' +import { isZoneCreated } from '@/utils/zone' export default { name: 'image', @@ -42,7 +43,7 @@ export default { } return 'Not Ready' } - }, 'ostypename', 'hypervisor'] + }, 'ostypename', 'arch', 'hypervisor'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { fields.push('size') fields.push('account') @@ -57,16 +58,16 @@ export default { return fields }, details: () => { - var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'format', 'ostypename', 'size', 'physicalsize', 'isready', 'passwordenabled', + var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'arch', 'format', 'externalprovisioner', 'ostypename', 'size', 'physicalsize', 'isready', 'passwordenabled', 'crossZones', 'templatetype', 'directdownload', 'deployasis', 'ispublic', 'isfeatured', 'isextractable', 'isdynamicallyscalable', 'crosszones', 'type', - 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy'] + 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy', 'forcks'] if (['Admin'].includes(store.getters.userInfo.roletype)) { fields.push('templatetag', 'templatetype', 'url') } return fields }, searchFilters: () => { - var filters = ['name', 'zoneid', 'tags'] + var filters = ['name', 'zoneid', 'tags', 'arch', 'oscategoryid', 'templatetype', 'extensionid'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { filters.push('storageid') filters.push('imagestoreid') @@ -90,7 +91,7 @@ export default { }, { name: 'vnf.settings', component: shallowRef(defineAsyncComponent(() => import('@/views/image/TemplateVnfSettings.vue'))), - show: (record) => { return record.templatetype === 'VNF' } + show: (record) => { return record.templatetype === 'VNF' && 'updateVnfTemplate' in store.getters.apis } }, { name: 'events', @@ -110,16 +111,17 @@ export default { docHelp: 'adminguide/templates.html#uploading-templates-from-a-remote-http-server', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/image/RegisterOrUploadTemplate.vue'))) }, { api: 'registerTemplate', icon: 'cloud-upload-outlined', label: 'label.upload.template.from.local', - show: () => { return 'getUploadParamsForTemplate' in store.getters.apis }, docHelp: 'adminguide/templates.html#uploading-templates-and-isos-from-a-local-computer', listView: true, popup: true, + show: () => { return isZoneCreated() && 'getUploadParamsForTemplate' in store.getters.apis }, component: shallowRef(defineAsyncComponent(() => import('@/views/image/RegisterOrUploadTemplate.vue'))) }, { @@ -218,7 +220,7 @@ export default { } return 'Not Ready' } - }, 'ostypename'] + }, 'ostypename', 'arch'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { fields.push('size') fields.push('account') @@ -231,9 +233,9 @@ export default { } return fields }, - details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 'bootable', 'isready', 'directdownload', 'isextractable', 'ispublic', 'isfeatured', 'crosszones', 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy', 'url'], + details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 'arch', 'bootable', 'isready', 'passwordenabled', 'directdownload', 'isextractable', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'crosszones', 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy', 'url'], searchFilters: () => { - var filters = ['name', 'zoneid', 'tags'] + var filters = ['name', 'zoneid', 'tags', 'arch', 'oscategoryid'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { filters.push('storageid') filters.push('imagestoreid') @@ -270,13 +272,14 @@ export default { docHelp: 'adminguide/templates.html#id10', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/image/RegisterOrUploadIso.vue'))) }, { api: 'registerIso', icon: 'cloud-upload-outlined', label: 'label.upload.iso.from.local', - show: () => { return 'getUploadParamsForIso' in store.getters.apis }, + show: () => { return isZoneCreated() && 'getUploadParamsForIso' in store.getters.apis }, docHelp: 'adminguide/templates.html#id10', listView: true, popup: true, @@ -367,8 +370,21 @@ export default { icon: ['fa-solid', 'fa-dharmachakra'], docHelp: 'plugins/cloudstack-kubernetes-service.html#kubernetes-supported-versions', permission: ['listKubernetesSupportedVersions'], - columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'zonename'], - details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created'], + searchFilters: ['zoneid', 'minimumsemanticversion', 'arch'], + columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'arch', 'zonename'], + details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'arch', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created'], + tabs: [ + { + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'events', + resourceType: 'KubernetesSupportedVersion', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/EventsTab.vue'))), + show: () => { return 'listEvents' in store.getters.apis } + } + ], actions: [ { api: 'addKubernetesSupportedVersion', @@ -376,6 +392,7 @@ export default { label: 'label.kubernetes.version.add', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/image/AddKubernetesSupportedVersion.vue'))) }, { diff --git a/ui/src/config/section/infra.js b/ui/src/config/section/infra.js index 5b3b38a68e5..ae074002834 100644 --- a/ui/src/config/section/infra.js +++ b/ui/src/config/section/infra.js @@ -63,6 +63,27 @@ export default { permission: ['listHosts'], component: () => import('@/views/infra/CpuSockets.vue') }, + { + name: 'gpudevices', + title: 'label.gpu.devices', + icon: 'BoxPlotOutlined', + hidden: true, + permission: ['listGpuDevices'], + columns: ['busaddress', 'gpucardname', 'vgpuprofilename', 'hostname', 'virtualmachinename'], + details: ['id', 'busaddress', 'gpucardname', 'vgpuprofilename', 'hostname', 'virtualmachinename'], + searchFilters: ['gpucardid', 'vgpuprofileid'], + actions: [ + { + api: 'deleteGpuDevice', + icon: 'delete-outlined', + label: 'label.delete.gpu.device', + dataView: true, + popup: true, + groupAction: true, + groupMap: (selection) => { return selection.map(x => { return { id: x.id } }) } + } + ] + }, { name: 'metric', title: 'label.db.usage.metrics', @@ -79,7 +100,7 @@ export default { permission: ['listAlerts'], columns: ['name', 'description', 'type', 'sent'], details: ['name', 'id', 'type', 'sent', 'description'], - searchFilters: ['type'], + searchFilters: ['name', 'type'], actions: [ { api: 'archiveAlerts', diff --git a/ui/src/config/section/infra/clusters.js b/ui/src/config/section/infra/clusters.js index ab8bea6b79d..ad6c59dda42 100644 --- a/ui/src/config/section/infra/clusters.js +++ b/ui/src/config/section/infra/clusters.js @@ -24,9 +24,10 @@ export default { icon: 'cluster-outlined', docHelp: 'conceptsandterminology/concepts.html#about-clusters', permission: ['listClustersMetrics'], + searchFilters: ['name', 'zoneid', 'podid', 'arch', 'hypervisor'], columns: () => { - const fields = ['name', 'state', 'allocationstate', 'clustertype', 'hypervisortype', 'hosts'] - const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal', 'drsimbalance'] + const fields = ['name', 'allocationstate', 'clustertype', 'arch', 'hypervisortype'] + const metricsFields = ['state', 'hosts', 'cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal', 'drsimbalance'] if (store.getters.metrics) { fields.push(...metricsFields) } @@ -34,7 +35,7 @@ export default { fields.push('zonename') return fields }, - details: ['name', 'id', 'allocationstate', 'clustertype', 'managedstate', 'hypervisortype', 'podname', 'zonename', 'drsimbalance'], + details: ['name', 'id', 'allocationstate', 'clustertype', 'managedstate', 'arch', 'hypervisortype', 'externalprovisioner', 'podname', 'zonename', 'drsimbalance', 'storageaccessgroups', 'podstorageaccessgroups', 'zonestorageaccessgroups', 'externaldetails'], related: [{ name: 'host', title: 'label.hosts', @@ -56,7 +57,8 @@ export default { component: shallowRef(defineAsyncComponent(() => import('@/components/view/SettingsTab.vue'))) }, { name: 'drs', - component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ClusterDRSTab.vue'))) + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ClusterDRSTab.vue'))), + show: (resource) => { return resource.hypervisortype !== 'External' } }, { name: 'comments', component: shallowRef(defineAsyncComponent(() => import('@/components/view/AnnotationsTab.vue'))) @@ -72,7 +74,7 @@ export default { api: 'addCluster', icon: 'plus-outlined', label: 'label.add.cluster', - docHelp: 'adminguide/installguide/configuration.html#adding-a-cluster', + docHelp: 'installguide/configuration.html#adding-a-cluster', listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ClusterAdd.vue'))) @@ -82,14 +84,15 @@ export default { icon: 'edit-outlined', label: 'label.edit', dataView: true, - args: ['clustername'] + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ClusterUpdate.vue'))) }, { api: 'updateCluster', icon: 'play-circle-outlined', label: 'label.action.enable.cluster', message: 'message.action.enable.cluster', - docHelp: 'adminguide/installguide/hosts.html#disabling-and-enabling-zones-pods-and-clusters', + docHelp: 'adminguide/hosts.html#disabling-and-enabling-zones-pods-and-clusters', dataView: true, defaultArgs: { allocationstate: 'Enabled' }, show: (record) => { return record.allocationstate === 'Disabled' } @@ -99,7 +102,7 @@ export default { icon: 'pause-circle-outlined', label: 'label.action.disable.cluster', message: 'message.action.disable.cluster', - docHelp: 'adminguide/installguide/hosts.html#disabling-and-enabling-zones-pods-and-clusters', + docHelp: 'adminguide/hosts.html#disabling-and-enabling-zones-pods-and-clusters', dataView: true, defaultArgs: { allocationstate: 'Disabled' }, show: (record) => { return record.allocationstate === 'Enabled' } @@ -130,7 +133,7 @@ export default { dataView: true, defaultArgs: { iterations: null }, args: ['iterations'], - show: (record) => { return record.managedstate === 'Managed' } + show: (record) => { return record.hypervisortype !== 'External' && record.managedstate === 'Managed' } }, { api: 'enableOutOfBandManagementForCluster', @@ -139,7 +142,7 @@ export default { message: 'label.outofbandmanagement.enable', dataView: true, show: (record) => { - return record?.resourcedetails?.outOfBandManagementEnabled === 'false' + return record.hypervisortype !== 'External' && record?.resourcedetails?.outOfBandManagementEnabled === 'false' }, args: ['clusterid'], mapping: { @@ -155,7 +158,7 @@ export default { message: 'label.outofbandmanagement.disable', dataView: true, show: (record) => { - return !(record?.resourcedetails?.outOfBandManagementEnabled === 'false') + return record.hypervisortype !== 'External' && !(record?.resourcedetails?.outOfBandManagementEnabled === 'false') }, args: ['clusterid'], mapping: { @@ -171,7 +174,7 @@ export default { message: 'label.ha.enable', dataView: true, show: (record) => { - return record?.resourcedetails?.resourceHAEnabled === 'false' + return record.hypervisortype !== 'External' && record?.resourcedetails?.resourceHAEnabled === 'false' }, args: ['clusterid'], mapping: { @@ -187,7 +190,7 @@ export default { message: 'label.ha.disable', dataView: true, show: (record) => { - return !(record?.resourcedetails?.resourceHAEnabled === 'false') + return record.hypervisortype !== 'External' && !(record?.resourcedetails?.resourceHAEnabled === 'false') }, args: ['clusterid'], mapping: { @@ -207,6 +210,9 @@ export default { clusterids: { value: (record) => { return record.id } } + }, + show: (record) => { + return record.hypervisortype !== 'External' } }, { diff --git a/ui/src/config/section/infra/hosts.js b/ui/src/config/section/infra/hosts.js index d92f4af21e6..2f27db5780b 100644 --- a/ui/src/config/section/infra/hosts.js +++ b/ui/src/config/section/infra/hosts.js @@ -24,26 +24,35 @@ export default { icon: 'database-outlined', docHelp: 'conceptsandterminology/concepts.html#about-hosts', permission: ['listHostsMetrics'], + searchFilters: ['name', 'zoneid', 'podid', 'clusterid', 'arch', 'hypervisor'], resourceType: 'Host', filters: () => { - const filters = ['enabled', 'disabled', 'maintenance', 'up', 'down', 'alert'] + const filters = ['enabled', 'disabled', 'maintenance', 'up', 'down', 'disconnected', 'alert'] return filters }, params: { type: 'routing' }, columns: () => { - const fields = ['name', 'state', 'resourcestate', 'ipaddress', 'hypervisor', 'instances', 'powerstate'] - const metricsFields = ['cpunumber', 'cputotalghz', 'cpuusedghz', 'cpuallocatedghz', 'memorytotalgb', 'memoryusedgb', 'memoryallocatedgb', 'networkread', 'networkwrite'] + const fields = [ + 'name', 'state', 'resourcestate', 'ipaddress', 'arch', 'hypervisor', + { field: 'systeminstances', customTitle: 'system.vms' }, 'version' + ] + const metricsFields = ['instances', 'powerstate', 'cpunumber', 'cputotalghz', 'cpuusedghz', 'cpuallocatedghz', 'memorytotalgb', 'memoryusedgb', 'memoryallocatedgb', 'gputotal', 'gpuused', 'networkread', 'networkwrite'] if (store.getters.metrics) { fields.push(...metricsFields) } fields.push('clustername') fields.push('zonename') + fields.push('managementservername') return fields }, - details: ['name', 'id', 'resourcestate', 'ipaddress', 'hypervisor', 'type', 'clustername', 'podname', 'zonename', 'disconnected', 'created'], + details: ['name', 'id', 'resourcestate', 'ipaddress', 'hypervisor', 'externalprovisioner', 'arch', 'type', 'clustername', 'podname', 'zonename', 'storageaccessgroups', 'clusterstorageaccessgroups', 'podstorageaccessgroups', 'zonestorageaccessgroups', 'managementservername', 'disconnected', 'created', 'externaldetails'], tabs: [{ name: 'details', component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, { + name: 'gpu', + resourceType: 'Host', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/GPUTab.vue'))) }, { name: 'events', resourceType: 'Host', @@ -63,7 +72,7 @@ export default { api: 'addHost', icon: 'plus-outlined', label: 'label.add.host', - docHelp: 'adminguide/installguide/configuration.html#adding-a-host', + docHelp: 'installguide/configuration.html#adding-a-host', listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/infra/HostAdd.vue'))) @@ -73,12 +82,17 @@ export default { icon: 'edit-outlined', label: 'label.edit', dataView: true, - args: ['name', 'hosttags', 'istagarule', 'oscategoryid'], - mapping: { - oscategoryid: { - api: 'listOsCategories' - } - } + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/HostUpdate'))) + }, + { + api: 'updateHostPassword', + icon: 'key-outlined', + label: 'label.action.change.password', + dataView: true, + popup: true, + show: (record) => { return record.hypervisor !== 'External' }, + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ChangeHostPassword.vue'))) }, { api: 'provisionCertificate', @@ -110,9 +124,14 @@ export default { label: 'label.disable.host', message: 'message.confirm.disable.host', dataView: true, - show: (record) => { return record.resourcestate === 'Enabled' }, + show: (record) => record.resourcestate === 'Enabled', popup: true, - component: shallowRef(defineAsyncComponent(() => import('@/views/infra/HostEnableDisable'))) + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/HostEnableDisable'))), + events: { + 'refresh-data': () => { + store.dispatch('refreshCurrentPage') + } + } }, { api: 'updateHost', @@ -120,9 +139,14 @@ export default { label: 'label.enable.host', message: 'message.confirm.enable.host', dataView: true, - show: (record) => { return record.resourcestate === 'Disabled' }, + show: (record) => record.resourcestate === 'Disabled', popup: true, - component: shallowRef(defineAsyncComponent(() => import('@/views/infra/HostEnableDisable'))) + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/HostEnableDisable'))), + events: { + 'refresh-data': () => { + store.dispatch('refreshCurrentPage') + } + } }, { api: 'prepareHostForMaintenance', @@ -149,15 +173,9 @@ export default { message: 'label.outofbandmanagement.configure', docHelp: 'adminguide/hosts.html#out-of-band-management', dataView: true, - args: ['hostid', 'address', 'port', 'username', 'password', 'driver'], - mapping: { - hostid: { - value: (record) => { return record.id } - }, - driver: { - options: ['ipmitool', 'nestedcloudstack', 'redfish'] - } - } + popup: true, + show: (record) => { return record.hypervisor !== 'External' }, + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ConfigureHostOOBM'))) }, { api: 'enableOutOfBandManagementForHost', @@ -167,7 +185,7 @@ export default { docHelp: 'adminguide/hosts.html#out-of-band-management', dataView: true, show: (record) => { - return !(record?.outofbandmanagement?.enabled === true) + return record.hypervisor !== 'External' && !(record?.outofbandmanagement?.enabled === true) }, args: ['hostid'], mapping: { @@ -184,7 +202,7 @@ export default { docHelp: 'adminguide/hosts.html#out-of-band-management', dataView: true, show: (record) => { - return record?.outofbandmanagement?.enabled === true + return record.hypervisor !== 'External' && record?.outofbandmanagement?.enabled === true }, args: ['hostid'], mapping: { @@ -201,7 +219,7 @@ export default { docHelp: 'adminguide/hosts.html#out-of-band-management', dataView: true, show: (record) => { - return record?.outofbandmanagement?.enabled === true + return record.hypervisor !== 'External' && record?.outofbandmanagement?.enabled === true }, args: ['hostid', 'action'], mapping: { @@ -221,7 +239,7 @@ export default { docHelp: 'adminguide/hosts.html#out-of-band-management', dataView: true, show: (record) => { - return record?.outofbandmanagement?.enabled === true + return record.hypervisor !== 'External' && record?.outofbandmanagement?.enabled === true }, args: ['hostid', 'password'], mapping: { @@ -256,7 +274,7 @@ export default { docHelp: 'adminguide/reliability.html#ha-for-hosts', dataView: true, show: (record) => { - return !(record?.hostha?.haenable === true) + return record.hypervisor !== 'External' && !(record?.hostha?.haenable === true) }, args: ['hostid'], mapping: { @@ -285,7 +303,7 @@ export default { }, { api: 'startRollingMaintenance', - icon: 'setting-outlined', + icon: 'control-outlined', label: 'label.start.rolling.maintenance', message: 'label.start.rolling.maintenance', docHelp: 'adminguide/hosts.html#kvm-rolling-maintenance', diff --git a/ui/src/config/section/infra/managementServers.js b/ui/src/config/section/infra/managementServers.js index d1dfa6df70d..d2d11d5b25d 100644 --- a/ui/src/config/section/infra/managementServers.js +++ b/ui/src/config/section/infra/managementServers.js @@ -26,29 +26,63 @@ export default { permission: ['listManagementServersMetrics'], resourceType: 'ManagementServer', columns: () => { - const fields = ['name', 'state', 'version'] - const metricsFields = ['collectiontime', 'availableprocessors', 'cpuload', 'heapmemoryused', 'agentcount'] + const fields = ['name', 'state', 'ipaddress', 'version', 'osdistribution', 'pendingjobscount', 'agentscount'] + const metricsFields = ['collectiontime', 'availableprocessors', 'cpuload', 'heapmemoryused'] if (store.getters.metrics) { fields.push(...metricsFields) } return fields }, - details: ['collectiontime', 'usageislocal', 'dbislocal', 'lastserverstart', 'lastserverstop', 'lastboottime', 'version', 'loginfo', 'systemtotalcpucycles', 'systemloadaverages', 'systemcycleusage', 'systemmemorytotal', 'systemmemoryfree', 'systemmemoryvirtualsize', 'availableprocessors', 'javadistribution', 'javaversion', 'osdistribution', 'kernelversion', 'agentcount', 'sessions', 'heapmemoryused', 'heapmemorytotal', 'threadsblockedcount', 'threadsdeamoncount', 'threadsnewcount', 'threadsrunnablecount', 'threadsterminatedcount', 'threadstotalcount', 'threadswaitingcount'], + details: ['ipaddress', 'collectiontime', 'usageislocal', 'dbislocal', 'lastserverstart', 'lastserverstop', 'lastboottime', 'version', 'loginfo', 'systemtotalcpucycles', 'systemloadaverages', 'systemcycleusage', 'systemmemorytotal', 'systemmemoryfree', 'systemmemoryvirtualsize', 'availableprocessors', 'javadistribution', 'javaversion', 'osdistribution', 'kernelversion', 'pendingjobscount', 'agentscount', 'sessions', 'heapmemoryused', 'heapmemorytotal', 'threadsblockedcount', 'threadsdeamoncount', 'threadsnewcount', 'threadsrunnablecount', 'threadsterminatedcount', 'threadstotalcount', 'threadswaitingcount'], tabs: [ { name: 'details', component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) }, + { + name: 'management.server.peers', + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ManagementServerPeerTab.vue'))) + }, { name: 'pending.jobs', component: shallowRef(defineAsyncComponent(() => import('@/views/infra/AsyncJobsTab.vue'))) }, + { + name: 'connected.agents', + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ConnectedAgentsTab.vue'))) + }, { name: 'comments', component: shallowRef(defineAsyncComponent(() => import('@/components/view/AnnotationsTab.vue'))) } ], actions: [ + { + api: 'prepareForMaintenance', + icon: 'plus-square-outlined', + label: 'label.prepare.for.maintenance', + message: 'message.prepare.for.maintenance', + dataView: true, + popup: true, + confirmationText: 'MAINTENANCE', + show: (record, store) => { return record.state === 'Up' }, + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/Confirmation.vue'))) + }, + { + api: 'cancelMaintenance', + icon: 'minus-square-outlined', + label: 'label.cancel.maintenance', + message: 'message.cancel.maintenance', + dataView: true, + popup: true, + args: ['rebalance'], + show: (record, store) => { return ['PreparingForMaintenance', 'Maintenance'].includes(record.state) }, + mapping: { + managementserverid: { + value: (record, params) => { return record.id } + } + } + }, { api: 'prepareForShutdown', icon: 'exclamation-circle-outlined', @@ -68,7 +102,7 @@ export default { dataView: true, popup: true, confirmationText: 'SHUTDOWN', - show: (record, store) => { return ['Up', 'PreparingToShutDown', 'ReadyToShutDown'].includes(record.state) }, + show: (record, store) => { return ['Up', 'Maintenance', 'PreparingForShutDown', 'ReadyToShutDown'].includes(record.state) }, component: shallowRef(defineAsyncComponent(() => import('@/views/infra/Confirmation.vue'))) }, { @@ -76,10 +110,9 @@ export default { icon: 'close-circle-outlined', label: 'label.cancel.shutdown', message: 'message.cancel.shutdown', - docHelp: 'installguide/configuration.html#adding-a-zone', dataView: true, popup: true, - show: (record, store) => { return ['PreparingToShutDown', 'ReadyToShutDown', 'ShuttingDown'].includes(record.state) }, + show: (record, store) => { return ['PreparingForShutDown', 'ReadyToShutDown', 'ShuttingDown'].includes(record.state) }, mapping: { managementserverid: { value: (record, params) => { return record.id } diff --git a/ui/src/config/section/infra/objectStorages.js b/ui/src/config/section/infra/objectStorages.js index 821c1b2d948..f3469718efc 100644 --- a/ui/src/config/section/infra/objectStorages.js +++ b/ui/src/config/section/infra/objectStorages.js @@ -29,7 +29,7 @@ export default { return fields }, details: () => { - var fields = ['name', 'id', 'url', 'providername'] + var fields = ['name', 'id', 'url', 'providername', 'storagetotal', 'storageallocated', 'storageused'] return fields }, resourceType: 'ObjectStorage', @@ -59,7 +59,7 @@ export default { api: 'updateObjectStoragePool', icon: 'edit-outlined', label: 'label.action.update.object.storage', - args: ['name', 'url'], + args: ['name', 'url', 'size'], dataView: true }, { diff --git a/ui/src/config/section/infra/phynetworks.js b/ui/src/config/section/infra/phynetworks.js index 578a12516fa..0863eff6ec0 100644 --- a/ui/src/config/section/infra/phynetworks.js +++ b/ui/src/config/section/infra/phynetworks.js @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -import { shallowRef, defineAsyncComponent } from 'vue' +import { shallowRef, defineAsyncComponent, reactive } from 'vue' export default { name: 'physicalnetwork', title: 'label.physical.network', @@ -57,7 +57,7 @@ export default { args: ['name', 'zoneid', 'isolationmethods', 'vlan', 'tags', 'networkspeed', 'broadcastdomainrange'], mapping: { isolationmethods: { - options: ['VLAN', 'VXLAN', 'GRE', 'STT', 'BCF_SEGMENT', 'SSP', 'ODL', 'L3VPN', 'VCS'] + options: ['VLAN', 'VXLAN', 'GRE', 'STT', 'BCF_SEGMENT', 'SSP', 'ODL', 'L3VPN', 'VCS', 'NSX', 'NETRIS'] } } }, @@ -131,3 +131,6 @@ export default { } ] } +export const trafficTypeTab = reactive({ + index: 0 +}) diff --git a/ui/src/config/section/infra/pods.js b/ui/src/config/section/infra/pods.js index eff62e0a4a2..66d38c08896 100644 --- a/ui/src/config/section/infra/pods.js +++ b/ui/src/config/section/infra/pods.js @@ -24,8 +24,9 @@ export default { icon: 'appstore-outlined', docHelp: 'conceptsandterminology/concepts.html#about-pods', permission: ['listPods'], + searchFilters: ['name', 'zoneid'], columns: ['name', 'allocationstate', 'gateway', 'netmask', 'zonename'], - details: ['name', 'id', 'allocationstate', 'netmask', 'gateway', 'zonename'], + details: ['name', 'id', 'allocationstate', 'netmask', 'gateway', 'zonename', 'storageaccessgroups', 'zonestorageaccessgroups'], related: [{ name: 'cluster', title: 'label.clusters', @@ -70,7 +71,8 @@ export default { icon: 'edit-outlined', label: 'label.edit', dataView: true, - args: ['name', 'netmask', 'gateway'] + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/PodUpdate.vue'))) }, { api: 'updatePod', diff --git a/ui/src/config/section/infra/primaryStorages.js b/ui/src/config/section/infra/primaryStorages.js index f222edeaf70..f127a0853b9 100644 --- a/ui/src/config/section/infra/primaryStorages.js +++ b/ui/src/config/section/infra/primaryStorages.js @@ -24,6 +24,7 @@ export default { icon: 'hdd-outlined', docHelp: 'adminguide/storage.html#primary-storage', permission: ['listStoragePoolsMetrics'], + searchFilters: ['name', 'zoneid', 'podid', 'clusterid', 'ipaddress', 'path', 'scope'], columns: () => { const fields = ['name', 'state', 'ipaddress', 'scope', 'type', 'path'] const metricsFields = ['disksizeusedgb', 'disksizetotalgb', 'disksizeallocatedgb', 'disksizeunallocatedgb'] @@ -34,7 +35,7 @@ export default { fields.push('zonename') return fields }, - details: ['name', 'id', 'ipaddress', 'type', 'scope', 'tags', 'path', 'provider', 'hypervisor', 'overprovisionfactor', 'disksizetotal', 'disksizeallocated', 'disksizeused', 'clustername', 'podname', 'zonename', 'created'], + details: ['name', 'id', 'ipaddress', 'type', 'details', 'nfsmountopts', 'scope', 'tags', 'storageaccessgroups', 'path', 'provider', 'hypervisor', 'overprovisionfactor', 'disksizetotal', 'disksizeallocated', 'disksizeused', 'capacityiops', 'usediops', 'clustername', 'podname', 'zonename', 'created'], related: [{ name: 'volume', title: 'label.volumes', @@ -89,7 +90,8 @@ export default { icon: 'edit-outlined', label: 'label.edit', dataView: true, - args: ['name', 'tags', 'istagarule', 'capacitybytes', 'capacityiops'] + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/UpdatePrimaryStorage.vue'))) }, { api: 'updateStoragePool', @@ -133,6 +135,26 @@ export default { dataView: true, show: (record) => { return ['Maintenance', 'PrepareForMaintenance', 'ErrorInMaintenance'].includes(record.state) } }, + { + api: 'changeStoragePoolScope', + icon: 'swap-outlined', + label: 'label.action.change.primary.storage.scope', + dataView: true, + popup: true, + show: (record) => { + return (record.state === 'Disabled' && + (record.scope === 'CLUSTER' || + record.scope === 'ZONE') && + (record.hypervisor === 'KVM' || + record.hypervisor === 'VMware' || + record.hypervisor === 'HyperV' || + record.hypervisor === 'LXC' || + record.hypervisor === 'Any' || + record.hypervisor === 'Simulator') + ) + }, + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ChangeStoragePoolScope.vue'))) + }, { api: 'deleteStoragePool', icon: 'delete-outlined', diff --git a/ui/src/config/section/infra/routers.js b/ui/src/config/section/infra/routers.js index 9e45cb73246..e52bcd7a829 100644 --- a/ui/src/config/section/infra/routers.js +++ b/ui/src/config/section/infra/routers.js @@ -26,12 +26,12 @@ export default { permission: ['listRouters'], params: { projectid: '-1' }, columns: () => { - var columns = ['name', 'state', 'publicip', 'guestnetworkname', 'vpcname', 'redundantstate', 'softwareversion', 'hostname', 'account', 'zonename', 'requiresupgrade'] + var columns = ['name', 'state', 'publicip', { field: 'guestnetworkname', customTitle: 'network' }, 'redundantstate', 'softwareversion', 'hostname', 'arch', 'account', 'zonename', 'requiresupgrade'] columns.splice(6, 0, { field: 'version', customTitle: 'templateversion' }) return columns }, - searchFilters: ['name', 'zoneid', 'podid', 'clusterid'], - details: ['name', 'id', 'version', 'softwareversion', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'account', 'zonename', 'created', 'hostcontrolstate'], + searchFilters: ['name', 'zoneid', 'podid', 'clusterid', 'arch'], + details: ['name', 'id', 'version', 'softwareversion', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'arch', 'account', 'zonename', 'created', 'hostcontrolstate'], resourceType: 'VirtualRouter', filters: () => { const filters = ['starting', 'running', 'stopping', 'stopped', 'destroyed', 'expunging', 'migrating', 'error', 'unknown', 'shutdown'] diff --git a/ui/src/config/section/infra/secondaryStorages.js b/ui/src/config/section/infra/secondaryStorages.js index 774c233c446..3fc64c5c957 100644 --- a/ui/src/config/section/infra/secondaryStorages.js +++ b/ui/src/config/section/infra/secondaryStorages.js @@ -24,6 +24,7 @@ export default { icon: 'picture-outlined', docHelp: 'adminguide/storage.html#secondary-storage', permission: ['listImageStores'], + searchFilters: ['name', 'zoneid', 'provider'], columns: () => { var fields = ['name', 'url', 'protocol', 'scope', 'zonename'] if (store.getters.apis.listImageStores.params.filter(x => x.name === 'readonly').length > 0) { @@ -96,21 +97,10 @@ export default { }, { api: 'updateImageStore', - icon: 'stop-outlined', - label: 'label.action.image.store.read.only', - message: 'message.action.secondary.storage.read.only', + icon: 'edit-outlined', + label: 'label.edit', dataView: true, - defaultArgs: { readonly: true }, - show: (record) => { return record.readonly === false } - }, - { - api: 'updateImageStore', - icon: 'check-circle-outlined', - label: 'label.action.image.store.read.write', - message: 'message.action.secondary.storage.read.write', - dataView: true, - defaultArgs: { readonly: false }, - show: (record) => { return record.readonly === true } + args: ['name', 'readonly', 'capacitybytes'] }, { api: 'deleteImageStore', diff --git a/ui/src/config/section/infra/systemVms.js b/ui/src/config/section/infra/systemVms.js index e48b19cb5d4..6e135ccdd36 100644 --- a/ui/src/config/section/infra/systemVms.js +++ b/ui/src/config/section/infra/systemVms.js @@ -24,8 +24,9 @@ export default { icon: 'thunderbolt-outlined', docHelp: 'adminguide/systemvm.html', permission: ['listSystemVms'], - columns: ['name', 'state', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'hostname', 'zonename'], - details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable', 'hostcontrolstate'], + searchFilters: ['name', 'zoneid', 'podid', 'hostid', 'systemvmtype', 'storageid', 'arch'], + columns: ['name', 'state', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'version', 'hostname', 'arch', 'zonename'], + details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'arch', 'version', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable', 'hostcontrolstate'], resourceType: 'SystemVm', filters: () => { const filters = ['starting', 'running', 'stopping', 'stopped', 'destroyed', 'expunging', 'migrating', 'error', 'unknown', 'shutdown'] diff --git a/ui/src/config/section/infra/zones.js b/ui/src/config/section/infra/zones.js index b2768f77343..c957f885469 100644 --- a/ui/src/config/section/infra/zones.js +++ b/ui/src/config/section/infra/zones.js @@ -24,16 +24,17 @@ export default { icon: 'global-outlined', docHelp: 'conceptsandterminology/concepts.html#about-zones', permission: ['listZonesMetrics'], + searchFilters: ['name', 'domainid', 'tags'], columns: () => { - const fields = ['name', 'allocationstate', 'type', 'networktype', 'clusters'] - const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal'] + const fields = ['name', 'allocationstate', 'type', 'networktype'] + const metricsFields = ['clusters', 'cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal', 'gputotal', 'gpuused'] if (store.getters.metrics) { fields.push(...metricsFields) } fields.push('order') return fields }, - details: ['name', 'id', 'allocationstate', 'type', 'networktype', 'guestcidraddress', 'localstorageenabled', 'securitygroupsenabled', 'dns1', 'dns2', 'internaldns1', 'internaldns2'], + details: ['name', 'id', 'allocationstate', 'type', 'networktype', 'guestcidraddress', 'localstorageenabled', 'securitygroupsenabled', 'dns1', 'dns2', 'internaldns1', 'internaldns2', 'asnrange', 'storageaccessgroups'], related: [{ name: 'pod', title: 'label.pods', @@ -71,6 +72,18 @@ export default { }, { name: 'physical.network', component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/PhysicalNetworksTab.vue'))) + }, { + name: 'ipv4.subnets', + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/Ipv4GuestSubnetsTab.vue'))), + show: (record) => { return record.routedmodeenabled && 'listIpv4SubnetsForZone' in store.getters.apis } + }, { + name: 'asnumber', + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/AsNumbersTab.vue'))), + show: (record) => { return record.routedmodeenabled && 'listASNumbers' in store.getters.apis } + }, { + name: 'bgp.peers', + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/BgpPeersTab.vue'))), + show: (record) => { return record.routedmodeenabled && 'listBgpPeers' in store.getters.apis } }, { name: 'system.vms', component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/SystemVmsTab.vue'))), @@ -105,8 +118,8 @@ export default { icon: 'edit-outlined', label: 'label.action.edit.zone', dataView: true, - args: ['name', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'internaldns1', 'internaldns2', 'guestcidraddress', 'domain', 'localstorageenabled'], - show: (record) => { return record.networktype === 'Advanced' } + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ZoneUpdate.vue'))) }, { api: 'updateZone', diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 3d5241feb59..8c1ffcdf9cd 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -19,6 +19,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' import tungsten from '@/assets/icons/tungsten.svg?inline' import { isAdmin } from '@/role' +import { isZoneCreated } from '@/utils/zone' export default { name: 'network', @@ -47,14 +48,14 @@ export default { return fields }, details: () => { - var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu'] + var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'asnumber', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip4routing', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu'] if (!isAdmin()) { fields = fields.filter(function (e) { return e !== 'broadcasturi' }) } return fields }, filters: ['all', 'account', 'domainpath', 'shared'], - searchFilters: ['keyword', 'zoneid', 'domainid', 'account', 'type', 'tags'], + searchFilters: ['keyword', 'zoneid', 'domainid', 'account', 'type', 'restartrequired', 'displaynetwork', 'tags'], related: [{ name: 'vm', title: 'label.instances', @@ -67,6 +68,14 @@ export default { name: 'egress.rules', component: shallowRef(defineAsyncComponent(() => import('@/views/network/EgressRulesTab.vue'))), show: (record, route, user) => { return record.type === 'Isolated' && !('vpcname' in record) && 'listEgressFirewallRules' in store.getters.apis && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid) } + }, { + name: 'bgp.peers', + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/BgpPeersTab.vue'))), + show: (record, route, user) => { return !record.vpcid && ['Admin'].includes(user.roletype) && record.ip4routing === 'Dynamic' } + }, { + name: 'routing.firewall', + component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutingFirewallRulesTab.vue'))), + show: (record, route, user) => { return record.type === 'Isolated' && record.ip4routing && !('vpcname' in record) && 'listRoutingFirewallRules' in store.getters.apis && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid) } }, { name: 'ip.v6.firewall', component: shallowRef(defineAsyncComponent(() => import('@/views/network/Ipv6FirewallRulesTab.vue'))), @@ -74,7 +83,7 @@ export default { }, { name: (record) => { return record.type === 'Shared' ? 'ip.addresses' : 'public.ip.addresses' }, component: shallowRef(defineAsyncComponent(() => import('@/views/network/IpAddressesTab.vue'))), - show: (record, route, user) => { return 'listPublicIpAddresses' in store.getters.apis && (record.type === 'Shared' || (record.type === 'Isolated' && !('vpcname' in record) && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid))) } + show: (record, route, user) => { return 'listPublicIpAddresses' in store.getters.apis && (record.type === 'Shared' || (record.type === 'Isolated' && !record.ip4routing && !('vpcname' in record) && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid))) } }, { name: 'virtual.routers', component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutersTab.vue'))), @@ -82,7 +91,7 @@ export default { }, { name: 'vnf.appliances', component: shallowRef(defineAsyncComponent(() => import('@/views/network/VnfAppliancesTab.vue'))), - show: () => { return 'deployVnfAppliance' in store.getters.apis } + show: () => { return 'listVnfAppliances' in store.getters.apis } }, { name: 'guest.ip.range', component: shallowRef(defineAsyncComponent(() => import('@/views/network/GuestIpRanges.vue'))), @@ -123,7 +132,7 @@ export default { listView: true, popup: true, show: () => { - if (!store.getters.zones || store.getters.zones.length === 0) { + if (!isZoneCreated()) { return false } const AdvancedZones = store.getters.zones.filter(zone => zone.networktype === 'Advanced') @@ -140,7 +149,10 @@ export default { icon: 'edit-outlined', label: 'label.update.network', dataView: true, - disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) }, + disabled: (record, user) => { + return (!record.projectid && (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype))) || + (record.type === 'Shared' && record.specifyvlan && !['Admin'].includes(user.userInfo.roletype)) + }, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateNetwork.vue'))) }, @@ -150,7 +162,10 @@ export default { label: 'label.restart.network', message: 'message.restart.network', dataView: true, - disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) }, + disabled: (record, user) => { + return (!record.projectid && (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype))) || + (record.type === 'Shared' && record.specifyvlan && !['Admin'].includes(user.userInfo.roletype)) + }, args: (record, store, isGroupAction) => { var fields = [] if (isGroupAction || record.vpcid == null) { @@ -167,7 +182,7 @@ export default { { api: 'replaceNetworkACLList', icon: 'swap-outlined', - label: 'label.replace.acl.list', + label: 'label.replace.acl', message: 'message.confirm.replace.acl.new.one', docHelp: 'adminguide/networking_and_traffic.html#configuring-network-access-control-list', dataView: true, @@ -189,7 +204,10 @@ export default { label: 'label.action.delete.network', message: 'message.action.delete.network', dataView: true, - disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) }, + disabled: (record, user) => { + return (!record.projectid && (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype))) || + (record.type === 'Shared' && record.specifyvlan && !['Admin'].includes(user.userInfo.roletype)) + }, groupAction: true, popup: true, groupMap: (selection) => { return selection.map(x => { return { id: x } }) } @@ -211,8 +229,8 @@ export default { fields.push(...['domain', 'zonename']) return fields }, - details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'], - searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'], + details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip4routing', 'ip4routes', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'], + searchFilters: ['name', 'zoneid', 'domainid', 'account', 'restartrequired', 'tags'], related: [{ name: 'vm', title: 'label.instances', @@ -236,6 +254,7 @@ export default { icon: 'plus-outlined', label: 'label.add.vpc', docHelp: 'adminguide/networking_and_traffic.html#adding-a-virtual-private-cloud', + show: isZoneCreated, listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/network/CreateVpc.vue'))) @@ -297,14 +316,11 @@ export default { component: shallowRef(defineAsyncComponent(() => import('@/views/network/IngressEgressRuleConfigure.vue'))) }], show: () => { - if (!store.getters.zones || store.getters.zones.length === 0) { + if (!isZoneCreated()) { return false } const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true) - if (!listZoneHaveSGEnabled || listZoneHaveSGEnabled.length === 0) { - return false - } - return true + return (listZoneHaveSGEnabled && listZoneHaveSGEnabled.length > 0) || store.getters.showSecurityGroups }, actions: [ { @@ -337,7 +353,7 @@ export default { name: 'vnfapp', title: 'label.vnf.appliances', icon: 'gateway-outlined', - permission: ['listVnfTemplates'], + permission: ['listVnfAppliances'], resourceType: 'UserVm', params: () => { return { details: 'servoff,tmpl,nics', isvnf: true } @@ -385,6 +401,7 @@ export default { label: 'label.vnf.appliance.add', docHelp: 'adminguide/networking/vnf_templates_appliances.html#deploying-vnf-appliances', listView: true, + show: isZoneCreated, component: () => import('@/views/compute/DeployVnfAppliance.vue') }, { @@ -681,7 +698,7 @@ export default { { api: 'resetUserDataForVirtualMachine', icon: 'solution-outlined', - label: 'label.reset.userdata.on.vm', + label: 'label.reset.user.data.on.vm', message: 'message.desc.reset.userdata', docHelp: 'adminguide/virtual_machines.html#resetting-userdata', dataView: true, @@ -748,6 +765,7 @@ export default { icon: 'environment-outlined', docHelp: 'adminguide/networking_and_traffic.html#reserving-public-ip-addresses-and-vlans-for-accounts', permission: ['listPublicIpAddresses'], + searchFilters: ['ipaddress', 'zoneid', 'account', 'domainid', 'vlanid', 'tags'], resourceType: 'PublicIpAddress', columns: () => { var fields = ['ipaddress', 'state', 'associatednetworkname', 'vpcname', 'virtualmachinename', 'allocated', 'account'] @@ -757,7 +775,7 @@ export default { fields.push(...['domain', 'zonename']) return fields }, - details: ['ipaddress', 'id', 'associatednetworkname', 'virtualmachinename', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'domain', 'zonename'], + details: ['ipaddress', 'id', 'associatednetworkname', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'domain', 'zonename'], filters: ['allocated', 'reserved', 'free'], component: shallowRef(() => import('@/views/network/PublicIpResource.vue')), tabs: [{ @@ -782,7 +800,7 @@ export default { }, { name: 'vpn', component: shallowRef(defineAsyncComponent(() => import('@/views/network/VpnDetails.vue'))), - show: (record) => { return record.issourcenat } + show: (record) => { return record.issourcenat || record.virtualmachinetype === 'DomainRouter' || !record.hasrules } }, { name: 'events', @@ -827,10 +845,13 @@ export default { message: 'message.action.release.ip', docHelp: 'adminguide/networking_and_traffic.html#releasing-an-ip-address-alloted-to-a-vpc', dataView: true, - show: (record) => { return record.state === 'Allocated' && !record.issourcenat }, + show: (record) => { return record.state === 'Allocated' && !record.issourcenat && !record.issystem }, groupAction: true, popup: true, - groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + groupShow: (selectedIps) => { + return selectedIps.every((ip) => ip.state === 'Allocated' && !ip.issourcenat && !ip.issystem) + } }, { api: 'reserveIpAddress', @@ -850,7 +871,58 @@ export default { show: (record) => { return record.state === 'Reserved' }, groupAction: true, popup: true, - groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + groupShow: (selectedIps) => { + return selectedIps.every((ip) => ip.state === 'Reserved') + } + } + ] + }, + { + name: 'asnumbers', + title: 'label.asnumbers', + icon: 'partition-outlined', + permission: ['listASNumbers'], + show: () => { + if (!store.getters.zones || store.getters.zones.length === 0) { + return false + } + const AdvancedZonesWithRoutedmode = store.getters.zones.filter(zone => zone.routedmodeenabled) + if (isAdmin() && (AdvancedZonesWithRoutedmode && AdvancedZonesWithRoutedmode.length > 0)) { + return true + } + return false + }, + filters: ['all', 'allocatedonly', 'free'], + columns: ['asnumber', 'allocationstate', 'asnrange', 'associatednetworkname', 'vpcname', 'allocated', 'account', 'domain', 'zonename'], + searchFilters: ['zoneid', 'associatednetworkid', 'account', 'domainid'], + resourceType: 'ASNumber', + actions: [ + { + api: 'releaseASNumber', + icon: 'delete-outlined', + label: 'label.action.release.asnumber', + message: 'message.action.release.asnumber', + show: (record) => { return record.allocationstate === 'Allocated' }, + args: ['zoneid', 'asnumber'], + mapping: { + zoneid: { + value: (record) => { return record.zoneid } + }, + asnumber: { + value: (record) => { return record.asnumber } + } + }, + dataView: true, + groupAction: true, + popup: true, + groupShow: (selectedItems, storegetters) => { + return selectedItems.length === 1 && selectedItems[0].allocationstate === 'Allocated' + }, + groupMap: (selectedId, values, records) => { + const record = records.filter(x => { return x.id === selectedId[0] }) + return record + } } ] }, @@ -893,7 +965,7 @@ export default { { api: 'replaceNetworkACLList', icon: 'swap-outlined', - label: 'label.replace.acl.list', + label: 'label.replace.acl', message: 'message.confirm.replace.acl.new.one', docHelp: 'adminguide/networking_and_traffic.html#acl-on-private-gateway', dataView: true, @@ -921,7 +993,6 @@ export default { name: 's2svpn', title: 'label.site.to.site.vpn', icon: 'lock-outlined', - hidden: true, permission: ['listVpnGateways'], columns: ['publicip', 'account', 'domain'], details: ['publicip', 'account', 'domain'], @@ -932,6 +1003,7 @@ export default { label: 'label.add.vpn.gateway', docHelp: 'adminguide/networking_and_traffic.html#creating-a-vpn-gateway-for-the-vpc', listView: true, + show: isZoneCreated, args: ['vpcid'] }, { @@ -949,7 +1021,6 @@ export default { title: 'label.site.to.site.vpn.connections', docHelp: 'adminguide/networking_and_traffic.html#setting-up-a-site-to-site-vpn-connection', icon: 'sync-outlined', - hidden: true, permission: ['listVpnConnections'], columns: ['publicip', 'state', 'gateway', 'ipsecpsk', 'ikepolicy', 'esppolicy'], details: ['publicip', 'gateway', 'passive', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'esppolicy', 'ikelifetime', 'ikeversion', 'esplifetime', 'dpd', 'splitconnections', 'forceencap', 'created'], @@ -990,10 +1061,9 @@ export default { }, { name: 'acllist', - title: 'label.network.acl.lists', + title: 'label.network.acls', icon: 'bars-outlined', docHelp: 'adminguide/networking_and_traffic.html#configuring-network-access-control-list', - hidden: true, permission: ['listNetworkACLLists'], columns: ['name', 'description', 'id'], details: ['name', 'description', 'id'], @@ -1001,15 +1071,15 @@ export default { name: 'details', component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) }, { - name: 'acl.list.rules', - component: shallowRef(defineAsyncComponent(() => import('@/views/network/AclListRulesTab.vue'))), + name: 'acl.rules', + component: shallowRef(defineAsyncComponent(() => import('@/views/network/AclRulesTab.vue'))), show: () => true }], actions: [ { api: 'createNetworkACLList', icon: 'plus-outlined', - label: 'label.add.acl.list', + label: 'label.add.acl', docHelp: 'adminguide/networking_and_traffic.html#creating-acl-lists', listView: true, args: ['name', 'description', 'vpcid'] @@ -1017,15 +1087,15 @@ export default { { api: 'updateNetworkACLList', icon: 'edit-outlined', - label: 'label.edit.acl.list', + label: 'label.edit.acl', dataView: true, args: ['name', 'description'] }, { api: 'deleteNetworkACLList', icon: 'delete-outlined', - label: 'label.delete.acl.list', - message: 'message.confirm.delete.acl.list', + label: 'label.delete.acl', + message: 'message.confirm.delete.acl', dataView: true } ] @@ -1107,6 +1177,7 @@ export default { icon: 'plus-outlined', label: 'label.add.vpn.user', listView: true, + show: isZoneCreated, args: (record, store) => { if (store.userInfo.roletype === 'User') { return ['username', 'password'] @@ -1186,6 +1257,7 @@ export default { docHelp: 'adminguide/networking_and_traffic.html#creating-and-updating-a-vpn-customer-gateway', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/network/CreateVpnCustomerGateway.vue'))) }, { @@ -1364,7 +1436,7 @@ export default { permission: ['listGuestVlans'], resourceType: 'GuestVlan', filters: ['allocatedonly', 'all'], - columns: ['vlan', 'allocationstate', 'physicalnetworkname', 'taken', 'account', 'project', 'domain', 'zonename'], + columns: ['vlan', 'allocationstate', 'physicalnetworkname', 'taken', 'account', 'project', 'domain', 'zonename', 'guest.networks'], details: ['vlan', 'allocationstate', 'physicalnetworkname', 'taken', 'account', 'project', 'domain', 'isdedicated', 'zonename'], searchFilters: ['zoneid'], tabs: [{ @@ -1375,12 +1447,47 @@ export default { component: shallowRef(defineAsyncComponent(() => import('@/views/network/GuestVlanNetworksTab.vue'))), show: (record) => { return (record.allocationstate === 'Allocated') } }], + show: isZoneCreated + }, + { + name: 'ipv4subnets', + title: 'label.ipv4.subnets', + icon: 'pic-center-outlined', + permission: ['listIpv4SubnetsForGuestNetwork'], + columns: ['subnet', 'zonename', 'parentsubnet', 'networkname', 'vpcname', 'created', 'allocated'], + details: ['subnet', 'zonename', 'zoneid', 'parentsubnet', 'networkname', 'networkid', 'vpcname', 'vpcid', 'created', 'allocated', 'state'], + searchFilters: ['zoneid'], show: () => { if (!store.getters.zones || store.getters.zones.length === 0) { return false } - return true - } + const AdvancedZonesWithRoutedmode = store.getters.zones.filter(zone => zone.routedmodeenabled) + if (isAdmin() && (AdvancedZonesWithRoutedmode && AdvancedZonesWithRoutedmode.length > 0)) { + return true + } + return false + }, + actions: [ + { + api: 'createIpv4SubnetForGuestNetwork', + icon: 'plus-outlined', + label: 'label.add.ipv4.subnet', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/CreateIpv4SubnetForNetwork.vue'))) + }, + { + api: 'deleteIpv4SubnetForGuestNetwork', + icon: 'delete-outlined', + label: 'label.delete.ipv4.subnet', + message: 'message.action.delete.ipv4.subnet', + dataView: true, + show: (record) => { return !record.networkid }, + groupAction: true, + popup: true, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + } + ] } ] } diff --git a/ui/src/config/section/offering.js b/ui/src/config/section/offering.js index 9250842ff4e..7bdd992039c 100644 --- a/ui/src/config/section/offering.js +++ b/ui/src/config/section/offering.js @@ -16,6 +16,7 @@ // under the License. import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' +import { getFilteredExternalDetails } from '@/utils/extension' export default { name: 'offering', @@ -29,6 +30,7 @@ export default { docHelp: 'adminguide/service_offerings.html#compute-and-disk-service-offerings', icon: 'cloud-outlined', permission: ['listServiceOfferings'], + searchFilters: ['name', 'gpuenabled', 'zoneid', 'domainid', 'cpunumber', 'cpuspeed', 'memory'], params: () => { var params = {} if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { @@ -37,9 +39,9 @@ export default { return params }, filters: ['active', 'inactive'], - columns: ['name', 'displaytext', 'state', 'cpunumber', 'cpuspeed', 'memory', 'domain', 'zone', 'order'], + columns: ['name', 'displaytext', 'state', 'cpunumber', 'cpuspeed', 'memory', 'gpu', 'domain', 'zone', 'order'], details: () => { - var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness', 'encryptroot'] + var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'storageaccessgroups', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness', 'encryptroot', 'purgeresources', 'leaseduration', 'gpucardid', 'gpucardname', 'vgpuprofileid', 'vgpuprofilename', 'gpucount', 'gpudisplay', 'leaseexpiryaction', 'externaldetails'] if (store.getters.apis.createServiceOffering && store.getters.apis.createServiceOffering.params.filter(x => x.name === 'storagepolicy').length > 0) { fields.splice(6, 0, 'vspherestoragepolicy') @@ -94,7 +96,12 @@ export default { label: 'label.edit', docHelp: 'adminguide/service_offerings.html#modifying-or-deleting-a-service-offering', dataView: true, - args: ['name', 'displaytext', 'storagetags', 'hosttags'] + args: ['name', 'displaytext', 'storageatags', 'hosttags', 'externaldetails'], + mapping: { + externaldetails: { + transformedvalue: (record) => { return getFilteredExternalDetails(record.serviceofferingdetails) } + } + } }, { api: 'updateServiceOffering', icon: 'lock-outlined', @@ -139,8 +146,9 @@ export default { icon: 'setting-outlined', docHelp: 'adminguide/service_offerings.html#system-service-offerings', permission: ['listServiceOfferings', 'listInfrastructure'], + searchFilters: ['name', 'zoneid', 'domainid', 'cpunumber', 'cpuspeed', 'memory'], params: { issystem: 'true', isrecursive: 'true' }, - columns: ['name', 'state', 'systemvmtype', 'cpunumber', 'cpuspeed', 'memory', 'storagetype', 'order'], + columns: ['name', 'state', 'systemvmtype', 'cpunumber', 'cpuspeed', 'memory', 'storagetype', 'offerha', 'order'], filters: ['active', 'inactive'], details: ['name', 'id', 'displaytext', 'systemvmtype', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'storagetags', 'hosttags', 'tags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness'], resourceType: 'ServiceOffering', @@ -215,6 +223,7 @@ export default { icon: 'hdd-outlined', docHelp: 'adminguide/service_offerings.html#compute-and-disk-service-offerings', permission: ['listDiskOfferings'], + searchFilters: ['name', 'zoneid', 'domainid', 'storageid'], params: () => { var params = {} if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { @@ -225,7 +234,9 @@ export default { columns: ['name', 'displaytext', 'state', 'disksize', 'domain', 'zone', 'order'], filters: ['active', 'inactive'], details: () => { - var fields = ['name', 'id', 'displaytext', 'disksize', 'provisioningtype', 'storagetype', 'iscustomized', 'disksizestrictness', 'iscustomizediops', 'diskIopsReadRate', 'diskIopsWriteRate', 'diskBytesReadRate', 'diskBytesWriteRate', 'miniops', 'maxiops', 'tags', 'domain', 'zone', 'created', 'encrypt'] + var fields = ['name', 'id', 'displaytext', 'disksize', 'provisioningtype', 'storagetype', 'iscustomized', 'disksizestrictness', 'iscustomizediops', + 'diskIopsReadRate', 'diskIopsWriteRate', 'diskBytesReadRate', 'diskBytesReadRateMax', 'diskBytesWriteRate', 'diskBytesWriteRateMax', 'miniops', 'maxiops', 'tags', + 'domain', 'zone', 'created', 'encrypt'] if (store.getters.apis.createDiskOffering && store.getters.apis.createDiskOffering.params.filter(x => x.name === 'storagepolicy').length > 0) { fields.splice(6, 0, 'vspherestoragepolicy') @@ -314,6 +325,7 @@ export default { icon: 'cloud-upload-outlined', docHelp: 'adminguide/virtual_machines.html#backup-offerings', permission: ['listBackupOfferings'], + searchFilters: ['zoneid'], columns: ['name', 'description', 'zonename'], details: ['name', 'id', 'description', 'externalid', 'zone', 'allowuserdrivenbackups', 'created'], related: [{ @@ -367,9 +379,22 @@ export default { icon: 'wifi-outlined', docHelp: 'adminguide/networking.html#network-offerings', permission: ['listNetworkOfferings'], + filters: ['all', 'forvpc', 'guestnetwork'], + searchFilters: ['name', 'zoneid', 'domainid', 'guestiptype', 'tags'], columns: ['name', 'state', 'guestiptype', 'traffictype', 'networkrate', 'domain', 'zone', 'order'], - details: ['name', 'id', 'displaytext', 'guestiptype', 'traffictype', 'internetprotocol', 'networkrate', 'ispersistent', 'egressdefaultpolicy', 'availability', 'conservemode', 'specifyvlan', 'specifyipranges', 'supportspublicaccess', 'supportsstrechedl2subnet', 'forvpc', 'fornsx', 'nsxmode', 'service', 'tags', 'domain', 'zone'], + details: ['name', 'id', 'displaytext', 'guestiptype', 'traffictype', 'internetprotocol', 'networkrate', 'ispersistent', 'egressdefaultpolicy', 'availability', 'conservemode', 'specifyvlan', 'routingmode', 'specifyasnumber', 'specifyipranges', 'supportspublicaccess', 'supportsstrechedl2subnet', 'forvpc', 'fornsx', 'networkmode', 'service', 'tags', 'domain', 'zone'], resourceType: 'NetworkOffering', + customParamHandler: (params, query) => { + const { filter } = query + if (!filter) { + return params + } + params.forvpc = filter === 'forvpc' + if (filter === 'all') { + delete params.forvpc + } + return params + }, tabs: [ { name: 'details', @@ -465,9 +490,10 @@ export default { icon: 'deployment-unit-outlined', docHelp: 'plugins/nuage-plugin.html?#vpc-offerings', permission: ['listVPCOfferings'], + searchFilters: ['name', 'zoneid', 'domainid'], resourceType: 'VpcOffering', columns: ['name', 'state', 'displaytext', 'domain', 'zone', 'order'], - details: ['name', 'id', 'displaytext', 'internetprotocol', 'distributedvpcrouter', 'tags', 'service', 'fornsx', 'nsxmode', 'domain', 'zone', 'created'], + details: ['name', 'id', 'displaytext', 'internetprotocol', 'distributedvpcrouter', 'tags', 'routingmode', 'specifyasnumber', 'service', 'fornsx', 'networkmode', 'domain', 'zone', 'created'], related: [{ name: 'vpc', title: 'label.vpc', diff --git a/ui/src/config/section/plugin/quota.js b/ui/src/config/section/plugin/quota.js index ffa1ae86e65..630e42e4c04 100644 --- a/ui/src/config/section/plugin/quota.js +++ b/ui/src/config/section/plugin/quota.js @@ -16,6 +16,8 @@ // under the License. import { shallowRef, defineAsyncComponent } from 'vue' +import { i18n } from '@/locales' + export default { name: 'quota', title: 'label.quota', @@ -78,9 +80,102 @@ export default { icon: 'credit-card-outlined', docHelp: 'plugins/quota.html#quota-tariff', permission: ['quotaTariffList'], - columns: ['usageName', 'usageTypeDescription', 'usageUnit', 'tariffValue', 'tariffActions'], - details: ['usageName', 'usageTypeDescription', 'usageUnit', 'tariffValue'], - component: shallowRef(() => import('@/views/plugins/quota/QuotaTariff.vue')) + customParamHandler: (params, query) => { + params.listall = false + + if (['all', 'removed'].includes(query.filter) || params.id) { + params.listall = true + } + + if (['removed'].includes(query.filter)) { + params.listonlyremoved = true + } + + return params + }, + columns: [ + 'name', + { + field: 'usageName', + customTitle: 'usageType', + usageName: (record) => i18n.global.t(record.usageName) + }, + { + field: 'usageUnit', + customTitle: 'usageUnit', + usageUnit: (record) => i18n.global.t(record.usageUnit) + }, + { + field: 'tariffValue', + customTitle: 'quota.tariff.value' + }, + { + field: 'executionPosition', + customTitle: 'quota.tariff.position', + executionPosition: (record) => record.position + }, + { + field: 'effectiveDate', + customTitle: 'start.date' + }, + { + field: 'endDate', + customTitle: 'end.date' + }, + 'removed' + ], + details: [ + 'uuid', + 'name', + 'description', + { + field: 'usageName', + customTitle: 'usageType' + }, + 'usageUnit', + { + field: 'tariffValue', + customTitle: 'quota.tariff.value' + }, + { + field: 'effectiveDate', + customTitle: 'start.date' + }, + { + field: 'endDate', + customTitle: 'end.date' + }, + 'removed' + ], + filters: ['all', 'active', 'removed'], + searchFilters: ['usagetype'], + actions: [ + { + api: 'quotaTariffCreate', + icon: 'plus-outlined', + label: 'label.action.quota.tariff.create', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/plugins/quota/CreateQuotaTariff.vue'))) + }, + { + api: 'quotaTariffUpdate', + icon: 'edit-outlined', + label: 'label.action.quota.tariff.edit', + dataView: true, + popup: true, + show: (record) => !record.removed, + component: shallowRef(defineAsyncComponent(() => import('@/views/plugins/quota/EditQuotaTariff.vue'))) + }, + { + api: 'quotaTariffDelete', + icon: 'delete-outlined', + label: 'label.action.quota.tariff.remove', + message: 'message.action.quota.tariff.remove', + dataView: true, + show: (record) => !record.removed + } + ] }, { name: 'quotaemailtemplate', diff --git a/ui/src/config/section/role.js b/ui/src/config/section/role.js index 3823d633b18..16d55c39b2d 100644 --- a/ui/src/config/section/role.js +++ b/ui/src/config/section/role.js @@ -24,7 +24,8 @@ export default { icon: 'idcard-outlined', docHelp: 'adminguide/accounts.html#roles', permission: ['listRoles', 'listRolePermissions'], - columns: ['name', 'type', 'description'], + searchFilters: ['name', 'type'], + columns: ['name', 'type', 'description', 'state'], details: ['name', 'id', 'type', 'description', 'ispublic'], tabs: [{ name: 'details', @@ -55,6 +56,38 @@ export default { popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/iam/ImportRole.vue'))) }, + { + api: 'enableRole', + icon: 'play-circle-outlined', + label: 'label.action.enable.role', + message: 'message.enable.role', + dataView: true, + show: (record, store) => { + return record.state === 'disabled' + }, + mapping: { + id: { + value: (record) => { return record.id } + } + }, + popup: true + }, + { + api: 'disableRole', + icon: 'pause-circle-outlined', + label: 'label.action.disable.role', + message: 'message.disable.role', + dataView: true, + show: (record, store) => { + return record.state === 'enabled' + }, + mapping: { + id: { + value: (record) => { return record.id } + } + }, + popup: true + }, { api: 'updateRole', icon: 'edit-outlined', diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index ff0fd99242d..b04d67a361e 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -17,6 +17,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' +import { isZoneCreated } from '@/utils/zone' export default { name: 'storage', @@ -38,7 +39,7 @@ export default { } }, columns: () => { - const fields = ['name', 'state', 'sizegb', 'type', 'vmname'] + const fields = ['name', 'state', 'size', 'type', 'vmname', 'vmstate'] const metricsFields = ['diskkbsread', 'diskkbswrite', 'diskiopstotal'] if (store.getters.userInfo.roletype === 'Admin') { @@ -62,7 +63,7 @@ export default { return fields }, - details: ['name', 'id', 'type', 'storagetype', 'diskofferingdisplaytext', 'deviceid', 'sizegb', 'physicalsize', 'provisioningtype', 'utilization', 'diskkbsread', 'diskkbswrite', 'diskioread', 'diskiowrite', 'diskiopstotal', 'miniops', 'maxiops', 'path'], + details: ['name', 'id', 'type', 'storagetype', 'diskofferingdisplaytext', 'deviceid', 'sizegb', 'physicalsize', 'provisioningtype', 'utilization', 'diskkbsread', 'diskkbswrite', 'diskioread', 'diskiowrite', 'diskiopstotal', 'miniops', 'maxiops', 'path', 'deleteprotection'], related: [{ name: 'snapshot', title: 'label.snapshots', @@ -91,7 +92,7 @@ export default { } ], searchFilters: () => { - var filters = ['name', 'zoneid', 'domainid', 'account', 'state', 'tags'] + var filters = ['name', 'zoneid', 'domainid', 'account', 'state', 'tags', 'serviceofferingid', 'diskofferingid', 'isencrypted'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { filters.push('storageid') } @@ -103,6 +104,7 @@ export default { icon: 'plus-outlined', docHelp: 'adminguide/storage.html#creating-a-new-volume', label: 'label.action.create.volume', + show: isZoneCreated, listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/CreateVolume.vue'))) @@ -112,7 +114,7 @@ export default { icon: 'cloud-upload-outlined', docHelp: 'adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine', label: 'label.upload.volume.from.local', - show: () => { return 'getUploadParamsForVolume' in store.getters.apis }, + show: () => { return isZoneCreated() && 'getUploadParamsForVolume' in store.getters.apis }, listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/UploadLocalVolume.vue'))) @@ -122,6 +124,7 @@ export default { icon: 'link-outlined', docHelp: 'adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine', label: 'label.upload.volume.from.url', + show: isZoneCreated, listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/UploadVolume.vue'))) @@ -148,7 +151,7 @@ export default { icon: 'edit-outlined', label: 'label.edit', dataView: true, - args: ['name'], + args: ['name', 'deleteprotection'], mapping: { account: { value: (record) => { return record.account } @@ -165,9 +168,10 @@ export default { label: 'label.action.take.snapshot', dataView: true, show: (record, store) => { - return record.state === 'Ready' && (record.hypervisor !== 'KVM' || - record.hypervisor === 'KVM' && record.vmstate === 'Running' && store.features.kvmsnapshotenabled || - record.hypervisor === 'KVM' && record.vmstate !== 'Running') + return record.state === 'Ready' && + (record.hypervisor !== 'KVM' || + ['Stopped', 'Destroyed'].includes(record.vmstate) || + store.features.kvmsnapshotenabled) }, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/TakeSnapshot.vue'))) @@ -179,9 +183,10 @@ export default { label: 'label.action.recurring.snapshot', dataView: true, show: (record, store) => { - return record.state === 'Ready' && (record.hypervisor !== 'KVM' || - record.hypervisor === 'KVM' && record.vmstate === 'Running' && store.features.kvmsnapshotenabled || - record.hypervisor === 'KVM' && record.vmstate !== 'Running') + return record.state === 'Ready' && + (record.hypervisor !== 'KVM' || + (['Stopped', 'Destroyed'].includes(record.vmstate)) || + (store.features.kvmsnapshotenabled)) }, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/RecurringSnapshotVolume.vue'))), @@ -251,23 +256,10 @@ export default { label: 'label.action.create.template.from.volume', dataView: true, show: (record) => { - return !['Destroy', 'Destroyed', 'Expunging', 'Expunged', 'Migrating', 'Uploading', 'UploadError', 'Creating'].includes(record.state) && - ((record.type === 'ROOT' && record.vmstate === 'Stopped') || - (record.type !== 'ROOT' && !record.virtualmachineid && !['Allocated', 'Uploaded'].includes(record.state))) + return record.state === 'Ready' && (record.vmstate === 'Stopped' || !record.virtualmachineid) }, - args: (record, store) => { - var fields = ['volumeid', 'name', 'displaytext', 'ostypeid', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'requireshvm', 'passwordenabled'] - if (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) { - fields.push('domainid') - fields.push('account') - } - return fields - }, - mapping: { - volumeid: { - value: (record) => { return record.id } - } - } + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/storage/CreateTemplate.vue'))) }, { api: 'recoverVolume', @@ -388,6 +380,26 @@ export default { dataView: true, show: (record) => { return record.state === 'BackedUp' && record.revertable } }, + { + api: 'extractSnapshot', + icon: 'cloud-download-outlined', + label: 'label.action.download.snapshot', + message: 'message.action.download.snapshot', + dataView: true, + show: (record, store) => { + return (['Admin'].includes(store.userInfo.roletype) || // If admin or owner or belongs to current project + ((record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) || + (record.domainid === store.userInfo.domainid && record.projectid && store.project && store.project.id && record.projectid === store.project.id))) && + record.state === 'BackedUp' + }, + args: ['zoneid'], + mapping: { + zoneid: { + value: (record) => { return record.zoneid } + } + }, + response: (result) => { return `Please click ${result.snapshot.url} to download.` } + }, { api: 'deleteSnapshot', icon: 'delete-outlined', @@ -406,17 +418,32 @@ export default { title: 'label.backup', icon: 'cloud-upload-outlined', permission: ['listBackups'], - columns: [{ name: (record) => { return record.virtualmachinename } }, 'status', 'virtualmachinename', 'type', 'created', 'account', 'domain', 'zone'], - details: ['virtualmachinename', 'id', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'zone', 'account', 'domain', 'created'], + params: { listvmdetails: 'true' }, + columns: ['name', 'status', 'size', 'virtualsize', 'virtualmachinename', 'backupofferingname', 'intervaltype', 'type', 'created', 'account', 'domain', 'zone'], + details: ['name', 'description', 'virtualmachinename', 'id', 'intervaltype', 'type', 'externalid', 'size', 'virtualsize', 'volumes', 'backupofferingname', 'zone', 'account', 'domain', 'created'], + searchFilters: () => { + var filters = ['name', 'zoneid', 'domainid', 'account', 'backupofferingid'] + return filters + }, + tabs: [ + { + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'instance.metadata', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/BackupMetadata.vue'))) + } + ], actions: [ { api: 'restoreBackup', icon: 'sync-outlined', - docHelp: 'adminguide/virtual_machines.html#restoring-vm-backups', + docHelp: 'adminguide/virtual_machines.html#restoring-instance-backups', label: 'label.backup.restore', message: 'message.backup.restore', dataView: true, - show: (record) => { return record.state !== 'Destroyed' } + show: (record) => { return record.status === 'BackedUp' } }, { api: 'restoreVolumeFromBackupAndAttachToVM', @@ -424,17 +451,28 @@ export default { label: 'label.backup.attach.restore', message: 'message.backup.attach.restore', dataView: true, - show: (record) => { return record.state !== 'Destroyed' }, + show: (record) => { return record.status === 'BackedUp' }, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/RestoreAttachBackupVolume.vue'))) }, + { + api: 'createVMFromBackup', + icon: 'caret-right-outlined', + docHelp: 'adminguide/virtual_machines.html#creating-a-new-instance-from-backup', + label: 'label.create.instance.from.backup', + message: 'message.backup.restore', + dataView: true, + popup: true, + show: (record) => { return record.status === 'BackedUp' }, + component: shallowRef(defineAsyncComponent(() => import('@/views/storage/CreateVMFromBackup.vue'))) + }, { api: 'removeVirtualMachineFromBackupOffering', icon: 'scissor-outlined', label: 'label.backup.offering.remove', message: 'message.backup.offering.remove', dataView: true, - show: (record) => { return record.state !== 'Destroyed' }, + show: (record) => { return record.state !== 'Destroyed' && record.vmbackupofferingremoved !== true }, args: ['forced', 'virtualmachineid'], mapping: { forced: { @@ -517,6 +555,149 @@ export default { groupMap: (selection) => { return selection.map(x => { return { id: x } }) } } ] + }, + { + name: 'sharedfs', + title: 'label.shared.filesystems', + icon: 'file-text-outlined', + permission: ['listSharedFileSystems'], + resourceType: 'SharedFS', + columns: () => { + const fields = ['name', 'state', 'sizegb'] + const metricsFields = ['diskkbsread', 'diskkbswrite', 'utilization', 'physicalsize'] + + if (store.getters.metrics) { + fields.push(...metricsFields) + } + if (store.getters.userInfo.roletype === 'Admin') { + fields.push('storage') + fields.push('account') + } else if (store.getters.userInfo.roletype === 'DomainAdmin') { + fields.push('account') + } + if (store.getters.listAllProjects) { + fields.push('project') + } + fields.push('zonename') + + return fields + }, + details: ['id', 'name', 'description', 'state', 'filesystem', 'diskofferingdisplaytext', 'ipaddress', 'sizegb', 'provider', 'protocol', 'provisioningtype', 'utilization', 'diskkbsread', 'diskkbswrite', 'diskioread', 'diskiowrite', 'account', 'domain', 'created'], + tabs: [{ + component: shallowRef(defineAsyncComponent(() => import('@/views/storage/SharedFSTab.vue'))) + }], + searchFilters: () => { + var filters = ['name', 'zoneid', 'domainid', 'account', 'networkid', 'serviceofferingid', 'diskofferingid'] + return filters + }, + actions: [ + { + api: 'createSharedFileSystem', + icon: 'plus-outlined', + docHelp: 'adminguide/storage.html#creating-a-new-file-share', + label: 'label.create.sharedfs', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/storage/CreateSharedFS.vue'))) + }, + { + api: 'updateSharedFileSystem', + icon: 'edit-outlined', + docHelp: 'adminguide/storage.html#lifecycle-operations', + label: 'label.update.sharedfs', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/storage/UpdateSharedFS.vue'))) + }, + { + api: 'startSharedFileSystem', + icon: 'caret-right-outlined', + label: 'label.action.start.sharedfs', + message: 'message.action.start.sharedfs', + docHelp: 'adminguide/storage.html#lifecycle-operations', + dataView: true, + popup: true, + groupAction: true, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + show: (record) => { return ['Stopped'].includes(record.state) } + }, + { + api: 'stopSharedFileSystem', + icon: 'poweroff-outlined', + label: 'label.action.stop.sharedfs', + message: 'message.action.stop.sharedfs', + docHelp: 'adminguide/storage.html#lifecycle-operations', + dataView: true, + popup: true, + groupAction: true, + groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }, + args: ['forced'], + show: (record) => { return ['Ready'].includes(record.state) } + }, + { + api: 'restartSharedFileSystem', + icon: 'reload-outlined', + docHelp: 'adminguide/storage.html#lifecycle-operations', + label: 'label.action.restart.sharedfs', + message: 'message.action.restart.sharedfs', + dataView: true, + popup: true, + args: ['cleanup'], + show: (record) => { return ['Stopped', 'Ready', 'Detached'].includes(record.state) } + }, + { + api: 'changeSharedFileSystemDiskOffering', + icon: 'swap-outlined', + docHelp: 'adminguide/storage.html#lifecycle-operations', + label: 'label.change.disk.offering', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/storage/ChangeSharedFSDiskOffering.vue'))), + show: (record) => { return ['Stopped', 'Ready'].includes(record.state) } + }, + { + api: 'changeSharedFileSystemServiceOffering', + icon: 'arrows-alt-outlined', + docHelp: 'adminguide/storage.html#lifecycle-operations', + label: 'label.change.service.offering', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/storage/ChangeSharedFSServiceOffering.vue'))), + show: (record) => { return ['Stopped'].includes(record.state) } + }, + { + api: 'destroySharedFileSystem', + icon: 'delete-outlined', + docHelp: 'adminguide/storage.html#lifecycle-operations', + label: 'label.destroy.sharedfs', + message: 'message.action.destroy.sharedfs', + dataView: true, + popup: true, + groupAction: true, + groupMap: (selection, values) => { return selection.map(x => { return { id: x, expunge: values.expunge, forced: values.forced } }) }, + args: ['expunge', 'forced'], + show: (record) => { return !['Destroyed', 'Expunging', 'Error'].includes(record.state) } + }, + { + api: 'recoverSharedFileSystem', + icon: 'medicine-box-outlined', + docHelp: 'adminguide/storage.html#lifecycle-operations', + label: 'label.recover.sharedfs', + message: 'message.action.recover.sharedfs', + dataView: true, + show: (record) => { return record.state === 'Destroyed' } + }, + { + api: 'expungeSharedFileSystem', + icon: 'delete-outlined', + docHelp: 'adminguide/storage.html#lifecycle-operations', + label: 'label.expunge.sharedfs', + message: 'message.action.expunge.sharedfs', + dataView: true, + popup: true, + show: (record) => { return ['Destroyed', 'Expunging', 'Error'].includes(record.state) } + } + ] } ] } diff --git a/ui/src/config/section/tools.js b/ui/src/config/section/tools.js index a04563c477a..a07228ca87b 100644 --- a/ui/src/config/section/tools.js +++ b/ui/src/config/section/tools.js @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. import store from '@/store' +import { shallowRef, defineAsyncComponent } from 'vue' export default { name: 'tools', @@ -60,6 +61,14 @@ export default { } ] }, + { + name: 'usage', + title: 'label.usage', + icon: 'ContainerOutlined', + permission: ['listUsageRecords'], + meta: { title: 'label.usage', icon: 'ContainerOutlined' }, + component: () => import('@/views/infra/UsageRecords.vue') + }, { name: 'manageinstances', title: 'label.action.import.export.instances', @@ -68,6 +77,163 @@ export default { resourceType: 'UserVm', permission: ['listInfrastructure', 'listUnmanagedInstances'], component: () => import('@/views/tools/ManageInstances.vue') + }, + { + name: 'managevolumes', + title: 'label.action.import.unmanage.volumes', + icon: 'interaction-outlined', + docHelp: 'adminguide/virtual_machines.html#importing-and-unmanaging-volume', + resourceType: 'UserVm', + permission: ['listInfrastructure', 'listVolumesForImport'], + component: () => import('@/views/tools/ManageVolumes.vue') + }, + { + name: 'webhook', + title: 'label.webhooks', + icon: 'node-index-outlined', + docHelp: 'adminguide/webhooks.html', + permission: ['listWebhooks'], + columns: () => { + const cols = ['name', 'payloadurl', 'state', 'created'] + if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { + cols.splice(3, 0, 'account', 'domain', 'scope') + } + if (store.getters.listAllProjects) { + cols.push('project') + } + return cols + }, + details: ['name', 'id', 'description', 'scope', 'payloadurl', 'sslverification', 'secretkey', 'state', 'account', 'domainid'], + searchFilters: () => { + var filters = ['state'] + if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { + filters.push('scope', 'domainid', 'account') + } + return filters + }, + tabs: [ + { + name: 'details', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, + { + name: 'recent.deliveries', + component: shallowRef(defineAsyncComponent(() => import('@/components/view/WebhookDeliveriesTab.vue'))) + } + ], + actions: [ + { + api: 'createWebhook', + icon: 'plus-outlined', + label: 'label.create.webhook', + docHelp: 'adminguide/events.html#creating-webhooks', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/tools/CreateWebhook.vue'))) + }, + { + api: 'updateWebhook', + icon: 'edit-outlined', + label: 'label.update.webhook', + dataView: true, + popup: true, + args: ['name', 'description', 'payloadurl', 'sslverification', 'secretkey', 'state'], + mapping: { + state: { + options: ['Enabled', 'Disabled'] + } + } + }, + { + api: 'updateWebhook', + icon: 'play-circle-outlined', + label: 'label.enable.webhook', + message: 'message.confirm.enable.webhook', + dataView: true, + groupAction: true, + popup: true, + defaultArgs: { state: 'Enabled' }, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + show: (record) => { return ['Disabled'].includes(record.state) } + }, + { + api: 'updateWebhook', + icon: 'pause-circle-outlined', + label: 'label.disable.webhook', + message: 'message.confirm.disable.webhook', + dataView: true, + groupAction: true, + popup: true, + defaultArgs: { state: 'Disabled' }, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + show: (record) => { return ['Enabled'].includes(record.state) } + }, + { + api: 'executeWebhookDelivery', + icon: 'right-square-outlined', + label: 'label.test.webhook.delivery', + message: 'message.test.webhook.delivery', + dataView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/tools/TestWebhookDelivery.vue'))) + }, + { + api: 'deleteWebhook', + icon: 'delete-outlined', + label: 'label.delete.webhook', + message: 'message.delete.webhook', + dataView: true, + groupAction: true, + popup: true, + groupShow: (selectedItems, storegetters) => { + if (['Admin'].includes(storegetters.userInfo.roletype)) { + return true + } + }, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + } + ] + }, + { + name: 'webhookdeliveries', + title: 'label.webhook.deliveries', + icon: 'gateway-outlined', + hidden: true, + permission: ['listWebhookDeliveries'], + columns: () => { + const cols = ['payload', 'eventtype', 'webhookname', 'success', 'response', 'duration'] + if (['Admin'].includes(store.getters.userInfo.roletype)) { + cols.splice(3, 0, 'managementservername') + } + return cols + }, + details: () => { + const fields = ['id', 'eventid', 'eventtype', 'headers', 'payload', 'success', 'response', 'startdate', 'enddate'] + if (['Admin'].includes(store.getters.userInfo.roletype)) { + fields.splice(1, 0, 'managementserverid', 'managementservername') + } + return fields + }, + actions: [ + { + api: 'executeWebhookDelivery', + icon: 'retweet-outlined', + label: 'label.redeliver', + message: 'message.redeliver.webhook.delivery', + dataView: true, + popup: true + }, + { + api: 'deleteWebhookDelivery', + icon: 'delete-outlined', + label: 'label.delete.webhook.delivery', + message: 'message.delete.webhook.delivery', + dataView: true, + groupAction: true, + popup: true, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + } + ] } ] } diff --git a/ui/src/config/section/user.js b/ui/src/config/section/user.js index d8c4ac06d0c..a18994fd6ce 100644 --- a/ui/src/config/section/user.js +++ b/ui/src/config/section/user.js @@ -17,6 +17,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' +import { i18n } from '@/locales' export default { name: 'accountuser', @@ -25,7 +26,32 @@ export default { docHelp: 'adminguide/accounts.html#users', hidden: true, permission: ['listUsers'], - columns: ['username', 'state', 'firstname', 'lastname', 'email', 'account', 'domain'], + searchFilters: () => { + const filters = ['usersource'] + if (store.getters.userInfo.roletype === 'Admin') { + filters.push('apikeyaccess') + } + return filters + }, + columns: [ + 'username', 'state', 'firstname', 'lastname', + 'email', 'account', 'domain', + { + field: 'userSource', + customTitle: 'userSource', + userSource: (record) => { + let { usersource: source } = record + + if (source === 'saml2') { + source = 'saml' + } else if (source === 'saml2disabled') { + source = 'saml.disabled' + } + + return i18n.global.t(`label.${source}`) + } + } + ], details: ['username', 'id', 'firstname', 'lastname', 'email', 'usersource', 'timezone', 'rolename', 'roletype', 'is2faenabled', 'account', 'domain', 'created'], tabs: [ { diff --git a/ui/src/core/ext.js b/ui/src/core/ext.js index f6cfb353b13..ac8e0e45fcb 100644 --- a/ui/src/core/ext.js +++ b/ui/src/core/ext.js @@ -18,15 +18,11 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' -// import { fab } from '@fortawesome/free-brands-svg-icons' -// import { fas } from '@fortawesome/free-solid-svg-icons' -// import { far } from '@fortawesome/free-regular-svg-icons' - import { faCentos, faUbuntu, faDebian, faSuse, faRedhat, faFedora, faLinux, faFreebsd, faApple, faWindows, faJava } from '@fortawesome/free-brands-svg-icons' -import { faCompactDisc, faCameraRetro, faDharmachakra } from '@fortawesome/free-solid-svg-icons' +import { fas, faCompactDisc, faCameraRetro, faDharmachakra, faMicrochip } from '@fortawesome/free-solid-svg-icons' library.add(faCentos, faUbuntu, faDebian, faSuse, faRedhat, faFedora, faLinux, faFreebsd, faApple, faWindows, faJava) -library.add(faCompactDisc, faCameraRetro, faDharmachakra) +library.add(fas, faCompactDisc, faCameraRetro, faDharmachakra, faMicrochip) export default { install: (app) => { diff --git a/ui/src/core/lazy_lib/components_use.js b/ui/src/core/lazy_lib/components_use.js index 98fc9e0c816..6f716bfbd94 100644 --- a/ui/src/core/lazy_lib/components_use.js +++ b/ui/src/core/lazy_lib/components_use.js @@ -48,6 +48,7 @@ import { Divider, DatePicker, TimePicker, + Typography, Upload, Progress, Skeleton, @@ -61,6 +62,7 @@ import { Tree, Calendar, Slider, + Result, AutoComplete, Collapse, Space, @@ -116,6 +118,7 @@ export default { app.use(Divider) app.use(DatePicker) app.use(TimePicker) + app.use(Typography) app.use(Upload) app.use(Progress) app.use(Skeleton) @@ -133,5 +136,6 @@ export default { app.use(Descriptions) app.use(Space) app.use(Statistic) + app.use(Result) } } diff --git a/ui/src/core/lazy_lib/icons_use.js b/ui/src/core/lazy_lib/icons_use.js index ea0abdc3506..502eb5de0b6 100644 --- a/ui/src/core/lazy_lib/icons_use.js +++ b/ui/src/core/lazy_lib/icons_use.js @@ -19,8 +19,10 @@ import { AimOutlined, ApartmentOutlined, ApiOutlined, + AppstoreAddOutlined, AppstoreOutlined, ArrowDownOutlined, + ArrowRightOutlined, ArrowUpOutlined, ArrowsAltOutlined, AuditOutlined, @@ -55,6 +57,7 @@ import { ClusterOutlined, CodeOutlined, CompassOutlined, + ContainerOutlined, ControlOutlined, CopyOutlined, CreditCardOutlined, @@ -80,6 +83,9 @@ import { FieldTimeOutlined, FileDoneOutlined, FileProtectOutlined, + FileSyncOutlined, + FileTextOutlined, + FileZipOutlined, FilterOutlined, FilterTwoTone, FireOutlined, @@ -96,6 +102,7 @@ import { GlobalOutlined, GoldOutlined, GoogleOutlined, + GroupOutlined, HddOutlined, HomeOutlined, IdcardOutlined, @@ -120,15 +127,19 @@ import { MinusSquareOutlined, MobileOutlined, MoreOutlined, + NodeIndexOutlined, NotificationOutlined, NumberOutlined, LaptopOutlined, OrderedListOutlined, PaperClipOutlined, + PartitionOutlined, PauseCircleOutlined, + PicCenterOutlined, PictureOutlined, PieChartOutlined, PlayCircleOutlined, + PlaySquareOutlined, PlusCircleOutlined, PlusOutlined, PlusSquareOutlined, @@ -139,7 +150,9 @@ import { ReconciliationOutlined, RedoOutlined, ReloadOutlined, + RetweetOutlined, RightCircleOutlined, + RightSquareOutlined, RocketOutlined, SafetyCertificateOutlined, SafetyOutlined, @@ -151,6 +164,7 @@ import { SettingOutlined, ShareAltOutlined, ShoppingOutlined, + SplitCellsOutlined, StopOutlined, SwapOutlined, SyncOutlined, @@ -167,6 +181,8 @@ import { UserOutlined, UserSwitchOutlined, UploadOutlined, + VerticalAlignBottomOutlined, + VerticalAlignTopOutlined, WarningOutlined, WifiOutlined, SolutionOutlined @@ -178,8 +194,10 @@ export default { app.component('AimOutlined', AimOutlined) app.component('ApartmentOutlined', ApartmentOutlined) app.component('ApiOutlined', ApiOutlined) + app.component('AppstoreAddOutlined', AppstoreAddOutlined) app.component('AppstoreOutlined', AppstoreOutlined) app.component('ArrowDownOutlined', ArrowDownOutlined) + app.component('ArrowRightOutlined', ArrowRightOutlined) app.component('ArrowUpOutlined', ArrowUpOutlined) app.component('ArrowsAltOutlined', ArrowsAltOutlined) app.component('AuditOutlined', AuditOutlined) @@ -213,6 +231,7 @@ export default { app.component('CloudUploadOutlined', CloudUploadOutlined) app.component('ClusterOutlined', ClusterOutlined) app.component('CodeOutlined', CodeOutlined) + app.component('ContainerOutlined', ContainerOutlined) app.component('ControlOutlined', ControlOutlined) app.component('CompassOutlined', CompassOutlined) app.component('CopyOutlined', CopyOutlined) @@ -239,6 +258,9 @@ export default { app.component('FieldTimeOutlined', FieldTimeOutlined) app.component('FileDoneOutlined', FileDoneOutlined) app.component('FileProtectOutlined', FileProtectOutlined) + app.component('FileSyncOutlined', FileSyncOutlined) + app.component('FileTextOutlined', FileTextOutlined) + app.component('FileZipOutlined', FileZipOutlined) app.component('FilterOutlined', FilterOutlined) app.component('FilterTwoTone', FilterTwoTone) app.component('FireOutlined', FireOutlined) @@ -255,6 +277,7 @@ export default { app.component('GlobalOutlined', GlobalOutlined) app.component('GoldOutlined', GoldOutlined) app.component('GoogleOutlined', GoogleOutlined) + app.component('GroupOutlined', GroupOutlined) app.component('HddOutlined', HddOutlined) app.component('HomeOutlined', HomeOutlined) app.component('IdcardOutlined', IdcardOutlined) @@ -279,15 +302,19 @@ export default { app.component('MinusSquareOutlined', MinusSquareOutlined) app.component('MobileOutlined', MobileOutlined) app.component('MoreOutlined', MoreOutlined) + app.component('NodeIndexOutlined', NodeIndexOutlined) app.component('NotificationOutlined', NotificationOutlined) app.component('NumberOutlined', NumberOutlined) app.component('LaptopOutlined', LaptopOutlined) app.component('OrderedListOutlined', OrderedListOutlined) app.component('PaperClipOutlined', PaperClipOutlined) + app.component('PartitionOutlined', PartitionOutlined) app.component('PauseCircleOutlined', PauseCircleOutlined) + app.component('PicCenterOutlined', PicCenterOutlined) app.component('PictureOutlined', PictureOutlined) app.component('PieChartOutlined', PieChartOutlined) app.component('PlayCircleOutlined', PlayCircleOutlined) + app.component('PlaySquareOutlined', PlaySquareOutlined) app.component('PlusCircleOutlined', PlusCircleOutlined) app.component('PlusOutlined', PlusOutlined) app.component('PlusSquareOutlined', PlusSquareOutlined) @@ -298,7 +325,9 @@ export default { app.component('ReconciliationOutlined', ReconciliationOutlined) app.component('RedoOutlined', RedoOutlined) app.component('ReloadOutlined', ReloadOutlined) + app.component('RetweetOutlined', RetweetOutlined) app.component('RightCircleOutlined', RightCircleOutlined) + app.component('RightSquareOutlined', RightSquareOutlined) app.component('RocketOutlined', RocketOutlined) app.component('SafetyCertificateOutlined', SafetyCertificateOutlined) app.component('SafetyOutlined', SafetyOutlined) @@ -310,6 +339,7 @@ export default { app.component('SettingOutlined', SettingOutlined) app.component('ShareAltOutlined', ShareAltOutlined) app.component('ShoppingOutlined', ShoppingOutlined) + app.component('SplitCellsOutlined', SplitCellsOutlined) app.component('StopOutlined', StopOutlined) app.component('SwapOutlined', SwapOutlined) app.component('SyncOutlined', SyncOutlined) @@ -326,6 +356,8 @@ export default { app.component('UserOutlined', UserOutlined) app.component('UserSwitchOutlined', UserSwitchOutlined) app.component('UploadOutlined', UploadOutlined) + app.component('VerticalAlignBottomOutlined', VerticalAlignBottomOutlined) + app.component('VerticalAlignTopOutlined', VerticalAlignTopOutlined) app.component('WarningOutlined', WarningOutlined) app.component('WifiOutlined', WifiOutlined) app.component('renderIcon', renderIcon) diff --git a/ui/src/layouts/UserLayout.vue b/ui/src/layouts/UserLayout.vue index 6c81c858bd2..52fe94c7f5d 100644 --- a/ui/src/layouts/UserLayout.vue +++ b/ui/src/layouts/UserLayout.vue @@ -37,8 +37,9 @@ - diff --git a/ui/src/locales/index.js b/ui/src/locales/index.js index 29c828b6447..6933e05206e 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 fbe3fa78584..2ce2f6b6468 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,17 @@ import { resourceTypePlugin, fileSizeUtilPlugin, genericUtilPlugin, - localesPlugin + localesPlugin, + dialogUtilPlugin, + cpuArchitectureUtilPlugin, + imagesUtilPlugin, + extensionsUtilPlugin } 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,24 +59,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(extensions) vueApp.use(directives) vueApp.use(require('vue3-shortkey'), { prevent: ['input', 'textarea'] }) -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 - - loadLanguageAsync().then(() => { - vueApp.use(store) - .use(router) - .use(i18n) - .use(bootstrap) - .mount('#app') +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') + }) + }).catch(error => { + renderError(error) }) -}) diff --git a/ui/src/permission.js b/ui/src/permission.js index 6db690f6e6a..266dc992c8d 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 @@ -112,7 +112,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 67b168be8c2..911234d9b71 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, diff --git a/ui/src/store/modules/app.js b/ui/src/store/modules/app.js index cf2b34e4b8e..f6d66870d96 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 3b080be5f63..ad11fe5a8d0 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 fb5b6ff5e0b..0fbdc5788c0 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,23 @@ 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 } from '@/store/mutation-types' +import { + applyCustomGuiTheme +} from '@/utils/guiTheme' + const user = { state: { token: '', @@ -63,6 +73,8 @@ const user = { loginFlag: false, logoutFlag: false, customColumns: {}, + msId: '', + maintenanceInitiated: false, shutdownTriggered: false, twoFaEnabled: false, twoFaProvider: '', @@ -120,6 +132,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 +154,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 +190,12 @@ 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 + } } }, @@ -212,8 +241,12 @@ 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() }).catch(error => { reject(error) @@ -259,6 +292,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,37 +314,44 @@ 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) 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) + store.dispatch('SetCsLatestVersion', result.rolename) resolve(cachedApis) }).catch(error => { reject(error) }) } 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 +359,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 +377,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 +436,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 +467,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,6 +481,7 @@ 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) @@ -406,6 +495,11 @@ const user = { } }).catch(() => { resolve() + }).finally(() => { + Object.keys(Cookies.get()).forEach(cookieName => { + Cookies.remove(cookieName) + Cookies.remove(cookieName, { path: '/client' }) + }) }) }) }, @@ -429,7 +523,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,7 +548,7 @@ 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) @@ -462,7 +556,7 @@ const user = { reject(error) }) - api('listConfigurations', { name: 'hypervisor.custom.display.name' }).then(json => { + 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) @@ -474,7 +568,7 @@ const user = { }, 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 +579,29 @@ const user = { SetDomainStore ({ commit }, domainStore) { commit('SET_DOMAIN_STORE', domainStore) }, + SetCsLatestVersion ({ commit }, rolename) { + 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 77aeb8fb7b6..0b1f921ab86 100644 --- a/ui/src/store/mutation-types.js +++ b/ui/src/store/mutation-types.js @@ -29,15 +29,18 @@ 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' diff --git a/ui/src/style/README.md b/ui/src/style/README.md index 391c66565d4..8e3fd832bd5 100644 --- a/ui/src/style/README.md +++ b/ui/src/style/README.md @@ -5,7 +5,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 +13,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 +25,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 +34,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 00000000000..290a3b6e78c --- /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 fc6fdf75170..de2d494c878 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/date.js b/ui/src/utils/date.js new file mode 100644 index 00000000000..216dfde1303 --- /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 00000000000..cd8d0c4daca --- /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 00000000000..b1a7209fd27 --- /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 00000000000..fa650cdd7b8 --- /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 00000000000..c3054f48ff7 --- /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 3e829bdb8dd..61456d98b12 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 (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) } - if (name) { - content = content + ' - ' + name - } - message.success({ - content, - key: jobId, - duration: 2 - }) store.dispatch('AddHeaderNotice', { key: jobId, title, @@ -288,37 +295,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 +344,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 +386,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': @@ -418,6 +420,8 @@ export const resourceTypePlugin = { case 'VpnCustomerGateway': case 'AutoScaleVmGroup': case 'QuotaTariff': + case 'GuestOsCategory': + case 'Extension': return resourceType.toLowerCase() } return '' @@ -427,6 +431,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 || '' } } @@ -488,6 +495,15 @@ export const fileSizeUtilPlugin = { } } +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) { @@ -496,11 +512,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)))) } } } @@ -521,3 +536,64 @@ 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) + }) + } + } +} + +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)' } + ] + 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 + } + } +} diff --git a/ui/src/utils/quota.js b/ui/src/utils/quota.js new file mode 100644 index 00000000000..b8adbb93518 --- /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/request.js b/ui/src/utils/request.js index c2fe04ab9d1..42b26c9785b 100644 --- a/ui/src/utils/request.js +++ b/ui/src/utils/request.js @@ -51,7 +51,7 @@ 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 @@ -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 b2f3d5451ca..5e893fb58ec 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 a55d05dbd8f..8773f07446e 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,35 @@ 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 +} diff --git a/ui/src/utils/zone.js b/ui/src/utils/zone.js new file mode 100644 index 00000000000..266bf9df445 --- /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 b59fe8ed6cc..0fa66de33ad 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -17,10 +17,16 @@ @@ -148,7 +171,8 @@ @refresh-data="fetchData" @poll-action="pollActionCompletion" @close-action="closeAction" - @cancel-bulk-action="handleCancel"/> + @cancel-bulk-action="handleCancel" + /> @@ -172,25 +196,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" + > - + @@ -213,17 +251,26 @@ :pagination="true" style="overflow-y: auto" > +
-
+
-
+ layout="vertical" + > +
= 0 }" > - {{ }} + {{ }} + :label="opt" + > {{ opt }} @@ -273,11 +333,15 @@ }" v-focus="fieldIndex === firstIndex" > - {{ }} + {{ }} + :label="opt.name || opt.description || opt.traffictype || opt.publicip" + > {{ opt.name || opt.description || opt.traffictype || opt.publicip }} @@ -293,44 +357,96 @@ }" v-focus="fieldIndex === firstIndex" > - {{ }} - + {{ }} +
- + - + - + - + - + - + - + - + - + - + - + - + {{ opt.name || opt.description || opt.traffictype || opt.publicip }}
@@ -352,10 +468,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') }}
@@ -395,28 +523,39 @@
-
+
- + + :tabs="$route.meta.tabs" + />
-
+
+ /> + showQuickJumper + > @@ -441,14 +581,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 8503f71082b..24065f47b1a 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/KubernetesRemoveNodes.vue b/ui/src/views/compute/KubernetesRemoveNodes.vue new file mode 100644 index 00000000000..803ec2bd1b1 --- /dev/null +++ b/ui/src/views/compute/KubernetesRemoveNodes.vue @@ -0,0 +1,151 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + + + + diff --git a/ui/src/views/compute/KubernetesServiceTab.vue b/ui/src/views/compute/KubernetesServiceTab.vue index b3556cb65ce..fc8f9c60213 100644 --- a/ui/src/views/compute/KubernetesServiceTab.vue +++ b/ui/src/views/compute/KubernetesServiceTab.vue @@ -25,7 +25,7 @@ - +
@@ -114,7 +114,18 @@ + + diff --git a/ui/src/views/iam/EditUser.vue b/ui/src/views/iam/EditUser.vue index e082fd17a06..c062e545b18 100644 --- a/ui/src/views/iam/EditUser.vue +++ b/ui/src/views/iam/EditUser.vue @@ -81,6 +81,16 @@ + + + + Enabled + Inherit + Disabled + +
{{ $t('label.cancel') }} {{ $t('label.ok') }} @@ -92,7 +102,7 @@ + + diff --git a/ui/src/views/infra/ChangeStoragePoolScope.vue b/ui/src/views/infra/ChangeStoragePoolScope.vue new file mode 100644 index 00000000000..7c1565fe959 --- /dev/null +++ b/ui/src/views/infra/ChangeStoragePoolScope.vue @@ -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. + + + + + + diff --git a/ui/src/views/infra/ClusterAdd.vue b/ui/src/views/infra/ClusterAdd.vue index 862b458b388..7fee1f11bf6 100644 --- a/ui/src/views/infra/ClusterAdd.vue +++ b/ui/src/views/infra/ClusterAdd.vue @@ -16,14 +16,23 @@ // under the License. diff --git a/ui/src/views/infra/ConfigureHostOOBM.vue b/ui/src/views/infra/ConfigureHostOOBM.vue new file mode 100644 index 00000000000..a873baab83f --- /dev/null +++ b/ui/src/views/infra/ConfigureHostOOBM.vue @@ -0,0 +1,172 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + + + + + diff --git a/ui/src/views/infra/Confirmation.vue b/ui/src/views/infra/Confirmation.vue index 96b7f6f69ce..6f9084dc814 100644 --- a/ui/src/views/infra/Confirmation.vue +++ b/ui/src/views/infra/Confirmation.vue @@ -24,6 +24,34 @@ :rules="rules" @finish="handleSubmit" > + + + + + {{ opt }} + + + + + + + + diff --git a/ui/src/views/infra/HostInfo.vue b/ui/src/views/infra/HostInfo.vue index 1d0b47eba95..0fea5788a31 100644 --- a/ui/src/views/infra/HostInfo.vue +++ b/ui/src/views/infra/HostInfo.vue @@ -48,11 +48,49 @@
+ +
+ {{ $t('label.instance.conversion.support') }} +
+ {{ host.instanceconversionsupported }} +
+
+
+ +
+ {{ $t('label.host.virtv2v.version') }} +
+ {{ host.details['host.virtv2v.version'] }} +
+
+
+ +
+ {{ $t('label.host.ovftool.version') }} +
+ {{ host.details['host.ovftool.version'] }} +
+
+
{{ $t('label.hosttags') }} +
+ {{ hosttag.tag }} + + {{ $t('label.hosttags.explicit.abbr') }} + + + {{ $t('label.hosttags.implicit.abbr') }} + +
+
+
+ +
+ {{ $t('label.storageaccessgroups') }}
- {{ host.hosttags }} + {{ host.storageaccessgroups }}
@@ -72,14 +110,48 @@
- -
- {{ $t('label.powerstate') }} + +
- {{ host.outofbandmanagement.powerstate }} + {{ $t('label.oobm.username') }} +
+ {{ host.outofbandmanagement.username }} +
-
-
+ + +
+ {{ $t('label.oobm.powerstate') }} +
+ {{ host.outofbandmanagement.powerstate }} +
+
+
+ +
+ {{ $t('label.oobm.driver') }} +
+ {{ host.outofbandmanagement.driver }} +
+
+
+ +
+ {{ $t('label.oobm.address') }} +
+ {{ host.outofbandmanagement.address }} +
+
+
+ +
+ {{ $t('label.oobm.port') }} +
+ {{ host.outofbandmanagement.port }} +
+
+
+
{{ $t('label.haenable') }} @@ -117,7 +189,7 @@ + + diff --git a/ui/src/views/infra/InfraSummary.vue b/ui/src/views/infra/InfraSummary.vue index a35db46f760..dc8559adb11 100644 --- a/ui/src/views/infra/InfraSummary.vue +++ b/ui/src/views/infra/InfraSummary.vue @@ -169,7 +169,7 @@ diff --git a/ui/src/views/infra/Metrics.vue b/ui/src/views/infra/Metrics.vue index 010059b885e..dc691751f24 100644 --- a/ui/src/views/infra/Metrics.vue +++ b/ui/src/views/infra/Metrics.vue @@ -77,7 +77,7 @@ + + diff --git a/ui/src/views/infra/Resources.vue b/ui/src/views/infra/Resources.vue index 3d0c4a27099..cad1a026c0b 100644 --- a/ui/src/views/infra/Resources.vue +++ b/ui/src/views/infra/Resources.vue @@ -66,7 +66,7 @@ + + diff --git a/ui/src/views/infra/UsageRecords.vue b/ui/src/views/infra/UsageRecords.vue new file mode 100644 index 00000000000..1a89efec1f4 --- /dev/null +++ b/ui/src/views/infra/UsageRecords.vue @@ -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. + + + + + + diff --git a/ui/src/views/infra/ZoneUpdate.vue b/ui/src/views/infra/ZoneUpdate.vue new file mode 100644 index 00000000000..26968f25127 --- /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 7343b30b692..9d47138f0ae 100644 --- a/ui/src/views/infra/network/DedicatedVLANTab.vue +++ b/ui/src/views/infra/network/DedicatedVLANTab.vue @@ -174,7 +174,7 @@ + + diff --git a/ui/src/views/infra/zone/BgpPeersTab.vue b/ui/src/views/infra/zone/BgpPeersTab.vue new file mode 100644 index 00000000000..e55daf02a41 --- /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 c39534d7b93..4c9af84a8f8 100644 --- a/ui/src/views/infra/zone/IpAddressRangeForm.vue +++ b/ui/src/views/infra/zone/IpAddressRangeForm.vue @@ -30,16 +30,17 @@ :columns="columns" :pagination="false" style="margin-bottom: 24px; width: 100%" > -