mirror of https://github.com/apache/cloudstack.git
merge main changes
This commit is contained in:
commit
2aae36d3e1
|
|
@ -50,7 +50,8 @@ github:
|
|||
rebase: false
|
||||
|
||||
collaborators:
|
||||
- gpordeus
|
||||
- ingox
|
||||
- gp-santos
|
||||
- erikbocks
|
||||
- Imvedansh
|
||||
- Damans227
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ jobs:
|
|||
# https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2004-Readme.md#mysql
|
||||
sudo apt-get install -y mysql-server
|
||||
sudo systemctl start mysql
|
||||
sudo mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY ''; FLUSH PRIVILEGES;"
|
||||
sudo mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY ''; FLUSH PRIVILEGES;"
|
||||
sudo systemctl restart mysql
|
||||
sudo mysql -uroot -e "SELECT VERSION();"
|
||||
|
||||
|
|
@ -341,7 +341,7 @@ jobs:
|
|||
echo -e "Simulator CI Test Results: (only failures listed)\n"
|
||||
python3 ./tools/marvin/xunit-reader.py integration-test-results/
|
||||
|
||||
- uses: codecov/codecov-action@v4
|
||||
- uses: codecov/codecov-action@v6
|
||||
with:
|
||||
files: jacoco-coverage.xml
|
||||
fail_ci_if_error: true
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ jobs:
|
|||
cd nonoss && bash -x install-non-oss.sh && cd ..
|
||||
mvn -P quality -Dsimulator -Dnoredist clean install -T$(nproc)
|
||||
|
||||
- uses: codecov/codecov-action@v4
|
||||
- uses: codecov/codecov-action@v6
|
||||
with:
|
||||
files: ./client/target/site/jacoco-aggregate/jacoco.xml
|
||||
fail_ci_if_error: true
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "Security"
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@ jobs:
|
|||
comment_repo: ""
|
||||
steps:
|
||||
- name: Setup
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.45.
|
||||
with:
|
||||
destination: /opt/gh-aw/
|
||||
- name: Check workflow file
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_WORKFLOW_FILE: "daily-repo-status.lock.yml"
|
||||
with:
|
||||
|
|
@ -96,7 +96,7 @@ jobs:
|
|||
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
|
||||
steps:
|
||||
- name: Setup
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.45.
|
||||
with:
|
||||
destination: /opt/gh-aw/
|
||||
- name: Checkout
|
||||
|
|
@ -120,7 +120,7 @@ jobs:
|
|||
id: checkout-
|
||||
if: |
|
||||
github.event.
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
|
@ -132,7 +132,7 @@ jobs:
|
|||
await main();
|
||||
- name: Generate agentic run
|
||||
id:
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
|
@ -469,7 +469,7 @@ jobs:
|
|||
}
|
||||
|
||||
- name: Generate workflow
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
with:
|
||||
script: |
|
||||
const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs');
|
||||
|
|
@ -559,7 +559,7 @@ jobs:
|
|||
{{#runtime-import .github/workflows/daily-repo-status.md}}
|
||||
|
||||
- name: Substitute
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.
|
||||
GH_AW_GITHUB_ACTOR: ${{ github.actor }}
|
||||
|
|
@ -589,7 +589,7 @@ jobs:
|
|||
}
|
||||
});
|
||||
- name: Interpolate variables and render
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.
|
||||
with:
|
||||
|
|
@ -667,7 +667,7 @@ jobs:
|
|||
bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
|
||||
- name: Redact secrets in
|
||||
if: always()
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
|
||||
|
|
@ -682,7 +682,7 @@ jobs:
|
|||
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload Safe
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0.
|
||||
with:
|
||||
name: safe-
|
||||
path: ${{ env.GH_AW_SAFE_OUTPUTS }}
|
||||
|
|
@ -690,7 +690,7 @@ jobs:
|
|||
- name: Ingest agent
|
||||
id:
|
||||
if: always()
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
|
||||
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
|
||||
|
|
@ -704,13 +704,13 @@ jobs:
|
|||
await main();
|
||||
- name: Upload sanitized agent
|
||||
if: always() && env.
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0.
|
||||
with:
|
||||
name: agent-
|
||||
path: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
if-no-files-found:
|
||||
- name: Upload engine output
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0.
|
||||
with:
|
||||
name:
|
||||
path: |
|
||||
|
|
@ -719,7 +719,7 @@ jobs:
|
|||
if-no-files-found:
|
||||
- name: Parse agent logs for step
|
||||
if: always()
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
|
||||
with:
|
||||
|
|
@ -730,7 +730,7 @@ jobs:
|
|||
await main();
|
||||
- name: Parse MCP Gateway logs for step
|
||||
if: always()
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
|
||||
|
|
@ -755,7 +755,7 @@ jobs:
|
|||
- name: Upload agent
|
||||
if: always()
|
||||
continue-on-error:
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0.
|
||||
with:
|
||||
name: agent-
|
||||
path: |
|
||||
|
|
@ -784,12 +784,12 @@ jobs:
|
|||
total_count: ${{ steps.missing_tool.outputs.total_count }}
|
||||
steps:
|
||||
- name: Setup
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.45.
|
||||
with:
|
||||
destination: /opt/gh-aw/
|
||||
- name: Download agent output
|
||||
continue-on-error:
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v6.0.
|
||||
with:
|
||||
name: agent-
|
||||
path: /tmp/gh-aw/safeoutputs/
|
||||
|
|
@ -800,7 +800,7 @@ jobs:
|
|||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
|
||||
- name: Process No-Op
|
||||
id:
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_NOOP_MAX:
|
||||
|
|
@ -816,7 +816,7 @@ jobs:
|
|||
await main();
|
||||
- name: Record Missing
|
||||
id:
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_WORKFLOW_NAME: "Daily Repo Status"
|
||||
|
|
@ -831,7 +831,7 @@ jobs:
|
|||
await main();
|
||||
- name: Handle Agent
|
||||
id:
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_WORKFLOW_NAME: "Daily Repo Status"
|
||||
|
|
@ -851,7 +851,7 @@ jobs:
|
|||
await main();
|
||||
- name: Handle No-Op
|
||||
id:
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_WORKFLOW_NAME: "Daily Repo Status"
|
||||
|
|
@ -881,18 +881,18 @@ jobs:
|
|||
success: ${{ steps.parse_results.outputs.success }}
|
||||
steps:
|
||||
- name: Setup
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.45.
|
||||
with:
|
||||
destination: /opt/gh-aw/
|
||||
- name: Download agent
|
||||
continue-on-error:
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v6.0.
|
||||
with:
|
||||
name: agent-
|
||||
path: /tmp/gh-aw/threat-detection/
|
||||
- name: Download agent output
|
||||
continue-on-error:
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v6.0.
|
||||
with:
|
||||
name: agent-
|
||||
path: /tmp/gh-aw/threat-detection/
|
||||
|
|
@ -902,7 +902,7 @@ jobs:
|
|||
run: |
|
||||
echo "Agent output-types: $AGENT_OUTPUT_TYPES"
|
||||
- name: Setup threat
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
WORKFLOW_NAME: "Daily Repo Status"
|
||||
WORKFLOW_DESCRIPTION: "This workflow creates daily repo status reports. It gathers recent repository\nactivity (issues, PRs, discussions, releases, code changes) and generates\nengaging GitHub issues with productivity insights, community highlights,\nand project recommendations."
|
||||
|
|
@ -955,7 +955,7 @@ jobs:
|
|||
XDG_CONFIG_HOME: /home/
|
||||
- name: Parse threat detection
|
||||
id:
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
|
||||
|
|
@ -964,7 +964,7 @@ jobs:
|
|||
await main();
|
||||
- name: Upload threat detection
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0.
|
||||
with:
|
||||
name: threat-detection.
|
||||
path: /tmp/gh-aw/threat-detection/detection.
|
||||
|
|
@ -993,12 +993,12 @@ jobs:
|
|||
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
|
||||
steps:
|
||||
- name: Setup
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.45.
|
||||
with:
|
||||
destination: /opt/gh-aw/
|
||||
- name: Download agent output
|
||||
continue-on-error:
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v6.0.
|
||||
with:
|
||||
name: agent-
|
||||
path: /tmp/gh-aw/safeoutputs/
|
||||
|
|
@ -1009,7 +1009,7 @@ jobs:
|
|||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
|
||||
- name: Process Safe
|
||||
id:
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"report\",\"daily-status\"],\"max\":1,\"title_prefix\":\"[repo-status] \"},\"missing_data\":{},\"missing_tool\":{}}"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Login to Docker Registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ${{ secrets.DOCKER_REGISTRY }}
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
|
|
|
|||
|
|
@ -53,11 +53,11 @@ jobs:
|
|||
comment_repo: ""
|
||||
steps:
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.71.1
|
||||
with:
|
||||
destination: /opt/gh-aw/actions
|
||||
- name: Check workflow file timestamps
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_WORKFLOW_FILE: "issue-triage-agent.lock.yml"
|
||||
with:
|
||||
|
|
@ -91,7 +91,7 @@ jobs:
|
|||
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
|
||||
steps:
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.71.1
|
||||
with:
|
||||
destination: /opt/gh-aw/actions
|
||||
- name: Checkout repository
|
||||
|
|
@ -113,7 +113,7 @@ jobs:
|
|||
echo "Git configured with standard GitHub Actions identity"
|
||||
- name: Generate agentic run info
|
||||
id: generate_aw_info
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
|
@ -167,7 +167,7 @@ jobs:
|
|||
run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.18.0
|
||||
- name: Determine automatic lockdown mode for GitHub MCP Server
|
||||
id: determine-automatic-lockdown
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
|
||||
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
|
||||
|
|
@ -459,7 +459,7 @@ jobs:
|
|||
}
|
||||
GH_AW_MCP_CONFIG_EOF
|
||||
- name: Generate workflow overview
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs');
|
||||
|
|
@ -552,7 +552,7 @@ jobs:
|
|||
{{#runtime-import .github/workflows/issue-triage-agent.md}}
|
||||
GH_AW_PROMPT_EOF
|
||||
- name: Substitute placeholders
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
GH_AW_GITHUB_ACTOR: ${{ github.actor }}
|
||||
|
|
@ -582,7 +582,7 @@ jobs:
|
|||
}
|
||||
});
|
||||
- name: Interpolate variables and render templates
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
|
|
@ -661,7 +661,7 @@ jobs:
|
|||
bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
|
||||
- name: Redact secrets in logs
|
||||
if: always()
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
|
||||
|
|
@ -676,7 +676,7 @@ jobs:
|
|||
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload Safe Outputs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: safe-output
|
||||
path: ${{ env.GH_AW_SAFE_OUTPUTS }}
|
||||
|
|
@ -684,7 +684,7 @@ jobs:
|
|||
- name: Ingest agent output
|
||||
id: collect_output
|
||||
if: always()
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
|
||||
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com"
|
||||
|
|
@ -698,13 +698,13 @@ jobs:
|
|||
await main();
|
||||
- name: Upload sanitized agent output
|
||||
if: always() && env.GH_AW_AGENT_OUTPUT
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: agent-output
|
||||
path: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
if-no-files-found: warn
|
||||
- name: Upload engine output files
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: agent_outputs
|
||||
path: |
|
||||
|
|
@ -713,7 +713,7 @@ jobs:
|
|||
if-no-files-found: ignore
|
||||
- name: Parse agent logs for step summary
|
||||
if: always()
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
|
||||
with:
|
||||
|
|
@ -724,7 +724,7 @@ jobs:
|
|||
await main();
|
||||
- name: Parse MCP Gateway logs for step summary
|
||||
if: always()
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
|
||||
|
|
@ -749,7 +749,7 @@ jobs:
|
|||
- name: Upload agent artifacts
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: agent-artifacts
|
||||
path: |
|
||||
|
|
@ -780,12 +780,12 @@ jobs:
|
|||
total_count: ${{ steps.missing_tool.outputs.total_count }}
|
||||
steps:
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.71.1
|
||||
with:
|
||||
destination: /opt/gh-aw/actions
|
||||
- name: Download agent output artifact
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: agent-output
|
||||
path: /tmp/gh-aw/safeoutputs/
|
||||
|
|
@ -796,7 +796,7 @@ jobs:
|
|||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
|
||||
- name: Process No-Op Messages
|
||||
id: noop
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_NOOP_MAX: 1
|
||||
|
|
@ -812,7 +812,7 @@ jobs:
|
|||
await main();
|
||||
- name: Record Missing Tool
|
||||
id: missing_tool
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_WORKFLOW_NAME: "Issue Triage Agent"
|
||||
|
|
@ -827,7 +827,7 @@ jobs:
|
|||
await main();
|
||||
- name: Handle Agent Failure
|
||||
id: handle_agent_failure
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_WORKFLOW_NAME: "Issue Triage Agent"
|
||||
|
|
@ -846,7 +846,7 @@ jobs:
|
|||
await main();
|
||||
- name: Handle No-Op Message
|
||||
id: handle_noop_message
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_WORKFLOW_NAME: "Issue Triage Agent"
|
||||
|
|
@ -874,18 +874,18 @@ jobs:
|
|||
success: ${{ steps.parse_results.outputs.success }}
|
||||
steps:
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.71.1
|
||||
with:
|
||||
destination: /opt/gh-aw/actions
|
||||
- name: Download agent artifacts
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: agent-artifacts
|
||||
path: /tmp/gh-aw/threat-detection/
|
||||
- name: Download agent output artifact
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: agent-output
|
||||
path: /tmp/gh-aw/threat-detection/
|
||||
|
|
@ -895,7 +895,7 @@ jobs:
|
|||
run: |
|
||||
echo "Agent output-types: $AGENT_OUTPUT_TYPES"
|
||||
- name: Setup threat detection
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
WORKFLOW_NAME: "Issue Triage Agent"
|
||||
WORKFLOW_DESCRIPTION: "No description provided"
|
||||
|
|
@ -948,7 +948,7 @@ jobs:
|
|||
XDG_CONFIG_HOME: /home/runner
|
||||
- name: Parse threat detection results
|
||||
id: parse_results
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
|
||||
|
|
@ -957,7 +957,7 @@ jobs:
|
|||
await main();
|
||||
- name: Upload threat detection log
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: threat-detection.log
|
||||
path: /tmp/gh-aw/threat-detection/detection.log
|
||||
|
|
@ -987,12 +987,12 @@ jobs:
|
|||
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
|
||||
steps:
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0
|
||||
uses: github/gh-aw/actions/setup@f01a9d118afa6e306f3645ca31e43f4ea8fb4d22 # v0.71.1
|
||||
with:
|
||||
destination: /opt/gh-aw/actions
|
||||
- name: Download agent output artifact
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: agent-output
|
||||
path: /tmp/gh-aw/safeoutputs/
|
||||
|
|
@ -1003,7 +1003,7 @@ jobs:
|
|||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
|
||||
- name: Process Safe Outputs
|
||||
id: process_safe_outputs
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"allowed\":[\"bug\",\"feature\",\"enhancement\",\"documentation\",\"question\",\"help-wanted\",\"good-first-issue\"]},\"missing_data\":{},\"missing_tool\":{}}"
|
||||
|
|
|
|||
|
|
@ -17,16 +17,14 @@
|
|||
|
||||
name: "PR Merge Conflict Check"
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
schedule:
|
||||
- cron: '*/10 * * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: # added using https://github.com/step-security/secure-workflows
|
||||
contents: read
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
group: "gh-aw-${{ github.workflow }}"
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
|
|
@ -35,7 +33,7 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Conflict Check
|
||||
uses: eps1lon/actions-label-merge-conflict@v2.0.0
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.3
|
||||
with:
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
dirtyLabel: "status:has-conflicts"
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v5
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ jobs:
|
|||
npm run lint
|
||||
npm run test:unit
|
||||
|
||||
- uses: codecov/codecov-action@v4
|
||||
- uses: codecov/codecov-action@v6
|
||||
if: github.repository == 'apache/cloudstack'
|
||||
with:
|
||||
working-directory: ui
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public class VirtualMachineTO {
|
|||
|
||||
private long minRam;
|
||||
private long maxRam;
|
||||
private long requestedRam;
|
||||
private String hostName;
|
||||
private String arch;
|
||||
private String os;
|
||||
|
|
@ -207,15 +208,20 @@ public class VirtualMachineTO {
|
|||
return minRam;
|
||||
}
|
||||
|
||||
public void setRam(long minRam, long maxRam) {
|
||||
public void setRam(long minRam, long maxRam, long requestedRam) {
|
||||
this.minRam = minRam;
|
||||
this.maxRam = maxRam;
|
||||
this.requestedRam = requestedRam;
|
||||
}
|
||||
|
||||
public long getMaxRam() {
|
||||
return maxRam;
|
||||
}
|
||||
|
||||
public long getRequestedRam() {
|
||||
return requestedRam;
|
||||
}
|
||||
|
||||
public String getHostName() {
|
||||
return hostName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,17 +26,19 @@ public interface Investigator extends Adapter {
|
|||
* Returns if the vm is still alive.
|
||||
*
|
||||
* @param vm to work on.
|
||||
* @return true if vm is alive, otherwise false
|
||||
*/
|
||||
public boolean isVmAlive(VirtualMachine vm, Host host) throws UnknownVM;
|
||||
boolean isVmAlive(VirtualMachine vm, Host host) throws UnknownVM;
|
||||
|
||||
public Status isAgentAlive(Host agent);
|
||||
/**
|
||||
* Returns the agent status of the host.
|
||||
*
|
||||
* @param host
|
||||
* @return status of the host agent
|
||||
*/
|
||||
Status getHostAgentStatus(Host host);
|
||||
|
||||
class UnknownVM extends Exception {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,12 @@ public class ProvisionCertificateCmd extends BaseAsyncCmd {
|
|||
description = "Name of the CA service provider, otherwise the default configured provider plugin will be used")
|
||||
private String provider;
|
||||
|
||||
@Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN,
|
||||
description = "When true, uses SSH to re-provision the agent's certificate, bypassing the NIO agent connection. " +
|
||||
"Use this when agents are disconnected due to a CA change. Supported for KVM hosts and SystemVMs. Default is false",
|
||||
since = "4.23.0")
|
||||
private Boolean forced;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -79,6 +85,10 @@ public class ProvisionCertificateCmd extends BaseAsyncCmd {
|
|||
return provider;
|
||||
}
|
||||
|
||||
public boolean isForced() {
|
||||
return forced != null && forced;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -90,7 +100,7 @@ public class ProvisionCertificateCmd extends BaseAsyncCmd {
|
|||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to find host by ID: " + getHostId());
|
||||
}
|
||||
|
||||
boolean result = caManager.provisionCertificate(host, getReconnect(), getProvider());
|
||||
boolean result = caManager.provisionCertificate(host, getReconnect(), getProvider(), isForced());
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
response.setSuccess(result);
|
||||
setResponseObject(response);
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ public final class ConfigureHAForHostCmd extends BaseAsyncCmd {
|
|||
final HostHAResponse response = new HostHAResponse();
|
||||
response.setId(resourceUuid);
|
||||
response.setProvider(getHaProvider().toLowerCase());
|
||||
response.setStatus(result);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import org.apache.cloudstack.api.ApiConstants;
|
|||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.UserResponse;
|
||||
|
||||
import org.apache.cloudstack.api.ApiArgValidator;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.user.UserAccount;
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ public class GetUserCmd extends BaseCmd {
|
|||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.USER_API_KEY, type = CommandType.STRING, required = true, description = "API key of the user")
|
||||
@Parameter(name = ApiConstants.USER_API_KEY, type = CommandType.STRING, required = true, description = "API key of the user", validations = {ApiArgValidator.NotNullOrEmpty})
|
||||
private String apiKey;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import java.security.cert.X509Certificate;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.trilead.ssh2.Connection;
|
||||
|
||||
import org.apache.cloudstack.framework.ca.CAProvider;
|
||||
import org.apache.cloudstack.framework.ca.CAService;
|
||||
import org.apache.cloudstack.framework.ca.Certificate;
|
||||
|
|
@ -39,7 +41,10 @@ public interface CAManager extends CAService, Configurable, PluggableService {
|
|||
ConfigKey<String> CAProviderPlugin = new ConfigKey<>("Advanced", String.class,
|
||||
"ca.framework.provider.plugin",
|
||||
"root",
|
||||
"The CA provider plugin that is used for secure CloudStack management server-agent communication for encryption and authentication. Restart management server(s) when changed.", true);
|
||||
"The CA provider plugin used for CloudStack internal certificate management (MS-agent encryption and authentication). " +
|
||||
"The default 'root' provider auto-generates a CA on first startup, but also supports user-provided custom CA material " +
|
||||
"via the ca.plugin.root.private.key, ca.plugin.root.public.key, and ca.plugin.root.ca.certificate settings. " +
|
||||
"Restart management server(s) when changed.", false);
|
||||
|
||||
ConfigKey<Integer> CertKeySize = new ConfigKey<>("Advanced", Integer.class,
|
||||
"ca.framework.cert.keysize",
|
||||
|
|
@ -85,6 +90,12 @@ public interface CAManager extends CAService, Configurable, PluggableService {
|
|||
"The actual implementation will depend on the configured CA provider.",
|
||||
false);
|
||||
|
||||
ConfigKey<Boolean> CaInjectDefaultTruststore = new ConfigKey<>("Advanced", Boolean.class,
|
||||
"ca.framework.inject.default.truststore", "true",
|
||||
"When true, injects the CA provider's certificate into the JVM default truststore on management server startup. " +
|
||||
"This allows outgoing HTTPS connections from the management server to trust servers with certificates signed by the configured CA. " +
|
||||
"Restart management server(s) when changed.", false);
|
||||
|
||||
/**
|
||||
* Returns a list of available CA provider plugins
|
||||
* @return returns list of CAProvider
|
||||
|
|
@ -130,12 +141,26 @@ public interface CAManager extends CAService, Configurable, PluggableService {
|
|||
boolean revokeCertificate(final BigInteger certSerial, final String certCn, final String provider);
|
||||
|
||||
/**
|
||||
* Provisions certificate for given active and connected agent host
|
||||
* Provisions certificate for given agent host.
|
||||
* When forced=true, uses SSH to re-provision bypassing the NIO agent connection (for disconnected agents).
|
||||
* @param host
|
||||
* @param reconnect
|
||||
* @param provider
|
||||
* @param forced when true, provisions via SSH instead of NIO; supports KVM hosts and SystemVMs
|
||||
* @return returns success/failure as boolean
|
||||
*/
|
||||
boolean provisionCertificate(final Host host, final Boolean reconnect, final String provider);
|
||||
boolean provisionCertificate(final Host host, final Boolean reconnect, final String provider, final boolean forced);
|
||||
|
||||
/**
|
||||
* Provisions certificate for a KVM host using an existing SSH connection.
|
||||
* Runs keystore-setup to generate a CSR, issues a certificate, then runs keystore-cert-import.
|
||||
* Used during host discovery and for forced re-provisioning when the NIO agent is unreachable.
|
||||
* @param sshConnection active SSH connection to the KVM host
|
||||
* @param agentIp IP address of the KVM host agent
|
||||
* @param agentHostname hostname of the KVM host agent
|
||||
* @param caProvider optional CA provider plugin name (null uses default)
|
||||
*/
|
||||
void provisionCertificateViaSsh(Connection sshConnection, String agentIp, String agentHostname, String caProvider);
|
||||
|
||||
/**
|
||||
* Setups up a new keystore and generates CSR for a host
|
||||
|
|
|
|||
|
|
@ -722,17 +722,17 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>${cs.bcprov.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>${cs.bcprov.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bctls-jdk15on</artifactId>
|
||||
<artifactId>bctls-jdk18on</artifactId>
|
||||
<version>${cs.bcprov.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
@ -912,13 +912,13 @@
|
|||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<overWrite>false</overWrite>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<overWrite>false</overWrite>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</artifactItem>
|
||||
|
|
@ -942,7 +942,7 @@
|
|||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bctls-jdk15on</artifactId>
|
||||
<artifactId>bctls-jdk18on</artifactId>
|
||||
<overWrite>false</overWrite>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</artifactItem>
|
||||
|
|
@ -977,9 +977,9 @@
|
|||
<exclude>org.apache.tomcat.embed:tomcat-embed-core</exclude>
|
||||
<exclude>org.apache.geronimo.specs:geronimo-servlet_3.0_spec</exclude>
|
||||
<exclude>org.apache.geronimo.specs:geronimo-javamail_1.4_spec</exclude>
|
||||
<exclude>org.bouncycastle:bcprov-jdk15on</exclude>
|
||||
<exclude>org.bouncycastle:bcpkix-jdk15on</exclude>
|
||||
<exclude>org.bouncycastle:bctls-jdk15on</exclude>
|
||||
<exclude>org.bouncycastle:bcprov-jdk18on</exclude>
|
||||
<exclude>org.bouncycastle:bcpkix-jdk18on</exclude>
|
||||
<exclude>org.bouncycastle:bctls-jdk18on</exclude>
|
||||
<exclude>com.mysql:mysql-connector-j</exclude>
|
||||
<exclude>org.apache.cloudstack:cloud-plugin-storage-volume-storpool</exclude>
|
||||
<exclude>org.apache.cloudstack:cloud-plugin-storage-volume-linstor</exclude>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ public class CheckOnHostAnswer extends Answer {
|
|||
|
||||
public CheckOnHostAnswer(CheckOnHostCommand cmd, String details) {
|
||||
super(cmd, false, details);
|
||||
determined = false;
|
||||
alive = false;
|
||||
}
|
||||
|
||||
public boolean isDetermined() {
|
||||
|
|
@ -47,5 +49,4 @@ public class CheckOnHostAnswer extends Answer {
|
|||
public boolean isAlive() {
|
||||
return alive;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import com.cloud.host.Host;
|
|||
|
||||
public class CheckOnHostCommand extends Command {
|
||||
HostTO host;
|
||||
boolean reportCheckFailureIfOneStorageIsDown;
|
||||
boolean reportIfHeartBeatFailedForOneStoragePool;
|
||||
|
||||
protected CheckOnHostCommand() {
|
||||
}
|
||||
|
|
@ -34,17 +34,17 @@ public class CheckOnHostCommand extends Command {
|
|||
setWait(20);
|
||||
}
|
||||
|
||||
public CheckOnHostCommand(Host host, boolean reportCheckFailureIfOneStorageIsDown) {
|
||||
public CheckOnHostCommand(Host host, boolean reportIfHeartBeatFailedForOneStoragePool) {
|
||||
this(host);
|
||||
this.reportCheckFailureIfOneStorageIsDown = reportCheckFailureIfOneStorageIsDown;
|
||||
this.reportIfHeartBeatFailedForOneStoragePool = reportIfHeartBeatFailedForOneStoragePool;
|
||||
}
|
||||
|
||||
public HostTO getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public boolean isCheckFailedOnOneStorage() {
|
||||
return reportCheckFailureIfOneStorageIsDown;
|
||||
public boolean shouldReportIfHeartBeatFailedForOneStoragePool() {
|
||||
return reportIfHeartBeatFailedForOneStoragePool;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public class ScaleVmCommand extends Command {
|
|||
Integer maxSpeed;
|
||||
long minRam;
|
||||
long maxRam;
|
||||
private boolean limitCpuUseChange;
|
||||
|
||||
public VirtualMachineTO getVm() {
|
||||
return vm;
|
||||
|
|
@ -43,7 +44,7 @@ public class ScaleVmCommand extends Command {
|
|||
return cpus;
|
||||
}
|
||||
|
||||
public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer maxSpeed, long minRam, long maxRam, boolean limitCpuUse) {
|
||||
public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer maxSpeed, long minRam, long maxRam, boolean limitCpuUse, Double cpuQuotaPercentage, boolean limitCpuUseChange) {
|
||||
super();
|
||||
this.vmName = vmName;
|
||||
this.cpus = cpus;
|
||||
|
|
@ -52,6 +53,8 @@ public class ScaleVmCommand extends Command {
|
|||
this.minRam = minRam;
|
||||
this.maxRam = maxRam;
|
||||
this.vm = new VirtualMachineTO(1L, vmName, null, cpus, minSpeed, maxSpeed, minRam, maxRam, null, null, false, limitCpuUse, null);
|
||||
this.vm.setCpuQuotaPercentage(cpuQuotaPercentage);
|
||||
this.limitCpuUseChange = limitCpuUseChange;
|
||||
}
|
||||
|
||||
public void setCpus(int cpus) {
|
||||
|
|
@ -102,6 +105,10 @@ public class ScaleVmCommand extends Command {
|
|||
return vm;
|
||||
}
|
||||
|
||||
public boolean getLimitCpuUseChange() {
|
||||
return limitCpuUseChange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -2,14 +2,20 @@ cloudstack (4.23.0.0-SNAPSHOT) unstable; urgency=low
|
|||
|
||||
* Update the version to 4.23.0.0-SNAPSHOT
|
||||
|
||||
-- the Apache CloudStack project <dev@cloudstack.apache.org> Fri, 22 May 2026 10:20:00 -0300
|
||||
|
||||
cloudstack (4.22.1.0) unstable; urgency=low
|
||||
|
||||
* Update the version to 4.22.1.0
|
||||
|
||||
-- the Apache CloudStack project <dev@cloudstack.apache.org> Mon, 11 May 2026 20:26:07 +0530
|
||||
|
||||
cloudstack (4.22.0.0) unstable; urgency=low
|
||||
|
||||
* Update the version to 4.22.0.0
|
||||
|
||||
-- the Apache CloudStack project <dev@cloudstack.apache.org> Thu, 30 Oct 2025 19:23:55 +0530
|
||||
|
||||
cloudstack (4.23.0.0-SNAPSHOT-SNAPSHOT) unstable; urgency=low
|
||||
|
||||
* Update the version to 4.23.0.0-SNAPSHOT-SNAPSHOT
|
||||
|
||||
-- the Apache CloudStack project <dev@cloudstack.apache.org> Thu, Aug 28 11:58:36 2025 +0530
|
||||
|
||||
cloudstack (4.21.0.0) unstable; urgency=low
|
||||
|
||||
* Update the version to 4.21.0.0
|
||||
|
|
|
|||
|
|
@ -133,6 +133,20 @@ public interface CapacityManager {
|
|||
"capacity.calculate.workers", "1",
|
||||
"Number of worker threads to be used for capacities calculation", true);
|
||||
|
||||
ConfigKey<Integer> KvmMemoryDynamicScalingCapacity = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED,
|
||||
Integer.class, "kvm.memory.dynamic.scaling.capacity", "0",
|
||||
"Defines the maximum memory capacity in MiB for which VMs can be dynamically scaled to with KVM. " +
|
||||
"The 'kvm.memory.dynamic.scaling.capacity' setting's value will be used to define the value of the " +
|
||||
"'<maxMemory />' element of domain XMLs. If it is set to a value less than or equal to '0', then the host's memory capacity will be considered.",
|
||||
true, ConfigKey.Scope.Cluster);
|
||||
|
||||
ConfigKey<Integer> KvmCpuDynamicScalingCapacity = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED,
|
||||
Integer.class, "kvm.cpu.dynamic.scaling.capacity", "0",
|
||||
"Defines the maximum vCPU capacity for which VMs can be dynamically scaled to with KVM. " +
|
||||
"The 'kvm.cpu.dynamic.scaling.capacity' setting's value will be used to define the value of the " +
|
||||
"'<vcpu />' element of domain XMLs. If it is set to a value less than or equal to '0', then the host's CPU cores capacity will be considered.",
|
||||
true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public boolean releaseVmCapacity(VirtualMachine vm, boolean moveFromReserved, boolean moveToReservered, Long hostId);
|
||||
|
||||
void allocateVmCapacity(VirtualMachine vm, boolean fromLastHost);
|
||||
|
|
|
|||
|
|
@ -75,10 +75,10 @@ public interface HighAvailabilityManager extends Manager {
|
|||
+ " which are registered for the HA event that were successful and are now ready to be purged.",
|
||||
true, Cluster);
|
||||
|
||||
public static final ConfigKey<Boolean> KvmHAFenceHostIfHeartbeatFailsOnStorage = new ConfigKey<>("Advanced", Boolean.class, "kvm.ha.fence.on.storage.heartbeat.failure", "false",
|
||||
ConfigKey<Boolean> KvmHAFenceHostIfHeartbeatFailsOnStorage = new ConfigKey<>("Advanced", Boolean.class, "kvm.ha.fence.on.storage.heartbeat.failure", "false",
|
||||
"Proceed fencing the host even the heartbeat failed for only one storage pool", false, ConfigKey.Scope.Zone);
|
||||
|
||||
public enum WorkType {
|
||||
enum WorkType {
|
||||
Migration, // Migrating VMs off of a host.
|
||||
Stop, // Stops a VM for storage pool migration purposes. This should be obsolete now.
|
||||
CheckStop, // Checks if a VM has been stopped.
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import javax.inject.Inject;
|
|||
import javax.naming.ConfigurationException;
|
||||
import javax.persistence.EntityExistsException;
|
||||
|
||||
import com.cloud.hypervisor.KVMGuru;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
|
|
@ -5162,7 +5163,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
try {
|
||||
result = retrieveResultFromJobOutcomeAndThrowExceptionIfNeeded(outcome);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Unhandled exception", ex);
|
||||
throw new RuntimeException("Unable to reconfigure VM.", ex);
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
|
|
@ -5175,22 +5176,29 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
|
||||
private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, ServiceOffering newServiceOffering,
|
||||
boolean reconfiguringOnExistingHost) throws ResourceUnavailableException, ConcurrentOperationException {
|
||||
final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
|
||||
VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
|
||||
|
||||
HostVO hostVo = _hostDao.findById(vm.getHostId());
|
||||
|
||||
Long clustedId = hostVo.getClusterId();
|
||||
Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clustedId);
|
||||
Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clustedId);
|
||||
boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clustedId);
|
||||
boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clustedId);
|
||||
Long clusterId = hostVo.getClusterId();
|
||||
Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clusterId);
|
||||
Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clusterId);
|
||||
boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clusterId);
|
||||
boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clusterId);
|
||||
|
||||
int minMemory = (int)(newServiceOffering.getRamSize() / (divideMemoryByOverprovisioning ? memoryOvercommitRatio : 1));
|
||||
int minSpeed = (int)(newServiceOffering.getSpeed() / (divideCpuByOverprovisioning ? cpuOvercommitRatio : 1));
|
||||
|
||||
ScaleVmCommand scaleVmCommand =
|
||||
new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed,
|
||||
newServiceOffering.getSpeed(), minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L, newServiceOffering.getLimitCpuUse());
|
||||
Double cpuQuotaPercentage = null;
|
||||
if (newServiceOffering.getLimitCpuUse() && vm.getHypervisorType().equals(HypervisorType.KVM)) {
|
||||
KVMGuru kvmGuru = (KVMGuru) _hvGuruMgr.getGuru(vm.getHypervisorType());
|
||||
cpuQuotaPercentage = kvmGuru.getCpuQuotaPercentage(minSpeed, hostVo.getSpeed());
|
||||
}
|
||||
|
||||
boolean limitCpuUseChange = oldServiceOffering.getLimitCpuUse() != newServiceOffering.getLimitCpuUse();
|
||||
ScaleVmCommand scaleVmCommand = new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed, newServiceOffering.getSpeed(),
|
||||
minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L,
|
||||
newServiceOffering.getLimitCpuUse(), cpuQuotaPercentage, limitCpuUseChange);
|
||||
|
||||
scaleVmCommand.getVirtualMachine().setId(vm.getId());
|
||||
scaleVmCommand.getVirtualMachine().setUuid(vm.getUuid());
|
||||
|
|
@ -5219,16 +5227,20 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
|
||||
}
|
||||
|
||||
upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
|
||||
if (reconfiguringOnExistingHost) {
|
||||
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId());
|
||||
}
|
||||
|
||||
boolean vmUpgraded = upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
|
||||
if (vmUpgraded) {
|
||||
vm = _vmDao.findById(vm.getId());
|
||||
}
|
||||
|
||||
if (vm.getType().equals(VirtualMachine.Type.User)) {
|
||||
_userVmMgr.generateUsageEvent(vm, vm.isDisplayVm(), EventTypes.EVENT_VM_DYNAMIC_SCALE);
|
||||
}
|
||||
|
||||
if (reconfiguringOnExistingHost) {
|
||||
vm.setServiceOfferingId(oldServiceOffering.getId());
|
||||
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId());
|
||||
vm.setServiceOfferingId(newServiceOffering.getId());
|
||||
_capacityMgr.allocateVmCapacity(vm, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,21 @@ package com.cloud.network.dao;
|
|||
import com.cloud.network.vo.PublicIpQuarantineVO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface PublicIpQuarantineDao extends GenericDao<PublicIpQuarantineVO, Long> {
|
||||
|
||||
PublicIpQuarantineVO findByPublicIpAddressId(long publicIpAddressId);
|
||||
|
||||
PublicIpQuarantineVO findByIpAddress(String publicIpAddress);
|
||||
|
||||
/**
|
||||
* Returns a list of public IP addresses that are actively quarantined at the specified date and the previous owner differs from the specified user.
|
||||
*
|
||||
* @param userId used to check against the IP's previous owner;
|
||||
* @param date used to check if the quarantine is active;
|
||||
* @return a list of PublicIpQuarantineVOs.
|
||||
*/
|
||||
List<PublicIpQuarantineVO> listQuarantinedIpAddressesToUser(Long userId, Date date);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import org.springframework.stereotype.Component;
|
|||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class PublicIpQuarantineDaoImpl extends GenericDaoBase<PublicIpQuarantineVO, Long> implements PublicIpQuarantineDao {
|
||||
|
|
@ -33,6 +35,8 @@ public class PublicIpQuarantineDaoImpl extends GenericDaoBase<PublicIpQuarantine
|
|||
|
||||
private SearchBuilder<IPAddressVO> ipAddressSearchBuilder;
|
||||
|
||||
private SearchBuilder<PublicIpQuarantineVO> quarantinedIpAddressesSearch;
|
||||
|
||||
@Inject
|
||||
IPAddressDao ipAddressDao;
|
||||
|
||||
|
|
@ -47,8 +51,16 @@ public class PublicIpQuarantineDaoImpl extends GenericDaoBase<PublicIpQuarantine
|
|||
publicIpAddressByIdSearch.join("quarantineJoin", ipAddressSearchBuilder, ipAddressSearchBuilder.entity().getId(),
|
||||
publicIpAddressByIdSearch.entity().getPublicIpAddressId(), JoinBuilder.JoinType.INNER);
|
||||
|
||||
quarantinedIpAddressesSearch = createSearchBuilder();
|
||||
quarantinedIpAddressesSearch.and("previousOwnerId", quarantinedIpAddressesSearch.entity().getPreviousOwnerId(), SearchCriteria.Op.NEQ);
|
||||
quarantinedIpAddressesSearch.and();
|
||||
quarantinedIpAddressesSearch.op("removedIsNull", quarantinedIpAddressesSearch.entity().getRemoved(), SearchCriteria.Op.NULL);
|
||||
quarantinedIpAddressesSearch.and("endDate", quarantinedIpAddressesSearch.entity().getEndDate(), SearchCriteria.Op.GT);
|
||||
quarantinedIpAddressesSearch.cp();
|
||||
|
||||
ipAddressSearchBuilder.done();
|
||||
publicIpAddressByIdSearch.done();
|
||||
quarantinedIpAddressesSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -68,4 +80,14 @@ public class PublicIpQuarantineDaoImpl extends GenericDaoBase<PublicIpQuarantine
|
|||
|
||||
return findOneBy(sc, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PublicIpQuarantineVO> listQuarantinedIpAddressesToUser(Long userId, Date date) {
|
||||
SearchCriteria<PublicIpQuarantineVO> sc = quarantinedIpAddressesSearch.create();
|
||||
|
||||
sc.setParameters("previousOwnerId", userId);
|
||||
sc.setParameters("endDate", date);
|
||||
|
||||
return searchIncludingRemoved(sc, null, false, false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1073,7 +1073,7 @@ public class SystemVmTemplateRegistration {
|
|||
}
|
||||
Hypervisor.HypervisorType hypervisorType = templateDetails.getHypervisorType();
|
||||
updateSystemVMEntries(templateId, hypervisorType);
|
||||
updateConfigurationParams(hypervisorType, templateDetails.getName(), zoneId);
|
||||
updateConfigurationParams(hypervisorType, templateVO.getName(), zoneId);
|
||||
}
|
||||
|
||||
protected void updateTemplateUrlChecksumAndGuestOsId(VMTemplateVO templateVO,
|
||||
|
|
|
|||
|
|
@ -132,6 +132,24 @@ CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_tariff_usage` (
|
|||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.networks', 'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc', 'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
|
||||
|
||||
-- Creates the 'kvm.memory.dynamic.scaling.capacity' and, for already active ACS environments,
|
||||
-- initializes it with the value of the setting 'vm.serviceoffering.ram.size.max'
|
||||
INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, `subgroup_id`, `display_text`, `description`)
|
||||
SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 'kvm.memory.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27,
|
||||
'KVM memory dynamic scaling capacity', 'Defines the maximum memory capacity in MiB for which VMs can be dynamically scaled to with KVM. The ''kvm.memory.dynamic.scaling.capacity'' setting''s value will be used to define the value of the ''<maxMemory />'' element of domain XMLs. If it is set to a value less than or equal to ''0'', then the host''s memory capacity will be considered.'
|
||||
FROM `cloud`.`configuration` `cfg`
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 'kvm.memory.dynamic.scaling.capacity')
|
||||
AND `cfg`.`name` = 'vm.serviceoffering.ram.size.max';
|
||||
|
||||
-- Creates the 'kvm.cpu.dynamic.scaling.capacity' and, for already active ACS environments,
|
||||
-- initializes it with the value of the setting 'vm.serviceoffering.cpu.cores.max'
|
||||
INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, `subgroup_id`, `display_text`, `description`)
|
||||
SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 'kvm.cpu.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27,
|
||||
'KVM CPU dynamic scaling capacity', 'Defines the maximum vCPU capacity for which VMs can be dynamically scaled to with KVM. The ''kvm.cpu.dynamic.scaling.capacity'' setting''s value will be used to define the value of the ''<vcpu />'' element of domain XMLs. If it is set to a value less than or equal to ''0'', then the host''s CPU cores capacity will be considered.'
|
||||
FROM `cloud`.`configuration` `cfg`
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 'kvm.cpu.dynamic.scaling.capacity')
|
||||
AND `cfg`.`name` = 'vm.serviceoffering.cpu.cores.max';
|
||||
|
||||
-- ======================================================================
|
||||
-- DNS Framework Schema
|
||||
-- ======================================================================
|
||||
|
|
|
|||
|
|
@ -570,18 +570,19 @@ public class SystemVmTemplateRegistrationTest {
|
|||
SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails =
|
||||
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
|
||||
VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class);
|
||||
String templateName = "templateName";
|
||||
when(templateVO.getName()).thenReturn(templateName);
|
||||
GuestOSVO guestOS = Mockito.mock(GuestOSVO.class);
|
||||
|
||||
when(templateDetails.getGuestOs()).thenReturn("Debian");
|
||||
when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
|
||||
when(templateDetails.getName()).thenReturn("templateName");
|
||||
when(vmTemplateDao.findById(templateId)).thenReturn(templateVO);
|
||||
when(guestOSDao.findOneByDisplayName("Debian")).thenReturn(guestOS);
|
||||
when(guestOS.getId()).thenReturn(10L);
|
||||
when(vmTemplateDao.update(templateVO.getId(), templateVO)).thenReturn(true);
|
||||
doNothing().when(systemVmTemplateRegistration).updateSystemVMEntries(templateId, Hypervisor.HypervisorType.KVM);
|
||||
doNothing().when(systemVmTemplateRegistration).updateConfigurationParams(Hypervisor.HypervisorType.KVM,
|
||||
"templateName", zoneId);
|
||||
templateName, zoneId);
|
||||
|
||||
systemVmTemplateRegistration.updateRegisteredTemplateDetails(templateId, templateDetails, zoneId);
|
||||
|
||||
|
|
@ -590,7 +591,7 @@ public class SystemVmTemplateRegistrationTest {
|
|||
verify(vmTemplateDao).update(templateVO.getId(), templateVO);
|
||||
verify(systemVmTemplateRegistration).updateSystemVMEntries(templateId, Hypervisor.HypervisorType.KVM);
|
||||
verify(systemVmTemplateRegistration).updateConfigurationParams(Hypervisor.HypervisorType.KVM,
|
||||
"templateName", zoneId);
|
||||
templateName, zoneId);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -620,16 +621,17 @@ public class SystemVmTemplateRegistrationTest {
|
|||
SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails =
|
||||
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
|
||||
VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class);
|
||||
String templateName = "templateName";
|
||||
when(templateVO.getName()).thenReturn(templateName);
|
||||
|
||||
when(templateDetails.getGuestOs()).thenReturn("NonExistentOS");
|
||||
when(templateDetails.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
|
||||
when(templateDetails.getName()).thenReturn("templateName");
|
||||
when(vmTemplateDao.findById(templateId)).thenReturn(templateVO);
|
||||
when(guestOSDao.findOneByDisplayName("NonExistentOS")).thenReturn(null);
|
||||
when(vmTemplateDao.update(templateVO.getId(), templateVO)).thenReturn(true);
|
||||
doNothing().when(systemVmTemplateRegistration).updateSystemVMEntries(templateId, Hypervisor.HypervisorType.KVM);
|
||||
doNothing().when(systemVmTemplateRegistration).updateConfigurationParams(Hypervisor.HypervisorType.KVM,
|
||||
"templateName", zoneId);
|
||||
templateName, zoneId);
|
||||
|
||||
systemVmTemplateRegistration.updateRegisteredTemplateDetails(templateId, templateDetails, zoneId);
|
||||
|
||||
|
|
@ -637,7 +639,7 @@ public class SystemVmTemplateRegistrationTest {
|
|||
verify(vmTemplateDao).update(templateVO.getId(), templateVO);
|
||||
verify(systemVmTemplateRegistration).updateSystemVMEntries(templateId, Hypervisor.HypervisorType.KVM);
|
||||
verify(systemVmTemplateRegistration).updateConfigurationParams(Hypervisor.HypervisorType.KVM,
|
||||
"templateName", zoneId);
|
||||
templateName, zoneId);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -395,9 +395,9 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
}
|
||||
|
||||
// Find out if the volume is at state of download_in_progress on secondary storage
|
||||
VolumeDataStoreVO volumeStore = _volumeStoreDao.findByVolume(volume.getId());
|
||||
if (volumeStore != null) {
|
||||
if (volumeStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS) {
|
||||
VolumeDataStoreVO volumeOnImageStore = _volumeStoreDao.findByVolume(volume.getId());
|
||||
if (volumeOnImageStore != null) {
|
||||
if (volumeOnImageStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS) {
|
||||
String msg = String.format("Volume: %s is currently being uploaded; can't delete it.", volume);
|
||||
logger.debug(msg);
|
||||
result.setSuccess(false);
|
||||
|
|
@ -416,10 +416,10 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
|
||||
if (!volumeExistsOnPrimary(vol)) {
|
||||
// not created on primary store
|
||||
if (volumeStore == null) {
|
||||
if (volumeOnImageStore == null) {
|
||||
// also not created on secondary store
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Marking volume that was never created as destroyed: " + vol);
|
||||
logger.debug("Marking volume that was never created as destroyed: {}", vol);
|
||||
}
|
||||
VMTemplateVO template = templateDao.findById(vol.getTemplateId());
|
||||
if (template != null && !template.isDeployAsIs()) {
|
||||
|
|
@ -435,11 +435,21 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
if (volume.getDataStore().getRole() == DataStoreRole.Image) {
|
||||
// no need to change state in volumes table
|
||||
volume.processEventOnly(Event.DestroyRequested);
|
||||
if (volumeOnImageStore == null) {
|
||||
logger.debug("Volume {} doesn't exist on image store, no need to delete", vol);
|
||||
future.complete(result);
|
||||
return future;
|
||||
}
|
||||
} else if (volume.getDataStore().getRole() == DataStoreRole.Primary) {
|
||||
if (vol.getState() == Volume.State.Expunging) {
|
||||
logger.info("Volume {} is already in Expunging, retrying", volume);
|
||||
}
|
||||
volume.processEvent(Event.ExpungeRequested);
|
||||
if (!volumeExistsOnPrimary(vol)) {
|
||||
logger.debug("Volume {} doesn't exist on primary storage, no need to delete", vol);
|
||||
future.complete(result);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
DeleteVolumeContext<VolumeApiResult> context = new DeleteVolumeContext<>(null, vo, future);
|
||||
|
|
@ -460,13 +470,11 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
|
||||
private boolean volumeExistsOnPrimary(VolumeVO vol) {
|
||||
Long poolId = vol.getPoolId();
|
||||
|
||||
if (poolId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PrimaryDataStore primaryStore = dataStoreMgr.getPrimaryDataStore(poolId);
|
||||
|
||||
if (primaryStore == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -476,8 +484,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
}
|
||||
|
||||
String volumePath = vol.getPath();
|
||||
|
||||
if (volumePath == null || volumePath.trim().isEmpty()) {
|
||||
if (StringUtils.isBlank(volumePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1663,14 +1663,14 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
public List<String> getExtensionReservedResourceDetails(long extensionId) {
|
||||
ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(extensionId,
|
||||
ApiConstants.RESERVED_RESOURCE_DETAILS);
|
||||
if (detailsVO == null || !StringUtils.isNotBlank(detailsVO.getValue())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> reservedDetails = new ArrayList<>();
|
||||
String[] parts = detailsVO.getValue().split(",");
|
||||
for (String part : parts) {
|
||||
if (StringUtils.isNotBlank(part)) {
|
||||
reservedDetails.add(part.trim());
|
||||
if (detailsVO != null && StringUtils.isNotBlank(detailsVO.getValue())) {
|
||||
String[] parts = detailsVO.getValue().split(",");
|
||||
for (String part : parts) {
|
||||
String trimmedPart = part.trim();
|
||||
if (StringUtils.isNotBlank(trimmedPart)) {
|
||||
reservedDetails.add(trimmedPart);
|
||||
}
|
||||
}
|
||||
}
|
||||
addInbuiltExtensionReservedResourceDetails(extensionId, reservedDetails);
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ Requires: ipset
|
|||
Requires: perl
|
||||
Requires: rsync
|
||||
Requires: cifs-utils
|
||||
Requires: edk2-ovmf
|
||||
Requires: (edk2-ovmf or qemu-ovmf-x86_64)
|
||||
Requires: swtpm
|
||||
Requires: (python3-libvirt or python3-libvirt-python)
|
||||
Requires: (qemu-img or qemu-tools)
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
el8
|
||||
|
|
@ -0,0 +1 @@
|
|||
../el8/cloud-ipallocator.rc
|
||||
|
|
@ -0,0 +1 @@
|
|||
../el8/cloud.limits
|
||||
|
|
@ -0,0 +1,750 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
%define __os_install_post %{nil}
|
||||
%global debug_package %{nil}
|
||||
%global __requires_exclude libc\\.so\\..*|libc\\.so\\.6\\(GLIBC_.*\\)
|
||||
%define _binaries_in_noarch_packages_terminate_build 0
|
||||
|
||||
# DISABLE the post-percentinstall java repacking and line number stripping
|
||||
# we need to find a way to just disable the java repacking and line number stripping, but not the autodeps
|
||||
|
||||
Name: cloudstack
|
||||
Summary: CloudStack IaaS Platform
|
||||
#http://fedoraproject.org/wiki/PackageNamingGuidelines#Pre-Release_packages
|
||||
%define _maventag %{_fullver}
|
||||
Release: %{_rel}
|
||||
|
||||
Version: %{_ver}
|
||||
License: ASL 2.0
|
||||
Vendor: Apache CloudStack <dev@cloudstack.apache.org>
|
||||
Packager: Apache CloudStack <dev@cloudstack.apache.org>
|
||||
Group: System Environment/Libraries
|
||||
# FIXME do groups for every single one of the subpackages
|
||||
Source0: %{name}-%{_maventag}.tgz
|
||||
BuildRoot: %{_tmppath}/%{name}-%{_maventag}-%{release}-build
|
||||
BuildArch: noarch
|
||||
|
||||
BuildRequires: (java-11-openjdk-devel or java-17-openjdk-devel or java-21-openjdk-devel)
|
||||
#BuildRequires: ws-commons-util
|
||||
BuildRequires: jpackage-utils
|
||||
BuildRequires: gcc
|
||||
BuildRequires: glibc-devel
|
||||
BuildRequires: /usr/bin/mkisofs
|
||||
BuildRequires: python3-setuptools
|
||||
BuildRequires: wget
|
||||
BuildRequires: nodejs
|
||||
|
||||
%description
|
||||
CloudStack is a highly-scalable elastic, open source,
|
||||
intelligent IaaS cloud implementation.
|
||||
|
||||
%package management
|
||||
Summary: CloudStack management server UI
|
||||
Requires: (java-17-openjdk or java-21-openjdk)
|
||||
Requires: (tzdata-java or timezone-java)
|
||||
Requires: python3
|
||||
Requires: bash
|
||||
Requires: gawk
|
||||
Requires: which
|
||||
Requires: file
|
||||
Requires: tar
|
||||
Requires: bzip2
|
||||
Requires: gzip
|
||||
Requires: unzip
|
||||
Requires: (/sbin/mount.nfs or /usr/sbin/mount.nfs)
|
||||
Requires: (openssh-clients or openssh)
|
||||
Requires: (nfs-utils or nfs-client)
|
||||
Requires: iproute
|
||||
Requires: wget
|
||||
Requires: (mysql or mariadb or mysql8.4)
|
||||
Requires: sudo
|
||||
Requires: /sbin/service
|
||||
Requires: /sbin/chkconfig
|
||||
Requires: /usr/bin/ssh-keygen
|
||||
Requires: (genisoimage or mkisofs or xorrisofs)
|
||||
Requires: ipmitool
|
||||
Requires: %{name}-common = %{_ver}
|
||||
Requires: (iptables-services or iptables)
|
||||
Requires: rng-tools
|
||||
Requires: (qemu-img or qemu-tools)
|
||||
Requires: python3-pip
|
||||
Requires: python3-six
|
||||
Requires: python3-protobuf
|
||||
Requires: python3-setuptools
|
||||
Requires: (libgcrypt > 1.8.3 or libgcrypt20)
|
||||
Group: System Environment/Libraries
|
||||
%description management
|
||||
The CloudStack management server is the central point of coordination,
|
||||
management, and intelligence in CloudStack.
|
||||
|
||||
%package common
|
||||
Summary: Apache CloudStack common files and scripts
|
||||
Requires: python3
|
||||
Group: System Environment/Libraries
|
||||
%description common
|
||||
The Apache CloudStack files shared between agent and management server
|
||||
%global __requires_exclude libc\\.so\\..*|libc\\.so\\.6\\(GLIBC_.*\\)|^(libuuid\\.so\\.1|/usr/bin/python)$
|
||||
|
||||
%package agent
|
||||
Summary: CloudStack Agent for KVM hypervisors
|
||||
Requires: (openssh-clients or openssh)
|
||||
Requires: (java-17-openjdk or java-21-openjdk)
|
||||
Requires: (tzdata-java or timezone-java)
|
||||
Requires: %{name}-common = %{_ver}
|
||||
Requires: libvirt
|
||||
Requires: libvirt-daemon-driver-storage-rbd
|
||||
Requires: ebtables
|
||||
Requires: iptables
|
||||
Requires: ethtool
|
||||
Requires: (net-tools or net-tools-deprecated)
|
||||
Requires: iproute
|
||||
Requires: ipset
|
||||
Requires: perl
|
||||
Requires: rsync
|
||||
Requires: cifs-utils
|
||||
Requires: (edk2-ovmf or qemu-ovmf-x86_64)
|
||||
Requires: swtpm
|
||||
Requires: (python3-libvirt or python3-libvirt-python)
|
||||
Requires: (qemu-img or qemu-tools)
|
||||
Requires: qemu-kvm
|
||||
Requires: cryptsetup
|
||||
Requires: rng-tools
|
||||
Requires: (libgcrypt > 1.8.3 or libgcrypt20)
|
||||
Requires: (selinux-tools if selinux-tools)
|
||||
Requires: sysstat
|
||||
Provides: cloud-agent
|
||||
Group: System Environment/Libraries
|
||||
%description agent
|
||||
The CloudStack agent for KVM hypervisors
|
||||
|
||||
%package baremetal-agent
|
||||
Summary: CloudStack baremetal agent
|
||||
Requires: tftp-server
|
||||
Requires: xinetd
|
||||
Requires: syslinux
|
||||
Requires: chkconfig
|
||||
Requires: dhcp
|
||||
Requires: httpd
|
||||
Group: System Environment/Libraries
|
||||
%description baremetal-agent
|
||||
The CloudStack baremetal agent
|
||||
|
||||
%package usage
|
||||
Summary: CloudStack Usage calculation server
|
||||
Requires: (java-17-openjdk or java-21-openjdk)
|
||||
Requires: (tzdata-java or timezone-java)
|
||||
Group: System Environment/Libraries
|
||||
%description usage
|
||||
The CloudStack usage calculation service
|
||||
|
||||
%package ui
|
||||
Summary: CloudStack UI
|
||||
Group: System Environment/Libraries
|
||||
%description ui
|
||||
The CloudStack UI
|
||||
|
||||
%package marvin
|
||||
Summary: Apache CloudStack Marvin library
|
||||
Requires: python3-pip
|
||||
Requires: gcc
|
||||
Requires: python3-devel
|
||||
Requires: libffi-devel
|
||||
Requires: openssl-devel
|
||||
Group: System Environment/Libraries
|
||||
%description marvin
|
||||
Apache CloudStack Marvin library
|
||||
|
||||
%package integration-tests
|
||||
Summary: Apache CloudStack Marvin integration tests
|
||||
Requires: %{name}-marvin = %{_ver}
|
||||
Group: System Environment/Libraries
|
||||
%description integration-tests
|
||||
Apache CloudStack Marvin integration tests
|
||||
|
||||
%if "%{_ossnoss}" == "noredist"
|
||||
%package mysql-ha
|
||||
Summary: Apache CloudStack Balancing Strategy for MySQL
|
||||
Group: System Environmnet/Libraries
|
||||
%description mysql-ha
|
||||
Apache CloudStack Balancing Strategy for MySQL
|
||||
|
||||
%endif
|
||||
|
||||
%prep
|
||||
echo Doing CloudStack build
|
||||
|
||||
%setup -q -n %{name}-%{_maventag}
|
||||
|
||||
%build
|
||||
|
||||
cp packaging/suse15/replace.properties build/replace.properties
|
||||
echo VERSION=%{_maventag} >> build/replace.properties
|
||||
echo PACKAGE=%{name} >> build/replace.properties
|
||||
touch build/gitrev.txt
|
||||
echo $(git rev-parse HEAD) > build/gitrev.txt
|
||||
|
||||
if [ "%{_ossnoss}" == "NOREDIST" -o "%{_ossnoss}" == "noredist" ] ; then
|
||||
echo "Adding noredist flag to the maven build"
|
||||
FLAGS="$FLAGS -Dnoredist"
|
||||
fi
|
||||
|
||||
if [ "%{_sim}" == "SIMULATOR" -o "%{_sim}" == "simulator" ] ; then
|
||||
echo "Adding simulator flag to the maven build"
|
||||
FLAGS="$FLAGS -Dsimulator"
|
||||
fi
|
||||
|
||||
if [ \"%{_temp}\" != "" ]; then
|
||||
echo "Adding flags to package requested templates"
|
||||
FLAGS="$FLAGS `rpm --eval %{?_temp}`"
|
||||
fi
|
||||
|
||||
mvn -Psystemvm,developer $FLAGS clean package
|
||||
cd ui && npm install && npm run build && cd ..
|
||||
|
||||
%install
|
||||
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT}
|
||||
# Common directories
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_bindir}
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/agent
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/ipallocator
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/work
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/temp
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/mnt
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/management
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_initrddir}
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/default
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/profile.d
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d
|
||||
|
||||
# Common
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site
|
||||
mkdir -p ${RPM_BUILD_ROOT}/usr/bin
|
||||
cp -r scripts/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts
|
||||
install -D systemvm/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/
|
||||
install python/lib/cloud_utils.py ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site/cloud_utils.py
|
||||
cp -r python/lib/cloudutils ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site/
|
||||
python3 -m py_compile ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site/cloud_utils.py
|
||||
python3 -m compileall ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site/cloudutils
|
||||
cp build/gitrev.txt ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts
|
||||
cp packaging/suse15/cloudstack-sccs ${RPM_BUILD_ROOT}/usr/bin
|
||||
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts/network/cisco
|
||||
cp -r plugins/network-elements/cisco-vnmc/src/main/scripts/network/cisco/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts/network/cisco
|
||||
|
||||
# Management
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/cks/conf
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/run
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel
|
||||
|
||||
# Setup Jetty
|
||||
ln -sf /etc/%{name}/management ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/conf
|
||||
ln -sf /var/log/%{name}/management ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/logs
|
||||
|
||||
install -D client/target/utilities/bin/cloud-migrate-databases ${RPM_BUILD_ROOT}%{_bindir}/%{name}-migrate-databases
|
||||
install -D client/target/utilities/bin/cloud-set-guest-password ${RPM_BUILD_ROOT}%{_bindir}/%{name}-set-guest-password
|
||||
install -D client/target/utilities/bin/cloud-set-guest-sshkey ${RPM_BUILD_ROOT}%{_bindir}/%{name}-set-guest-sshkey
|
||||
install -D client/target/utilities/bin/cloud-setup-databases ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-databases
|
||||
install -D client/target/utilities/bin/cloud-setup-encryption ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-encryption
|
||||
install -D client/target/utilities/bin/cloud-setup-management ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-management
|
||||
install -D client/target/utilities/bin/cloud-setup-baremetal ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-baremetal
|
||||
install -D client/target/utilities/bin/cloud-sysvmadm ${RPM_BUILD_ROOT}%{_bindir}/%{name}-sysvmadm
|
||||
install -D client/target/utilities/bin/cloud-update-xenserver-licenses ${RPM_BUILD_ROOT}%{_bindir}/%{name}-update-xenserver-licenses
|
||||
# Bundle cmk in cloudstack-management
|
||||
wget https://github.com/apache/cloudstack-cloudmonkey/releases/latest/download/cmk.linux.x86-64 -O ${RPM_BUILD_ROOT}%{_bindir}/cmk
|
||||
chmod +x ${RPM_BUILD_ROOT}%{_bindir}/cmk
|
||||
|
||||
cp -r client/target/utilities/scripts/db/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup
|
||||
cp -r plugins/integrations/kubernetes-service/src/main/resources/conf/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/cks/conf
|
||||
cp -r client/target/cloud-client-ui-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/
|
||||
cp -r client/target/classes/META-INF/webapp ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapp
|
||||
cp ui/dist/config.json ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/
|
||||
cp -r ui/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapp/
|
||||
rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapp/config.json
|
||||
ln -sf /etc/%{name}/management/config.json ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapp/config.json
|
||||
mv ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/cloud-client-ui-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib/cloudstack-%{_maventag}.jar
|
||||
cp client/target/lib/*jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib/
|
||||
|
||||
# Don't package the scripts in the management webapp
|
||||
rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/scripts
|
||||
rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/vms
|
||||
|
||||
for name in db.properties server.properties log4j-cloud.xml environment.properties java.security.ciphers
|
||||
do
|
||||
cp client/target/conf/$name ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/$name
|
||||
done
|
||||
|
||||
ln -sf log4j-cloud.xml ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/log4j2.xml
|
||||
|
||||
install python/bindir/cloud-external-ipallocator.py ${RPM_BUILD_ROOT}%{_bindir}/%{name}-external-ipallocator.py
|
||||
install -D client/target/pythonlibs/jasypt-1.9.3.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
|
||||
install -D utils/target/cloud-utils-%{_maventag}-bundled.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/%{name}-utils.jar
|
||||
|
||||
install -D packaging/suse15/cloud-ipallocator.rc ${RPM_BUILD_ROOT}%{_initrddir}/%{name}-ipallocator
|
||||
install -D packaging/suse15/cloud.limits ${RPM_BUILD_ROOT}%{_sysconfdir}/security/limits.d/cloud
|
||||
install -D packaging/suse15/filelimit.conf ${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d
|
||||
install -D packaging/systemd/cloudstack-management.service ${RPM_BUILD_ROOT}%{_unitdir}/%{name}-management.service
|
||||
install -D packaging/systemd/cloudstack-management.default ${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-management
|
||||
install -D server/target/conf/cloudstack-sudoers ${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d/%{name}-management
|
||||
touch ${RPM_BUILD_ROOT}%{_localstatedir}/run/%{name}-management.pid
|
||||
#install -D server/target/conf/cloudstack-catalina.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-catalina
|
||||
install -D server/target/conf/cloudstack-management.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-management
|
||||
|
||||
install -D plugins/integrations/kubernetes-service/src/main/resources/conf/etcd-node.yml ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/cks/conf/etcd-node.yml
|
||||
install -D plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-control-node.yml ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/cks/conf/k8s-control-node.yml
|
||||
install -D plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-control-node-add.yml ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/cks/conf/k8s-control-node-add.yml
|
||||
install -D plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-node.yml ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/cks/conf/k8s-node.yml
|
||||
|
||||
# SystemVM template
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/templates/systemvm
|
||||
cp -r engine/schema/dist/systemvm-templates/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/templates/systemvm
|
||||
rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/templates/systemvm/sha512sum.txt
|
||||
|
||||
# Sample Extensions
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/extensions
|
||||
cp -r extensions/* ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/extensions
|
||||
ln -sf %{_sysconfdir}/%{name}/extensions ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/extensions
|
||||
|
||||
# UI
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/ui
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/
|
||||
cp -r client/target/classes/META-INF/webapp/WEB-INF ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui
|
||||
cp ui/dist/config.json ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/ui/
|
||||
cp -r ui/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/
|
||||
rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/config.json
|
||||
ln -sf /etc/%{name}/ui/config.json ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/config.json
|
||||
|
||||
# Package mysql-connector-python (bundled to avoid dependency on external community repo)
|
||||
# Version 8.0.31 is the last version supporting Python 3.6 (EL8)
|
||||
wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel https://files.pythonhosted.org/packages/08/1f/42d74bae9dd6dcfec67c9ed0f3fa482b1ae5ac5f117ca82ab589ecb3ca19/mysql_connector_python-8.0.31-py2.py3-none-any.whl
|
||||
# Version 8.3.0 supports Python 3.8 to 3.12 (EL9, EL10)
|
||||
wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel https://files.pythonhosted.org/packages/53/ed/26a4b8cacb8852c6fd97d2d58a7f2591c41989807ea82bd8d9725a4e6937/mysql_connector_python-8.3.0-py2.py3-none-any.whl
|
||||
|
||||
chmod 440 ${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d/%{name}-management
|
||||
chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/mnt
|
||||
chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/management
|
||||
chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/work
|
||||
chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/temp
|
||||
chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management
|
||||
chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/agent
|
||||
|
||||
# KVM Agent
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/agent
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/plugins
|
||||
install -D packaging/systemd/cloudstack-agent.service ${RPM_BUILD_ROOT}%{_unitdir}/%{name}-agent.service
|
||||
install -D packaging/systemd/cloudstack-rolling-maintenance@.service ${RPM_BUILD_ROOT}%{_unitdir}/%{name}-rolling-maintenance@.service
|
||||
install -D packaging/systemd/cloudstack-agent.default ${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-agent
|
||||
install -D agent/target/transformed/agent.properties ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/agent.properties
|
||||
install -D agent/target/transformed/uefi.properties ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/uefi.properties
|
||||
install -D agent/target/transformed/environment.properties ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/environment.properties
|
||||
install -D agent/target/transformed/log4j-cloud.xml ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/log4j-cloud.xml
|
||||
install -D agent/target/transformed/cloud-setup-agent ${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-agent
|
||||
install -D agent/target/transformed/cloudstack-agent-upgrade ${RPM_BUILD_ROOT}%{_bindir}/%{name}-agent-upgrade
|
||||
install -D agent/target/transformed/cloud-guest-tool ${RPM_BUILD_ROOT}%{_bindir}/%{name}-guest-tool
|
||||
install -D agent/target/transformed/libvirtqemuhook ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/libvirtqemuhook
|
||||
install -D agent/target/transformed/rolling-maintenance ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/rolling-maintenance
|
||||
install -D agent/target/transformed/cloud-ssh ${RPM_BUILD_ROOT}%{_bindir}/%{name}-ssh
|
||||
install -D agent/target/transformed/cloudstack-agent-profile.sh ${RPM_BUILD_ROOT}%{_sysconfdir}/profile.d/%{name}-agent-profile.sh
|
||||
install -D agent/target/transformed/cloudstack-agent.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-agent
|
||||
install -D plugins/hypervisors/kvm/target/cloud-plugin-hypervisor-kvm-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%name-agent/lib/cloud-plugin-hypervisor-kvm-%{_maventag}.jar
|
||||
cp plugins/hypervisors/kvm/target/dependencies/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib
|
||||
cp plugins/storage/volume/storpool/target/*.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib
|
||||
cp plugins/storage/volume/linstor/target/*.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib
|
||||
|
||||
# Usage server
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/usage
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-usage/lib
|
||||
install -D usage/target/cloud-usage-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-usage/cloud-usage-%{_maventag}.jar
|
||||
install -D usage/target/transformed/db.properties ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/usage/db.properties
|
||||
install -D usage/target/transformed/log4j-cloud_usage.xml ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/usage/log4j-cloud.xml
|
||||
cp usage/target/dependencies/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-usage/lib/
|
||||
cp client/target/lib/mysql*jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-usage/lib/
|
||||
install -D packaging/systemd/cloudstack-usage.service ${RPM_BUILD_ROOT}%{_unitdir}/%{name}-usage.service
|
||||
install -D packaging/systemd/cloudstack-usage.default ${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-usage
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/usage/
|
||||
install -D usage/target/transformed/cloudstack-usage.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-usage
|
||||
|
||||
# Marvin
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-marvin
|
||||
cp tools/marvin/dist/Marvin-*.tar.gz ${RPM_BUILD_ROOT}%{_datadir}/%{name}-marvin/
|
||||
|
||||
# integration-tests
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-integration-tests
|
||||
cp -r test/integration/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-integration-tests/
|
||||
|
||||
# MYSQL HA
|
||||
if [ "x%{_ossnoss}" == "xnoredist" ] ; then
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-mysql-ha/lib
|
||||
cp -r plugins/database/mysql-ha/target/cloud-plugin-database-mysqlha-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-mysql-ha/lib
|
||||
fi
|
||||
|
||||
#License files from whisker
|
||||
install -D tools/whisker/NOTICE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-management-%{version}/NOTICE
|
||||
install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-management-%{version}/LICENSE
|
||||
install -D tools/whisker/NOTICE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-common-%{version}/NOTICE
|
||||
install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-common-%{version}/LICENSE
|
||||
install -D tools/whisker/NOTICE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-agent-%{version}/NOTICE
|
||||
install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-agent-%{version}/LICENSE
|
||||
install -D tools/whisker/NOTICE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-usage-%{version}/NOTICE
|
||||
install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-usage-%{version}/LICENSE
|
||||
install -D tools/whisker/NOTICE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-ui-%{version}/NOTICE
|
||||
install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-ui-%{version}/LICENSE
|
||||
install -D tools/whisker/NOTICE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-marvin-%{version}/NOTICE
|
||||
install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-marvin-%{version}/LICENSE
|
||||
install -D tools/whisker/NOTICE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-integration-tests-%{version}/NOTICE
|
||||
install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-integration-tests-%{version}/LICENSE
|
||||
|
||||
%clean
|
||||
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT}
|
||||
|
||||
%posttrans common
|
||||
|
||||
unalias cp
|
||||
python_dir=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")
|
||||
if [ ! -z $python_dir ];then
|
||||
cp -f -r /usr/share/cloudstack-common/python-site/* $python_dir/
|
||||
fi
|
||||
|
||||
%preun management
|
||||
/usr/bin/systemctl stop cloudstack-management || true
|
||||
/usr/bin/systemctl disable cloudstack-management || true
|
||||
|
||||
%pre management
|
||||
id cloud > /dev/null 2>&1 || /usr/sbin/useradd -M -U -c "CloudStack unprivileged user" \
|
||||
-r -s /bin/sh -d %{_localstatedir}/cloudstack/management cloud || true
|
||||
|
||||
rm -rf %{_localstatedir}/cache/cloudstack
|
||||
|
||||
# in case of upgrade to 4.9+ copy commands.properties if not exists in /etc/cloudstack/management/
|
||||
if [ "$1" == "2" ] ; then
|
||||
if [ -f "%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/commands.properties" ] && [ ! -f "%{_sysconfdir}/%{name}/management/commands.properties" ] ; then
|
||||
cp -p %{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/commands.properties %{_sysconfdir}/%{name}/management/commands.properties
|
||||
fi
|
||||
fi
|
||||
|
||||
# Remove old tomcat symlinks and env config file
|
||||
if [ -L "%{_datadir}/%{name}-management/lib" ]
|
||||
then
|
||||
rm -f %{_datadir}/%{name}-management/bin
|
||||
rm -f %{_datadir}/%{name}-management/lib
|
||||
rm -f %{_datadir}/%{name}-management/temp
|
||||
rm -f %{_datadir}/%{name}-management/work
|
||||
rm -f %{_sysconfdir}/default/%{name}-management
|
||||
fi
|
||||
|
||||
%post management
|
||||
# Install mysql-connector-python wheel
|
||||
# Detect Python version to install compatible wheel
|
||||
if python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3, 7) else 1)'; then
|
||||
pip3 install %{_datadir}/%{name}-management/setup/wheel/mysql_connector_python-8.3.0-py2.py3-none-any.whl
|
||||
else
|
||||
pip3 install %{_datadir}/%{name}-management/setup/wheel/mysql_connector_python-8.0.31-py2.py3-none-any.whl
|
||||
fi
|
||||
|
||||
/usr/bin/systemctl enable cloudstack-management > /dev/null 2>&1 || true
|
||||
/usr/bin/systemctl enable --now rngd > /dev/null 2>&1 || true
|
||||
|
||||
grep -s -q "db.cloud.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e "\$adb.cloud.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "db.usage.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e "\$adb.usage.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "db.simulator.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e "\$adb.simulator.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
|
||||
# Update DB properties having master and slave(s), with source and replica(s) respectively (for inclusiveness)
|
||||
grep -s -q "^db.cloud.slaves=" "%{_sysconfdir}/%{name}/management/db.properties" && sed -i "s/^db.cloud.slaves=/db.cloud.replicas=/g" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "^db.cloud.secondsBeforeRetryMaster=" "%{_sysconfdir}/%{name}/management/db.properties" && sed -i "s/^db.cloud.secondsBeforeRetryMaster=/db.cloud.secondsBeforeRetrySource=/g" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "^db.cloud.queriesBeforeRetryMaster=" "%{_sysconfdir}/%{name}/management/db.properties" && sed -i "s/^db.cloud.queriesBeforeRetryMaster=/db.cloud.queriesBeforeRetrySource=/g" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "^db.usage.slaves=" "%{_sysconfdir}/%{name}/management/db.properties" && sed -i "s/^db.usage.slaves=/db.usage.replicas=/g" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "^db.usage.secondsBeforeRetryMaster=" "%{_sysconfdir}/%{name}/management/db.properties" && sed -i "s/^db.usage.secondsBeforeRetryMaster=/db.usage.secondsBeforeRetrySource=/g" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "^db.usage.queriesBeforeRetryMaster=" "%{_sysconfdir}/%{name}/management/db.properties" && sed -i "s/^db.usage.queriesBeforeRetryMaster=/db.usage.queriesBeforeRetrySource=/g" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
|
||||
if [ ! -f %{_datadir}/cloudstack-common/scripts/vm/hypervisor/xenserver/vhd-util ] ; then
|
||||
echo Please download vhd-util from http://download.cloudstack.org/tools/vhd-util and put it in
|
||||
echo %{_datadir}/cloudstack-common/scripts/vm/hypervisor/xenserver/
|
||||
fi
|
||||
|
||||
if [ -f %{_sysconfdir}/sysconfig/%{name}-management ] ; then
|
||||
rm -f %{_sysconfdir}/sysconfig/%{name}-management
|
||||
fi
|
||||
|
||||
chown -R cloud:cloud /var/log/cloudstack/management
|
||||
chown -R cloud:cloud /usr/share/cloudstack-management/templates
|
||||
find /usr/share/cloudstack-management/templates -type d -exec chmod 0770 {} \;
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
%posttrans management
|
||||
# Print help message
|
||||
if [ -f "/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text" ];then
|
||||
sed -i "s,^ACS_VERSION=.*,ACS_VERSION=%{_maventag},g" /usr/share/cloudstack-common/scripts/installer/cloudstack-help-text
|
||||
/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text management
|
||||
fi
|
||||
|
||||
%preun agent
|
||||
/sbin/service cloudstack-agent stop || true
|
||||
if [ "$1" == "0" ] ; then
|
||||
/sbin/chkconfig --del cloudstack-agent > /dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
%pre agent
|
||||
|
||||
# save old configs if they exist (for upgrade). Otherwise we may lose them
|
||||
# when the old packages are erased. There are a lot of properties files here.
|
||||
if [ -d "%{_sysconfdir}/cloud" ] ; then
|
||||
mv %{_sysconfdir}/cloud %{_sysconfdir}/cloud.rpmsave
|
||||
fi
|
||||
|
||||
%posttrans agent
|
||||
|
||||
if [ "$1" == "2" ] ; then
|
||||
echo "Running %{_bindir}/%{name}-agent-upgrade to update bridge name for upgrade from CloudStack 4.0.x (and before) to CloudStack 4.1 (and later)"
|
||||
%{_bindir}/%{name}-agent-upgrade
|
||||
fi
|
||||
if [ ! -d %{_sysconfdir}/libvirt/hooks ] ; then
|
||||
mkdir %{_sysconfdir}/libvirt/hooks
|
||||
fi
|
||||
cp -a ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/libvirtqemuhook %{_sysconfdir}/libvirt/hooks/qemu
|
||||
mkdir -m 0755 -p /usr/share/cloudstack-agent/tmp
|
||||
/usr/bin/systemctl restart libvirtd
|
||||
/usr/bin/systemctl enable cloudstack-agent > /dev/null 2>&1 || true
|
||||
/usr/bin/systemctl enable cloudstack-rolling-maintenance@p > /dev/null 2>&1 || true
|
||||
/usr/bin/systemctl enable --now rngd > /dev/null 2>&1 || true
|
||||
|
||||
# if saved agent.properties from upgrade exist, copy them over
|
||||
if [ -f "%{_sysconfdir}/cloud.rpmsave/agent/agent.properties" ]; then
|
||||
mv %{_sysconfdir}/%{name}/agent/agent.properties %{_sysconfdir}/%{name}/agent/agent.properties.rpmnew
|
||||
cp -p %{_sysconfdir}/cloud.rpmsave/agent/agent.properties %{_sysconfdir}/%{name}/agent
|
||||
# make sure we only do this on the first install of this RPM, don't want to overwrite on a reinstall
|
||||
mv %{_sysconfdir}/cloud.rpmsave/agent/agent.properties %{_sysconfdir}/cloud.rpmsave/agent/agent.properties.rpmsave
|
||||
fi
|
||||
|
||||
# if saved uefi.properties from upgrade exist, copy them over
|
||||
if [ -f "%{_sysconfdir}/cloud.rpmsave/agent/uefi.properties" ]; then
|
||||
mv %{_sysconfdir}/%{name}/agent/uefi.properties %{_sysconfdir}/%{name}/agent/uefi.properties.rpmnew
|
||||
cp -p %{_sysconfdir}/cloud.rpmsave/agent/uefi.properties %{_sysconfdir}/%{name}/agent
|
||||
# make sure we only do this on the first install of this RPM, don't want to overwrite on a reinstall
|
||||
mv %{_sysconfdir}/cloud.rpmsave/agent/uefi.properties %{_sysconfdir}/cloud.rpmsave/agent/uefi.properties.rpmsave
|
||||
fi
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
# Print help message
|
||||
if [ -f "/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text" ];then
|
||||
sed -i "s,^ACS_VERSION=.*,ACS_VERSION=%{_maventag},g" /usr/share/cloudstack-common/scripts/installer/cloudstack-help-text
|
||||
/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text agent
|
||||
fi
|
||||
|
||||
%pre usage
|
||||
id cloud > /dev/null 2>&1 || /usr/sbin/useradd -M -U -c "CloudStack unprivileged user" \
|
||||
-r -s /bin/sh -d %{_localstatedir}/cloudstack/management cloud|| true
|
||||
|
||||
%preun usage
|
||||
/sbin/service cloudstack-usage stop || true
|
||||
if [ "$1" == "0" ] ; then
|
||||
/sbin/chkconfig --del cloudstack-usage > /dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
%post usage
|
||||
if [ -f "%{_sysconfdir}/%{name}/management/db.properties" ]; then
|
||||
echo "Replacing usage server's db.properties with a link to the management server's db.properties"
|
||||
rm -f %{_sysconfdir}/%{name}/usage/db.properties
|
||||
ln -s %{_sysconfdir}/%{name}/management/db.properties %{_sysconfdir}/%{name}/usage/db.properties
|
||||
/usr/bin/systemctl enable cloudstack-usage > /dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
if [ -f "%{_sysconfdir}/%{name}/management/key" ]; then
|
||||
echo "Replacing usage server's key with a link to the management server's key"
|
||||
rm -f %{_sysconfdir}/%{name}/usage/key
|
||||
ln -s %{_sysconfdir}/%{name}/management/key %{_sysconfdir}/%{name}/usage/key
|
||||
fi
|
||||
|
||||
if [ ! -f "%{_sysconfdir}/%{name}/usage/key" ]; then
|
||||
ln -s %{_sysconfdir}/%{name}/management/key %{_sysconfdir}/%{name}/usage/key
|
||||
fi
|
||||
|
||||
mkdir -p /usr/local/libexec
|
||||
if [ ! -f "/usr/local/libexec/sanity-check-last-id" ]; then
|
||||
echo 1 > /usr/local/libexec/sanity-check-last-id
|
||||
fi
|
||||
chown cloud:cloud /usr/local/libexec/sanity-check-last-id
|
||||
|
||||
%posttrans usage
|
||||
# Print help message
|
||||
if [ -f "/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text" ];then
|
||||
sed -i "s,^ACS_VERSION=.*,ACS_VERSION=%{_maventag},g" /usr/share/cloudstack-common/scripts/installer/cloudstack-help-text
|
||||
/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text usage
|
||||
fi
|
||||
|
||||
%post marvin
|
||||
pip3 install --upgrade https://files.pythonhosted.org/packages/08/1f/42d74bae9dd6dcfec67c9ed0f3fa482b1ae5ac5f117ca82ab589ecb3ca19/mysql_connector_python-8.0.31-py2.py3-none-any.whl
|
||||
pip3 install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz
|
||||
|
||||
#No default permission as the permission setup is complex
|
||||
%files management
|
||||
%defattr(-,root,root,-)
|
||||
%dir %{_datadir}/%{name}-management
|
||||
%dir %attr(0770,root,cloud) %{_localstatedir}/%{name}/mnt
|
||||
%dir %attr(0770,cloud,cloud) %{_localstatedir}/%{name}/management
|
||||
%dir %attr(0770,root,cloud) %{_localstatedir}/cache/%{name}/management
|
||||
%dir %attr(0770,root,cloud) %{_localstatedir}/log/%{name}/management
|
||||
%config(noreplace) %{_sysconfdir}/default/%{name}-management
|
||||
%config(noreplace) %{_sysconfdir}/sudoers.d/%{name}-management
|
||||
%config(noreplace) %{_sysconfdir}/security/limits.d/cloud
|
||||
%config(noreplace) %{_sysconfdir}/systemd/system/%{name}-management.service.d
|
||||
%config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/db.properties
|
||||
%config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/server.properties
|
||||
%config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/config.json
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/management/log4j-cloud.xml
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/management/log4j2.xml
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/management/environment.properties
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/management/java.security.ciphers
|
||||
%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/logrotate.d/%{name}-management
|
||||
%attr(0644,root,root) %{_unitdir}/%{name}-management.service
|
||||
%attr(0755,cloud,cloud) %{_localstatedir}/run/%{name}-management.pid
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-setup-management
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-update-xenserver-licenses
|
||||
%{_datadir}/%{name}-management/conf
|
||||
%{_datadir}/%{name}-management/lib/*.jar
|
||||
%{_datadir}/%{name}-management/logs
|
||||
%{_datadir}/%{name}-management/templates
|
||||
%{_datadir}/%{name}-management/extensions
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-setup-databases
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-migrate-databases
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-set-guest-password
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-set-guest-sshkey
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-sysvmadm
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-setup-encryption
|
||||
%attr(0755,root,root) %{_bindir}/cmk
|
||||
%{_datadir}/%{name}-management/cks/conf/*.yml
|
||||
%{_datadir}/%{name}-management/setup/*.sql
|
||||
%{_datadir}/%{name}-management/setup/*.sh
|
||||
%{_datadir}/%{name}-management/setup/server-setup.xml
|
||||
%{_datadir}/%{name}-management/webapp/*
|
||||
%dir %attr(0770, cloud, cloud) %{_datadir}/%{name}-management/templates
|
||||
%dir %attr(0770, cloud, cloud) %{_datadir}/%{name}-management/templates/systemvm
|
||||
%attr(0644, cloud, cloud) %{_datadir}/%{name}-management/templates/systemvm/*
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-external-ipallocator.py
|
||||
%attr(0755,root,root) %{_initrddir}/%{name}-ipallocator
|
||||
%dir %attr(0770,root,root) %{_localstatedir}/log/%{name}/ipallocator
|
||||
%{_defaultdocdir}/%{name}-management-%{version}/LICENSE
|
||||
%{_defaultdocdir}/%{name}-management-%{version}/NOTICE
|
||||
%{_datadir}/%{name}-management/setup/wheel/*.whl
|
||||
%dir %attr(0755,cloud,cloud) %{_sysconfdir}/%{name}/extensions
|
||||
%attr(0755,cloud,cloud) %{_sysconfdir}/%{name}/extensions/*
|
||||
|
||||
%files agent
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-setup-agent
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-agent-upgrade
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-guest-tool
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-ssh
|
||||
%attr(0644,root,root) %{_unitdir}/%{name}-agent.service
|
||||
%attr(0644,root,root) %{_unitdir}/%{name}-rolling-maintenance@.service
|
||||
%config(noreplace) %{_sysconfdir}/default/%{name}-agent
|
||||
%attr(0644,root,root) %{_sysconfdir}/profile.d/%{name}-agent-profile.sh
|
||||
%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/logrotate.d/%{name}-agent
|
||||
%attr(0755,root,root) %{_datadir}/%{name}-common/scripts/network/cisco
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/agent
|
||||
%dir %{_localstatedir}/log/%{name}/agent
|
||||
%attr(0644,root,root) %{_datadir}/%{name}-agent/lib/*.jar
|
||||
%attr(0755,root,root) %{_datadir}/%{name}-agent/lib/libvirtqemuhook
|
||||
%attr(0755,root,root) %{_datadir}/%{name}-agent/lib/rolling-maintenance
|
||||
%dir %{_datadir}/%{name}-agent/plugins
|
||||
%{_defaultdocdir}/%{name}-agent-%{version}/LICENSE
|
||||
%{_defaultdocdir}/%{name}-agent-%{version}/NOTICE
|
||||
|
||||
%files common
|
||||
%dir %attr(0755,root,root) %{_datadir}/%{name}-common/python-site/cloudutils
|
||||
%dir %attr(0755,root,root) %{_datadir}/%{name}-common/vms
|
||||
%attr(0755,root,root) %{_datadir}/%{name}-common/scripts
|
||||
%attr(0755,root,root) /usr/bin/cloudstack-sccs
|
||||
%attr(0644, root, root) %{_datadir}/%{name}-common/vms/agent.zip
|
||||
%attr(0644, root, root) %{_datadir}/%{name}-common/vms/cloud-scripts.tgz
|
||||
%attr(0644, root, root) %{_datadir}/%{name}-common/vms/patch-sysvms.sh
|
||||
%attr(0644,root,root) %{_datadir}/%{name}-common/python-site/cloud_utils.py
|
||||
%attr(0644,root,root) %{_datadir}/%{name}-common/python-site/__pycache__/*
|
||||
%attr(0644,root,root) %{_datadir}/%{name}-common/python-site/cloudutils/*
|
||||
%attr(0644, root, root) %{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
|
||||
%attr(0644, root, root) %{_datadir}/%{name}-common/lib/%{name}-utils.jar
|
||||
%{_defaultdocdir}/%{name}-common-%{version}/LICENSE
|
||||
%{_defaultdocdir}/%{name}-common-%{version}/NOTICE
|
||||
|
||||
%files ui
|
||||
%config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/ui/config.json
|
||||
%{_datadir}/%{name}-ui/*
|
||||
%{_defaultdocdir}/%{name}-ui-%{version}/LICENSE
|
||||
%{_defaultdocdir}/%{name}-ui-%{version}/NOTICE
|
||||
|
||||
%files usage
|
||||
%attr(0644,root,root) %{_unitdir}/%{name}-usage.service
|
||||
%config(noreplace) %{_sysconfdir}/default/%{name}-usage
|
||||
%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/logrotate.d/%{name}-usage
|
||||
%attr(0644,root,root) %{_datadir}/%{name}-usage/*.jar
|
||||
%attr(0644,root,root) %{_datadir}/%{name}-usage/lib/*.jar
|
||||
%dir %attr(0770,root,cloud) %{_localstatedir}/log/%{name}/usage
|
||||
%attr(0644,root,root) %{_sysconfdir}/%{name}/usage/db.properties
|
||||
%attr(0644,root,root) %{_sysconfdir}/%{name}/usage/log4j-cloud.xml
|
||||
%{_defaultdocdir}/%{name}-usage-%{version}/LICENSE
|
||||
%{_defaultdocdir}/%{name}-usage-%{version}/NOTICE
|
||||
|
||||
%files marvin
|
||||
%attr(0644,root,root) %{_datadir}/%{name}-marvin/Marvin*.tar.gz
|
||||
%{_defaultdocdir}/%{name}-marvin-%{version}/LICENSE
|
||||
%{_defaultdocdir}/%{name}-marvin-%{version}/NOTICE
|
||||
|
||||
%files integration-tests
|
||||
%attr(0755,root,root) %{_datadir}/%{name}-integration-tests/*
|
||||
%{_defaultdocdir}/%{name}-integration-tests-%{version}/LICENSE
|
||||
%{_defaultdocdir}/%{name}-integration-tests-%{version}/NOTICE
|
||||
|
||||
%if "%{_ossnoss}" == "noredist"
|
||||
%files mysql-ha
|
||||
%defattr(0644,cloud,cloud,0755)
|
||||
%attr(0644,root,root) %{_datadir}/%{name}-mysql-ha/lib/*
|
||||
%endif
|
||||
|
||||
%files baremetal-agent
|
||||
%attr(0755,root,root) %{_bindir}/cloudstack-setup-baremetal
|
||||
|
||||
%changelog
|
||||
* Thu Dec 22 2022 Rohit Yadav <rohit@apache.org> 4.18.0
|
||||
- Add support for EL9
|
||||
|
||||
* Fri Oct 14 2022 Daan Hoogland <daan.hoogland@gmail.com> 4.18.0
|
||||
- initialising sanity check pointer file
|
||||
|
||||
* Tue Jun 29 2021 David Jumani <dj.davidjumani1994@gmail.com> 4.16.0
|
||||
- Adding SUSE 15 support
|
||||
|
||||
* Thu Apr 30 2015 Rohit Yadav <bhaisaab@apache.org> 4.6.0
|
||||
- Remove awsapi package
|
||||
|
||||
* Wed Nov 19 2014 Hugo Trippaers <hugo@apache.org> 4.6.0
|
||||
- Create a specific spec for CentOS 7
|
||||
|
||||
* Fri Jul 4 2014 Hugo Trippaers <hugo@apache.org> 4.5.0
|
||||
- Add a package for the mysql ha module
|
||||
|
||||
* Fri Oct 5 2012 Hugo Trippaers <hugo@apache.org> 4.1.0
|
||||
- new style spec file
|
||||
|
|
@ -0,0 +1 @@
|
|||
../el8/cloudstack-agent.te
|
||||
|
|
@ -0,0 +1 @@
|
|||
../el8/cloudstack-sccs
|
||||
|
|
@ -0,0 +1 @@
|
|||
../el8/filelimit.conf
|
||||
|
|
@ -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.
|
||||
|
||||
DBUSER=cloud
|
||||
DBPW=cloud
|
||||
DBROOTPW=
|
||||
MSLOG=vmops.log
|
||||
APISERVERLOG=api.log
|
||||
DBHOST=localhost
|
||||
DBDRIVER=jdbc:mysql
|
||||
COMPONENTS-SPEC=components-premium.xml
|
||||
REMOTEHOST=localhost
|
||||
AGENTCLASSPATH=
|
||||
AGENTLOG=/var/log/cloudstack/agent/agent.log
|
||||
AGENTLOGDIR=/var/log/cloudstack/agent/
|
||||
AGENTSYSCONFDIR=/etc/cloudstack/agent
|
||||
APISERVERLOG=/var/log/cloudstack/management/apilog.log
|
||||
BINDIR=/usr/bin
|
||||
COMMONLIBDIR=/usr/share/cloudstack-common
|
||||
CONFIGUREVARS=
|
||||
DEPSCLASSPATH=
|
||||
DOCDIR=
|
||||
IPALOCATORLOG=/var/log/cloudstack/management/ipallocator.log
|
||||
JAVADIR=/usr/share/java
|
||||
LIBEXECDIR=/usr/libexec
|
||||
LOCKDIR=/var/lock
|
||||
MSCLASSPATH=
|
||||
MSCONF=/etc/cloudstack/management
|
||||
MSENVIRON=/usr/share/cloudstack-management
|
||||
MSLOG=/var/log/cloudstack/management/management-server.log
|
||||
MSLOGDIR=/var/log/cloudstack/management/
|
||||
MSMNTDIR=/var/cloudstack/mnt
|
||||
MSUSER=cloud
|
||||
PIDDIR=/var/run
|
||||
PLUGINJAVADIR=/usr/share/cloudstack-management/plugin
|
||||
PREMIUMJAVADIR=/usr/share/cloudstack-management/premium
|
||||
PYTHONDIR=/usr/share/cloudstack-common/python-site/
|
||||
SERVERSYSCONFDIR=/etc/sysconfig
|
||||
SETUPDATADIR=/usr/share/cloudstack-management/setup
|
||||
SYSCONFDIR=/etc/sysconfig
|
||||
SYSTEMCLASSPATH=
|
||||
SYSTEMJARS=
|
||||
USAGECLASSPATH=
|
||||
USAGELOG=/var/log/cloudstack/usage/usage.log
|
||||
USAGESYSCONFDIR=/etc/sysconfig
|
||||
EXTENSIONSDEPLOYMENTMODE=production
|
||||
GUESTNVRAMTEMPLATELEGACY=/usr/share/qemu/ovmf-x86_64-vars.bin
|
||||
GUESTLOADERLEGACY=/usr/share/qemu/ovmf-x86_64-code.bin
|
||||
GUESTNVRAMTEMPLATESECURE=/usr/share/qemu/ovmf-x86_64-ms-vars.bin
|
||||
GUESTLOADERSECURE=/usr/share/qemu/ovmf-x86_64-ms-code.bin
|
||||
GUESTNVRAMPATH=/var/lib/libvirt/qemu/nvram/
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
JAVA_OPTS="-Djava.security.properties=/etc/cloudstack/management/java.security.ciphers -Djava.awt.headless=true -Xmx2G -XX:+UseParallelGC -XX:MaxGCPauseMillis=500 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/cloudstack/management/ -XX:ErrorFile=/var/log/cloudstack/management/cloudstack-management.err --add-opens=java.base/java.lang=ALL-UNNAMED --add-exports=java.base/sun.security.x509=ALL-UNNAMED"
|
||||
|
||||
CLASSPATH="/usr/share/cloudstack-management/lib/*:/etc/cloudstack/management:/usr/share/cloudstack-common:/usr/share/cloudstack-management/setup:/usr/share/cloudstack-management:/usr/share/java/mysql-connector-java.jar:/usr/share/cloudstack-mysql-ha/lib/*"
|
||||
CLASSPATH="/usr/share/cloudstack-management/lib/*:/etc/cloudstack/management:/usr/share/cloudstack-common:/usr/share/cloudstack-management/setup:/usr/share/cloudstack-management:/usr/share/cloudstack-mysql-ha/lib/*"
|
||||
|
||||
BOOTSTRAP_CLASS=org.apache.cloudstack.ServerDaemon
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
JAVA_OPTS="-Xms256m -Xmx2048m --add-opens=java.base/java.lang=ALL-UNNAMED"
|
||||
|
||||
CLASSPATH="/usr/share/cloudstack-usage/*:/usr/share/cloudstack-usage/lib/*:/usr/share/cloudstack-mysql-ha/lib/*:/etc/cloudstack/usage:/usr/share/java/mysql-connector-java.jar"
|
||||
CLASSPATH="/usr/share/cloudstack-usage/*:/usr/share/cloudstack-usage/lib/*:/usr/share/cloudstack-mysql-ha/lib/*:/etc/cloudstack/usage"
|
||||
|
||||
JAVA_CLASS=com.cloud.usage.UsageServer
|
||||
|
||||
|
|
|
|||
|
|
@ -40,17 +40,17 @@ public final class RootCACustomTrustManager implements X509TrustManager {
|
|||
private boolean authStrictness = true;
|
||||
private boolean allowExpiredCertificate = true;
|
||||
private CrlDao crlDao;
|
||||
private X509Certificate caCertificate;
|
||||
private List<X509Certificate> caCertificates;
|
||||
private Map<String, X509Certificate> activeCertMap;
|
||||
|
||||
public RootCACustomTrustManager(final String clientAddress, final boolean authStrictness, final boolean allowExpiredCertificate, final Map<String, X509Certificate> activeCertMap, final X509Certificate caCertificate, final CrlDao crlDao) {
|
||||
public RootCACustomTrustManager(final String clientAddress, final boolean authStrictness, final boolean allowExpiredCertificate, final Map<String, X509Certificate> activeCertMap, final List<X509Certificate> caCertificates, final CrlDao crlDao) {
|
||||
if (StringUtils.isNotEmpty(clientAddress)) {
|
||||
this.clientAddress = clientAddress.replace("/", "").split(":")[0];
|
||||
}
|
||||
this.authStrictness = authStrictness;
|
||||
this.allowExpiredCertificate = allowExpiredCertificate;
|
||||
this.activeCertMap = activeCertMap;
|
||||
this.caCertificate = caCertificate;
|
||||
this.caCertificates = caCertificates;
|
||||
this.crlDao = crlDao;
|
||||
}
|
||||
|
||||
|
|
@ -151,6 +151,6 @@ public final class RootCACustomTrustManager implements X509TrustManager {
|
|||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[]{caCertificate};
|
||||
return caCertificates.toArray(new X509Certificate[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ import java.security.cert.X509Certificate;
|
|||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
|
@ -60,6 +59,7 @@ import org.apache.cloudstack.framework.ca.CAProvider;
|
|||
import org.apache.cloudstack.framework.ca.Certificate;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.framework.config.ValidatedConfigKey;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.utils.security.CertUtils;
|
||||
import org.apache.cloudstack.utils.security.KeyStoreUtils;
|
||||
|
|
@ -92,6 +92,7 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
|
||||
private static KeyPair caKeyPair = null;
|
||||
private static X509Certificate caCertificate = null;
|
||||
private static List<X509Certificate> caCertificates = null;
|
||||
private static KeyStore managementKeyStore = null;
|
||||
|
||||
@Inject
|
||||
|
|
@ -103,20 +104,25 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
/////////////// Root CA Settings ///////////////////
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
private static ConfigKey<String> rootCAPrivateKey = new ConfigKey<>("Hidden", String.class,
|
||||
"ca.plugin.root.private.key",
|
||||
null,
|
||||
"The ROOT CA private key.", true);
|
||||
private static ConfigKey<String> rootCAPrivateKey = new ValidatedConfigKey<>("Hidden", String.class,
|
||||
"ca.plugin.root.private.key", null,
|
||||
"The ROOT CA private key in PEM format. " +
|
||||
"When set along with the public key and certificate, CloudStack uses this custom CA instead of auto-generating one. " +
|
||||
"All three ca.plugin.root.* keys must be set together. Restart management server(s) when changed.",
|
||||
false, ConfigKey.Scope.Global, null, RootCAProvider::validatePrivateKeyPem);
|
||||
|
||||
private static ConfigKey<String> rootCAPublicKey = new ConfigKey<>("Hidden", String.class,
|
||||
"ca.plugin.root.public.key",
|
||||
null,
|
||||
"The ROOT CA public key.", true);
|
||||
private static ConfigKey<String> rootCAPublicKey = new ValidatedConfigKey<>("Hidden", String.class,
|
||||
"ca.plugin.root.public.key", null,
|
||||
"The ROOT CA public key in PEM format (X.509/SPKI: must start with '-----BEGIN PUBLIC KEY-----'). " +
|
||||
"Required when providing a custom CA. Restart management server(s) when changed.",
|
||||
false, ConfigKey.Scope.Global, null, RootCAProvider::validatePublicKeyPem);
|
||||
|
||||
private static ConfigKey<String> rootCACertificate = new ConfigKey<>("Hidden", String.class,
|
||||
"ca.plugin.root.ca.certificate",
|
||||
null,
|
||||
"The ROOT CA certificate.", true);
|
||||
private static ConfigKey<String> rootCACertificate = new ValidatedConfigKey<>("Hidden", String.class,
|
||||
"ca.plugin.root.ca.certificate", null,
|
||||
"The CA certificate(s) in PEM format (must start with '-----BEGIN CERTIFICATE-----'). " +
|
||||
"For intermediate CAs, concatenate the signing cert first, followed by intermediate(s) and root. " +
|
||||
"Required when providing a custom CA. Restart management server(s) when changed.",
|
||||
false, ConfigKey.Scope.Global, null, RootCAProvider::validateCACertificatePem);
|
||||
|
||||
private static ConfigKey<String> rootCAIssuerDN = new ConfigKey<>("Advanced", String.class,
|
||||
"ca.plugin.root.issuer.dn",
|
||||
|
|
@ -151,7 +157,7 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
caCertificate, caKeyPair, keyPair.getPublic(),
|
||||
subject, CAManager.CertSignatureAlgorithm.value(),
|
||||
validityDays, domainNames, ipAddresses);
|
||||
return new Certificate(clientCertificate, keyPair.getPrivate(), Collections.singletonList(caCertificate));
|
||||
return new Certificate(clientCertificate, keyPair.getPrivate(), caCertificates);
|
||||
}
|
||||
|
||||
private Certificate generateCertificateUsingCsr(final String csr, final List<String> names, final List<String> ips, final int validityDays) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, CertificateException, SignatureException, IOException, OperatorCreationException {
|
||||
|
|
@ -205,7 +211,7 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
caCertificate, caKeyPair, request.getPublicKey(),
|
||||
subject, CAManager.CertSignatureAlgorithm.value(),
|
||||
validityDays, dnsNames, ipAddresses);
|
||||
return new Certificate(clientCertificate, null, Collections.singletonList(caCertificate));
|
||||
return new Certificate(clientCertificate, null, caCertificates);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
|
|
@ -219,7 +225,7 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
|
||||
@Override
|
||||
public List<X509Certificate> getCaCertificate() {
|
||||
return Collections.singletonList(caCertificate);
|
||||
return caCertificates;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -254,8 +260,8 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
private KeyStore getCaKeyStore() throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException {
|
||||
final KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(null, null);
|
||||
if (caKeyPair != null && caCertificate != null) {
|
||||
ks.setKeyEntry(caAlias, caKeyPair.getPrivate(), getKeyStorePassphrase(), new X509Certificate[]{caCertificate});
|
||||
if (caKeyPair != null && CollectionUtils.isNotEmpty(caCertificates)) {
|
||||
ks.setKeyEntry(caAlias, caKeyPair.getPrivate(), getKeyStorePassphrase(), caCertificates.toArray(new X509Certificate[0]));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -274,7 +280,7 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
final boolean authStrictness = rootCAAuthStrictness.value();
|
||||
final boolean allowExpiredCertificate = rootCAAllowExpiredCert.value();
|
||||
|
||||
TrustManager[] tms = new TrustManager[]{new RootCACustomTrustManager(remoteAddress, authStrictness, allowExpiredCertificate, certMap, caCertificate, crlDao)};
|
||||
TrustManager[] tms = new TrustManager[]{new RootCACustomTrustManager(remoteAddress, authStrictness, allowExpiredCertificate, certMap, caCertificates, crlDao)};
|
||||
|
||||
sslContext.init(kmf.getKeyManagers(), tms, new SecureRandom());
|
||||
final SSLEngine sslEngine = sslContext.createSSLEngine();
|
||||
|
|
@ -316,33 +322,39 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
if (!configDao.update(rootCAPrivateKey.key(), rootCAPrivateKey.category(), CertUtils.privateKeyToPem(keyPair.getPrivate()))) {
|
||||
logger.error("Failed to save RootCA private key");
|
||||
}
|
||||
caKeyPair = keyPair;
|
||||
} catch (final NoSuchProviderException | NoSuchAlgorithmException | IOException e) {
|
||||
logger.error("Failed to generate/save RootCA private/public keys due to exception:", e);
|
||||
}
|
||||
return loadRootCAKeyPair();
|
||||
return caKeyPair != null && caKeyPair.getPrivate() != null && caKeyPair.getPublic() != null;
|
||||
}
|
||||
|
||||
private boolean saveNewRootCACertificate() {
|
||||
boolean saveNewRootCACertificate() {
|
||||
if (caKeyPair == null) {
|
||||
throw new CloudRuntimeException("Cannot issue self-signed root CA certificate as CA keypair is not initialized");
|
||||
}
|
||||
try {
|
||||
logger.debug("Generating root CA certificate");
|
||||
final X509Certificate rootCaCertificate = CertUtils.generateV3Certificate(
|
||||
final X509Certificate generatedCACert = CertUtils.generateV3Certificate(
|
||||
null, caKeyPair, caKeyPair.getPublic(),
|
||||
rootCAIssuerDN.value(), CAManager.CertSignatureAlgorithm.value(),
|
||||
getCaValidityDays(), null, null);
|
||||
if (!configDao.update(rootCACertificate.key(), rootCACertificate.category(), CertUtils.x509CertificateToPem(rootCaCertificate))) {
|
||||
if (!configDao.update(rootCACertificate.key(), rootCACertificate.category(), CertUtils.x509CertificateToPem(generatedCACert))) {
|
||||
logger.error("Failed to update RootCA public/x509 certificate");
|
||||
}
|
||||
caCertificates = new ArrayList<>(java.util.Collections.singletonList(generatedCACert));
|
||||
caCertificate = generatedCACert;
|
||||
} catch (final CertificateException | NoSuchAlgorithmException | NoSuchProviderException | SignatureException | InvalidKeyException | OperatorCreationException | IOException e) {
|
||||
logger.error("Failed to generate RootCA certificate from private/public keys due to exception:", e);
|
||||
return false;
|
||||
}
|
||||
return loadRootCACertificate();
|
||||
return caCertificate != null;
|
||||
}
|
||||
|
||||
private boolean loadRootCAKeyPair() {
|
||||
if (caKeyPair != null) {
|
||||
return true;
|
||||
}
|
||||
if (StringUtils.isAnyEmpty(rootCAPublicKey.value(), rootCAPrivateKey.value())) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -355,14 +367,35 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
return caKeyPair.getPrivate() != null && caKeyPair.getPublic() != null;
|
||||
}
|
||||
|
||||
private boolean loadRootCACertificate() {
|
||||
boolean loadRootCACertificate() {
|
||||
if (caCertificate != null && CollectionUtils.isNotEmpty(caCertificates)) {
|
||||
return true;
|
||||
}
|
||||
caCertificate = null;
|
||||
caCertificates = null;
|
||||
if (StringUtils.isEmpty(rootCACertificate.value())) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
caCertificate = CertUtils.pemToX509Certificate(rootCACertificate.value());
|
||||
caCertificate.verify(caKeyPair.getPublic());
|
||||
} catch (final IOException | CertificateException | NoSuchAlgorithmException | InvalidKeyException | SignatureException | NoSuchProviderException e) {
|
||||
final List<X509Certificate> loadedCerts = CertUtils.pemToX509Certificates(rootCACertificate.value());
|
||||
if (CollectionUtils.isEmpty(loadedCerts)) {
|
||||
logger.error("No certificates found in ca.plugin.root.ca.certificate");
|
||||
return false;
|
||||
}
|
||||
final X509Certificate loadedCACert = loadedCerts.get(0);
|
||||
|
||||
// Verify key ownership without enforcing self-signature
|
||||
if (!loadedCACert.getPublicKey().equals(caKeyPair.getPublic())) {
|
||||
logger.error("The public key in the CA certificate does not match the configured CA public key");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loadedCerts.size() > 1) {
|
||||
logger.info("Loaded CA certificate chain with {} certificate(s)", loadedCerts.size());
|
||||
}
|
||||
caCertificates = loadedCerts;
|
||||
caCertificate = loadedCACert;
|
||||
} catch (final IOException | CertificateException e) {
|
||||
logger.error("Failed to load saved RootCA certificate due to exception:", e);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -389,9 +422,15 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
try {
|
||||
managementKeyStore = KeyStore.getInstance("JKS");
|
||||
managementKeyStore.load(null, null);
|
||||
managementKeyStore.setCertificateEntry(caAlias, caCertificate);
|
||||
int caIndex = 0;
|
||||
for (final X509Certificate cert : caCertificates) {
|
||||
managementKeyStore.setCertificateEntry(caAlias + "-" + caIndex++, cert);
|
||||
}
|
||||
final List<X509Certificate> fullChain = new ArrayList<>();
|
||||
fullChain.add(serverCertificate.getClientCertificate());
|
||||
fullChain.addAll(caCertificates);
|
||||
managementKeyStore.setKeyEntry(managementAlias, serverCertificate.getPrivateKey(), getKeyStorePassphrase(),
|
||||
new X509Certificate[]{serverCertificate.getClientCertificate(), caCertificate});
|
||||
fullChain.toArray(new X509Certificate[0]));
|
||||
} catch (final CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException e) {
|
||||
logger.error("Failed to load root CA management-server keystore due to exception: ", e);
|
||||
return false;
|
||||
|
|
@ -421,14 +460,63 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
}
|
||||
|
||||
|
||||
private boolean setupCA() {
|
||||
if (!loadRootCAKeyPair() && !saveNewRootCAKeypair()) {
|
||||
logger.error("Failed to save and load root CA keypair");
|
||||
return false;
|
||||
private static void validatePrivateKeyPem(String value) {
|
||||
if (StringUtils.isEmpty(value)) return;
|
||||
try {
|
||||
CertUtils.pemToPrivateKey(value);
|
||||
} catch (InvalidKeySpecException | IOException e) {
|
||||
throw new IllegalArgumentException(
|
||||
"ca.plugin.root.private.key is not a valid PEM private key: " + e.getMessage());
|
||||
}
|
||||
if (!loadRootCACertificate() && !saveNewRootCACertificate()) {
|
||||
logger.error("Failed to save and load root CA certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void validatePublicKeyPem(String value) {
|
||||
if (StringUtils.isEmpty(value)) return;
|
||||
try {
|
||||
CertUtils.pemToPublicKey(value);
|
||||
} catch (InvalidKeySpecException | IOException e) {
|
||||
throw new IllegalArgumentException(
|
||||
"ca.plugin.root.public.key is not a valid PEM public key: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static void validateCACertificatePem(String value) {
|
||||
if (StringUtils.isEmpty(value)) return;
|
||||
try {
|
||||
final List<X509Certificate> certs = CertUtils.pemToX509Certificates(value);
|
||||
if (CollectionUtils.isEmpty(certs)) {
|
||||
throw new IllegalArgumentException(
|
||||
"ca.plugin.root.ca.certificate contains no certificates");
|
||||
}
|
||||
} catch (IOException | CertificateException e) {
|
||||
throw new IllegalArgumentException(
|
||||
"ca.plugin.root.ca.certificate is not a valid PEM certificate: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setupCA() {
|
||||
if (!loadRootCAKeyPair()) {
|
||||
if (hasUserProvidedCAKeys()) {
|
||||
logger.error("Failed to load user-provided CA keys from configuration. " +
|
||||
"Check that ca.plugin.root.private.key, ca.plugin.root.public.key, and " +
|
||||
"ca.plugin.root.ca.certificate are all set and in the correct PEM format. " +
|
||||
"Overwriting with auto-generated keys.");
|
||||
}
|
||||
if (!saveNewRootCAKeypair()) {
|
||||
logger.error("Failed to save and load root CA keypair");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!loadRootCACertificate()) {
|
||||
if (hasUserProvidedCAKeys()) {
|
||||
logger.error("Failed to load user-provided CA certificate. " +
|
||||
"Check that ca.plugin.root.ca.certificate is set and in PEM format. " +
|
||||
"Overwriting with auto-generated certificate.");
|
||||
}
|
||||
if (!saveNewRootCACertificate()) {
|
||||
logger.error("Failed to save and load root CA certificate");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!loadManagementKeyStore()) {
|
||||
logger.error("Failed to check and configure management server keystore");
|
||||
|
|
@ -437,10 +525,16 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean hasUserProvidedCAKeys() {
|
||||
return StringUtils.isNotEmpty(rootCAPublicKey.value())
|
||||
|| StringUtils.isNotEmpty(rootCAPrivateKey.value())
|
||||
|| StringUtils.isNotEmpty(rootCACertificate.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
managementCertificateCustomSAN = CAManager.CertManagementCustomSubjectAlternativeName.value();
|
||||
return loadRootCAKeyPair() && loadRootCAKeyPair() && loadManagementKeyStore();
|
||||
return loadRootCAKeyPair() && loadRootCACertificate() && loadManagementKeyStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -23,9 +23,11 @@ import java.math.BigInteger;
|
|||
import java.security.KeyPair;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.utils.security.CertUtils;
|
||||
import org.junit.Assert;
|
||||
|
|
@ -63,14 +65,14 @@ public class RootCACustomTrustManagerTest {
|
|||
|
||||
@Test
|
||||
public void testAuthNotStrictWithInvalidCert() throws Exception {
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, true, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, true, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
trustManager.checkClientTrusted(null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthNotStrictWithRevokedCert() throws Exception {
|
||||
Mockito.when(crlDao.findBySerial(Mockito.any(BigInteger.class))).thenReturn(new CrlVO());
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, true, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, true, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
trustManager.checkClientTrusted(new X509Certificate[]{caCertificate}, "RSA");
|
||||
Assert.assertTrue(certMap.containsKey(clientIp));
|
||||
Assert.assertEquals(certMap.get(clientIp), caCertificate);
|
||||
|
|
@ -79,7 +81,7 @@ public class RootCACustomTrustManagerTest {
|
|||
@Test
|
||||
public void testAuthNotStrictWithInvalidCertOwnership() throws Exception {
|
||||
Mockito.when(crlDao.findBySerial(Mockito.any(BigInteger.class))).thenReturn(null);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, true, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, true, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
trustManager.checkClientTrusted(new X509Certificate[]{caCertificate}, "RSA");
|
||||
Assert.assertTrue(certMap.containsKey(clientIp));
|
||||
Assert.assertEquals(certMap.get(clientIp), caCertificate);
|
||||
|
|
@ -88,14 +90,14 @@ public class RootCACustomTrustManagerTest {
|
|||
@Test(expected = CertificateException.class)
|
||||
public void testAuthNotStrictWithDenyExpiredCertAndOwnership() throws Exception {
|
||||
Mockito.when(crlDao.findBySerial(Mockito.any(BigInteger.class))).thenReturn(null);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, false, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, false, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
trustManager.checkClientTrusted(new X509Certificate[]{expiredClientCertificate}, "RSA");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthNotStrictWithAllowExpiredCertAndOwnership() throws Exception {
|
||||
Mockito.when(crlDao.findBySerial(Mockito.any(BigInteger.class))).thenReturn(null);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, true, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, false, true, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
trustManager.checkClientTrusted(new X509Certificate[]{expiredClientCertificate}, "RSA");
|
||||
Assert.assertTrue(certMap.containsKey(clientIp));
|
||||
Assert.assertEquals(certMap.get(clientIp), expiredClientCertificate);
|
||||
|
|
@ -103,35 +105,50 @@ public class RootCACustomTrustManagerTest {
|
|||
|
||||
@Test(expected = CertificateException.class)
|
||||
public void testAuthStrictWithInvalidCert() throws Exception {
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, true, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, true, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
trustManager.checkClientTrusted(null, null);
|
||||
}
|
||||
|
||||
@Test(expected = CertificateException.class)
|
||||
public void testAuthStrictWithRevokedCert() throws Exception {
|
||||
Mockito.when(crlDao.findBySerial(Mockito.any(BigInteger.class))).thenReturn(new CrlVO());
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, true, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, true, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
trustManager.checkClientTrusted(new X509Certificate[]{caCertificate}, "RSA");
|
||||
}
|
||||
|
||||
@Test(expected = CertificateException.class)
|
||||
public void testAuthStrictWithInvalidCertOwnership() throws Exception {
|
||||
Mockito.when(crlDao.findBySerial(Mockito.any(BigInteger.class))).thenReturn(null);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, true, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, true, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
trustManager.checkClientTrusted(new X509Certificate[]{caCertificate}, "RSA");
|
||||
}
|
||||
|
||||
@Test(expected = CertificateException.class)
|
||||
public void testAuthStrictWithDenyExpiredCertAndOwnership() throws Exception {
|
||||
Mockito.when(crlDao.findBySerial(Mockito.any(BigInteger.class))).thenReturn(null);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, false, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, false, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
trustManager.checkClientTrusted(new X509Certificate[]{expiredClientCertificate}, "RSA");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAcceptedIssuersWithChain() throws Exception {
|
||||
final KeyPair rootKeyPair = CertUtils.generateRandomKeyPair(1024);
|
||||
final X509Certificate rootCert = CertUtils.generateV3Certificate(null, rootKeyPair, rootKeyPair.getPublic(),
|
||||
"CN=root", "SHA256withRSA", 365, null, null);
|
||||
final List<X509Certificate> chain = Arrays.asList(caCertificate, rootCert);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(
|
||||
clientIp, false, true, certMap, chain, crlDao);
|
||||
|
||||
final X509Certificate[] issuers = trustManager.getAcceptedIssuers();
|
||||
Assert.assertEquals(2, issuers.length);
|
||||
Assert.assertEquals(caCertificate, issuers[0]);
|
||||
Assert.assertEquals(rootCert, issuers[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthStrictWithAllowExpiredCertAndOwnership() throws Exception {
|
||||
Mockito.when(crlDao.findBySerial(Mockito.any(BigInteger.class))).thenReturn(null);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, true, certMap, caCertificate, crlDao);
|
||||
final RootCACustomTrustManager trustManager = new RootCACustomTrustManager(clientIp, true, true, certMap, Collections.singletonList(caCertificate), crlDao);
|
||||
Assert.assertTrue(trustManager.getAcceptedIssuers() != null);
|
||||
Assert.assertTrue(trustManager.getAcceptedIssuers().length == 1);
|
||||
Assert.assertEquals(trustManager.getAcceptedIssuers()[0], caCertificate);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import java.security.cert.X509Certificate;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -38,6 +39,7 @@ import javax.net.ssl.SSLEngine;
|
|||
|
||||
import org.apache.cloudstack.framework.ca.Certificate;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.utils.security.CertUtils;
|
||||
import org.apache.cloudstack.utils.security.SSLUtils;
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
|
|
@ -49,7 +51,6 @@ 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)
|
||||
|
|
@ -75,7 +76,7 @@ public class RootCAProviderTest {
|
|||
|
||||
addField(provider, "caKeyPair", caKeyPair);
|
||||
addField(provider, "caCertificate", caCertificate);
|
||||
addField(provider, "caKeyPair", caKeyPair);
|
||||
addField(provider, "caCertificates", Collections.singletonList(caCertificate));
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -129,6 +130,46 @@ public class RootCAProviderTest {
|
|||
certificate.getClientCertificate().verify(caCertificate.getPublicKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCaCertificateWithChain() throws Exception {
|
||||
final KeyPair rootKeyPair = CertUtils.generateRandomKeyPair(1024);
|
||||
final X509Certificate rootCert = CertUtils.generateV3Certificate(null, rootKeyPair, rootKeyPair.getPublic(),
|
||||
"CN=root", "SHA256withRSA", 365, null, null);
|
||||
final KeyPair intermediateKeyPair = CertUtils.generateRandomKeyPair(1024);
|
||||
final X509Certificate intermediateCert = CertUtils.generateV3Certificate(rootCert, rootKeyPair,
|
||||
intermediateKeyPair.getPublic(), "CN=intermediate", "SHA256withRSA", 365, null, null);
|
||||
|
||||
final List<X509Certificate> chain = Arrays.asList(intermediateCert, rootCert);
|
||||
addField(provider, "caKeyPair", intermediateKeyPair);
|
||||
addField(provider, "caCertificate", intermediateCert);
|
||||
addField(provider, "caCertificates", chain);
|
||||
|
||||
Assert.assertEquals(2, provider.getCaCertificate().size());
|
||||
Assert.assertEquals(intermediateCert, provider.getCaCertificate().get(0));
|
||||
Assert.assertEquals(rootCert, provider.getCaCertificate().get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIssueCertificateWithoutCsrAndChain() throws Exception {
|
||||
final KeyPair rootKeyPair = CertUtils.generateRandomKeyPair(1024);
|
||||
final X509Certificate rootCert = CertUtils.generateV3Certificate(null, rootKeyPair, rootKeyPair.getPublic(),
|
||||
"CN=root", "SHA256withRSA", 365, null, null);
|
||||
final KeyPair intermediateKeyPair = CertUtils.generateRandomKeyPair(1024);
|
||||
final X509Certificate intermediateCert = CertUtils.generateV3Certificate(rootCert, rootKeyPair,
|
||||
intermediateKeyPair.getPublic(), "CN=intermediate", "SHA256withRSA", 365, null, null);
|
||||
|
||||
addField(provider, "caKeyPair", intermediateKeyPair);
|
||||
addField(provider, "caCertificate", intermediateCert);
|
||||
addField(provider, "caCertificates", Arrays.asList(intermediateCert, rootCert));
|
||||
|
||||
final Certificate certificate = provider.issueCertificate(Arrays.asList("domain1.com"), null, 1);
|
||||
Assert.assertNotNull(certificate);
|
||||
Assert.assertEquals(2, certificate.getCaCertificates().size());
|
||||
Assert.assertEquals(intermediateCert, certificate.getCaCertificates().get(0));
|
||||
Assert.assertEquals(rootCert, certificate.getCaCertificates().get(1));
|
||||
certificate.getClientCertificate().verify(intermediateKeyPair.getPublic());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevokeCertificate() throws Exception {
|
||||
Assert.assertTrue(provider.revokeCertificate(CertUtils.generateRandomBigInt(), "anyString"));
|
||||
|
|
@ -177,8 +218,8 @@ public class RootCAProviderTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testIsManagementCertificateNoMatch() {
|
||||
ReflectionTestUtils.setField(provider, "managementCertificateCustomSAN", "cloudstack");
|
||||
public void testIsManagementCertificateNoMatch() throws Exception {
|
||||
addField(provider, "managementCertificateCustomSAN", "cloudstack");
|
||||
try {
|
||||
X509Certificate certificate = Mockito.mock(X509Certificate.class);
|
||||
List<List<?>> altNames = new ArrayList<>();
|
||||
|
|
@ -193,9 +234,9 @@ public class RootCAProviderTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testIsManagementCertificateMatch() {
|
||||
public void testIsManagementCertificateMatch() throws Exception {
|
||||
String customSAN = "cloudstack";
|
||||
ReflectionTestUtils.setField(provider, "managementCertificateCustomSAN", customSAN);
|
||||
addField(provider, "managementCertificateCustomSAN", customSAN);
|
||||
try {
|
||||
X509Certificate certificate = Mockito.mock(X509Certificate.class);
|
||||
List<List<?>> altNames = new ArrayList<>();
|
||||
|
|
@ -208,4 +249,58 @@ public class RootCAProviderTest {
|
|||
Assert.fail(String.format("Exception occurred: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadRootCACertificateWithMismatchedCert() throws Exception {
|
||||
KeyPair otherKeyPair = CertUtils.generateRandomKeyPair(1024);
|
||||
X509Certificate mismatchedCert = CertUtils.generateV3Certificate(null, otherKeyPair, otherKeyPair.getPublic(), "CN=other", "SHA256withRSA", 365, null, null);
|
||||
String mismatchedPem = CertUtils.x509CertificateToPem(mismatchedCert);
|
||||
|
||||
ConfigKey<String> mockCertKey = Mockito.mock(ConfigKey.class);
|
||||
Mockito.when(mockCertKey.value()).thenReturn(mismatchedPem);
|
||||
addField(provider, "rootCACertificate", mockCertKey);
|
||||
|
||||
addField(provider, "caCertificate", null);
|
||||
addField(provider, "caCertificates", null);
|
||||
|
||||
Boolean result = provider.loadRootCACertificate();
|
||||
Assert.assertFalse(result);
|
||||
Assert.assertNull(provider.getCaCertificate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveNewRootCACertificateWithStaleCache() throws Exception {
|
||||
ConfigurationDao configDao = Mockito.mock(ConfigurationDao.class);
|
||||
addField(provider, "configDao", configDao);
|
||||
|
||||
ConfigKey<String> mockCertKey = Mockito.mock(ConfigKey.class);
|
||||
Mockito.when(mockCertKey.key()).thenReturn("ca.plugin.root.ca.certificate");
|
||||
Mockito.when(mockCertKey.category()).thenReturn("Hidden");
|
||||
addField(provider, "rootCACertificate", mockCertKey);
|
||||
|
||||
ConfigKey<String> mockIssuerKey = Mockito.mock(ConfigKey.class);
|
||||
Mockito.when(mockIssuerKey.value()).thenReturn("CN=ca.cloudstack.apache.org");
|
||||
addField(provider, "rootCAIssuerDN", mockIssuerKey);
|
||||
|
||||
addField(provider, "caCertificate", null);
|
||||
addField(provider, "caCertificates", null);
|
||||
|
||||
Mockito.when(configDao.update(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(true);
|
||||
|
||||
Boolean result = provider.saveNewRootCACertificate();
|
||||
Assert.assertTrue(result);
|
||||
Assert.assertNotNull(provider.getCaCertificate());
|
||||
Assert.assertEquals(1, provider.getCaCertificate().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCACertificatePem() throws Exception {
|
||||
String truncatedPem = "-----BEGIN CERTIFICATE-----\nMIICxTCCAa0CAQAw\n";
|
||||
try {
|
||||
RootCAProvider.validateCACertificatePem(truncatedPem);
|
||||
Assert.fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
Assert.assertTrue(e.getMessage().contains("is not a valid PEM certificate"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ import org.apache.http.HttpHeaders;
|
|||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.TrustAllStrategy;
|
||||
import org.apache.http.entity.ContentType;
|
||||
|
|
@ -97,7 +99,9 @@ public class WebhookDeliveryThread implements Runnable {
|
|||
|
||||
protected void setHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||
if (webhook.isSslVerification()) {
|
||||
httpClient = HttpClients.createDefault();
|
||||
httpClient = HttpClients.custom()
|
||||
.setSSLContext(SSLContext.getDefault())
|
||||
.build();
|
||||
return;
|
||||
}
|
||||
httpClient = HttpClients
|
||||
|
|
|
|||
|
|
@ -41,15 +41,15 @@ public class HypervInvestigator extends AdapterBase implements Investigator {
|
|||
|
||||
@Override
|
||||
public boolean isVmAlive(com.cloud.vm.VirtualMachine vm, Host host) throws UnknownVM {
|
||||
Status status = isAgentAlive(host);
|
||||
Status status = getHostAgentStatus(host);
|
||||
if (status == null) {
|
||||
throw new UnknownVM();
|
||||
}
|
||||
return status == Status.Up ? true : null;
|
||||
return status == Status.Up;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status isAgentAlive(Host agent) {
|
||||
public Status getHostAgentStatus(Host agent) {
|
||||
if (agent.getHypervisorType() != Hypervisor.HypervisorType.Hyperv) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,7 @@
|
|||
package com.cloud.ha;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.CheckOnHostCommand;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.Status;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
|
|
@ -34,11 +31,12 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
|
|||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
|
||||
import org.apache.cloudstack.ha.HAManager;
|
||||
import org.apache.cloudstack.kvm.ha.KVMHostActivityChecker;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class KVMInvestigator extends AdapterBase implements Investigator {
|
||||
|
|
@ -54,13 +52,15 @@ public class KVMInvestigator extends AdapterBase implements Investigator {
|
|||
private HAManager haManager;
|
||||
@Inject
|
||||
private DataStoreProviderManager dataStoreProviderMgr;
|
||||
@Inject
|
||||
private KVMHostActivityChecker hostActivityChecker;
|
||||
|
||||
@Override
|
||||
public boolean isVmAlive(com.cloud.vm.VirtualMachine vm, Host host) throws UnknownVM {
|
||||
if (haManager.isHAEligible(host)) {
|
||||
return haManager.isVMAliveOnHost(host);
|
||||
}
|
||||
Status status = isAgentAlive(host);
|
||||
Status status = getHostAgentStatus(host);
|
||||
logger.debug("HA: HOST is ineligible legacy state {} for host {}", status, host);
|
||||
if (status == null) {
|
||||
throw new UnknownVM();
|
||||
|
|
@ -73,86 +73,41 @@ public class KVMInvestigator extends AdapterBase implements Investigator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Status isAgentAlive(Host agent) {
|
||||
if (agent.getHypervisorType() != Hypervisor.HypervisorType.KVM && agent.getHypervisorType() != Hypervisor.HypervisorType.LXC) {
|
||||
public Status getHostAgentStatus(Host host) {
|
||||
if (host.getHypervisorType() != Hypervisor.HypervisorType.KVM && host.getHypervisorType() != Hypervisor.HypervisorType.LXC) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (haManager.isHAEligible(agent)) {
|
||||
return haManager.getHostStatus(agent);
|
||||
if (haManager.isHAEligible(host)) {
|
||||
return haManager.getHostStatusFromHAConfig(host);
|
||||
}
|
||||
|
||||
List<StoragePoolVO> clusterPools = _storagePoolDao.findPoolsInClusters(Arrays.asList(agent.getClusterId()), null);
|
||||
boolean storageSupportHA = storageSupportHa(clusterPools);
|
||||
if (!storageSupportHA) {
|
||||
List<StoragePoolVO> zonePools = _storagePoolDao.findZoneWideStoragePoolsByHypervisor(agent.getDataCenterId(), agent.getHypervisorType());
|
||||
storageSupportHA = storageSupportHa(zonePools);
|
||||
List<StoragePoolVO> clusterPools = _storagePoolDao.findPoolsInClusters(Collections.singletonList(host.getClusterId()), null);
|
||||
boolean storageSupportsHA = storageSupportsHA(clusterPools);
|
||||
if (!storageSupportsHA) {
|
||||
List<StoragePoolVO> zonePools = _storagePoolDao.findZoneWideStoragePoolsByHypervisor(host.getDataCenterId(), host.getHypervisorType());
|
||||
storageSupportsHA = storageSupportsHA(zonePools);
|
||||
}
|
||||
if (!storageSupportHA) {
|
||||
logger.warn("Agent investigation was requested on host {}, but host does not support investigation because it has no NFS storage. Skipping investigation.", agent);
|
||||
if (!storageSupportsHA) {
|
||||
logger.warn("Agent investigation was requested on host {}, but host does not support investigation" +
|
||||
" because it has no HA supported storage. Skipping investigation.", host);
|
||||
return null;
|
||||
}
|
||||
|
||||
Status hostStatus = null;
|
||||
Status neighbourStatus = null;
|
||||
boolean reportFailureIfOneStorageIsDown = HighAvailabilityManager.KvmHAFenceHostIfHeartbeatFailsOnStorage.value();
|
||||
CheckOnHostCommand cmd = new CheckOnHostCommand(agent, reportFailureIfOneStorageIsDown);
|
||||
|
||||
try {
|
||||
Answer answer = _agentMgr.easySend(agent.getId(), cmd);
|
||||
if (answer != null) {
|
||||
hostStatus = answer.getResult() ? Status.Down : Status.Up;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to send command to host: {}", agent);
|
||||
}
|
||||
if (hostStatus == null) {
|
||||
hostStatus = Status.Disconnected;
|
||||
}
|
||||
|
||||
List<HostVO> neighbors = _resourceMgr.listHostsInClusterByStatus(agent.getClusterId(), Status.Up);
|
||||
for (HostVO neighbor : neighbors) {
|
||||
if (neighbor.getId() == agent.getId()
|
||||
|| (neighbor.getHypervisorType() != Hypervisor.HypervisorType.KVM && neighbor.getHypervisorType() != Hypervisor.HypervisorType.LXC)) {
|
||||
continue;
|
||||
}
|
||||
logger.debug("Investigating host:{} via neighbouring host:{}", agent, neighbor);
|
||||
try {
|
||||
Answer answer = _agentMgr.easySend(neighbor.getId(), cmd);
|
||||
if (answer != null) {
|
||||
neighbourStatus = answer.getResult() ? Status.Down : Status.Up;
|
||||
logger.debug("Neighbouring host:{} returned status:{} for the investigated host:{}", neighbor, neighbourStatus, agent);
|
||||
if (neighbourStatus == Status.Up) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to send command to host: {}", neighbor);
|
||||
}
|
||||
}
|
||||
if (neighbourStatus == Status.Up && (hostStatus == Status.Disconnected || hostStatus == Status.Down)) {
|
||||
hostStatus = Status.Disconnected;
|
||||
}
|
||||
if (neighbourStatus == Status.Down && (hostStatus == Status.Disconnected || hostStatus == Status.Down)) {
|
||||
hostStatus = Status.Down;
|
||||
}
|
||||
logger.debug("HA: HOST is ineligible legacy state {} for host {}", hostStatus, agent);
|
||||
return hostStatus;
|
||||
return hostActivityChecker.getHostAgentStatus(host);
|
||||
}
|
||||
|
||||
private boolean storageSupportHa(List<StoragePoolVO> pools) {
|
||||
boolean storageSupportHA = false;
|
||||
private boolean storageSupportsHA(List<StoragePoolVO> pools) {
|
||||
for (StoragePoolVO pool : pools) {
|
||||
DataStoreProvider storeProvider = dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName());
|
||||
DataStoreDriver storeDriver = storeProvider.getDataStoreDriver();
|
||||
if (storeDriver instanceof PrimaryDataStoreDriver) {
|
||||
PrimaryDataStoreDriver primaryStoreDriver = (PrimaryDataStoreDriver)storeDriver;
|
||||
if (primaryStoreDriver.isStorageSupportHA(pool.getPoolType())) {
|
||||
storageSupportHA = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return storageSupportHA;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,10 +35,9 @@ import com.cloud.agent.properties.AgentPropertiesFileHandler;
|
|||
public class KVMHABase {
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
private long _timeout = 60000; /* 1 minutes */
|
||||
protected long _heartBeatUpdateTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HEARTBEAT_UPDATE_TIMEOUT);
|
||||
protected long _heartBeatUpdateFreq = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_FREQUENCY);
|
||||
protected long _heartBeatUpdateFreqInMs = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_FREQUENCY);
|
||||
protected long _heartBeatUpdateMaxTries = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_MAX_TRIES);
|
||||
protected long _heartBeatUpdateRetrySleep = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_RETRY_SLEEP);
|
||||
protected long _heartBeatUpdateRetrySleepInMs = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_RETRY_SLEEP);
|
||||
|
||||
public static enum PoolType {
|
||||
PrimaryStorage, SecondaryStorage
|
||||
|
|
@ -138,7 +137,7 @@ public class KVMHABase {
|
|||
/* Can't find the mount point? */
|
||||
/* we need to mount it under poolName */
|
||||
if (poolName != null) {
|
||||
Script mount = new Script("/bin/bash", 60000);
|
||||
Script mount = new Script("/bin/bash", _timeout);
|
||||
mount.add("-c");
|
||||
mount.add("mount " + mountSource + " " + destPath);
|
||||
String result = mount.execute();
|
||||
|
|
@ -154,7 +153,6 @@ public class KVMHABase {
|
|||
}
|
||||
|
||||
protected String getMountPoint(HAStoragePool storagePool) {
|
||||
|
||||
StoragePool pool = null;
|
||||
String poolName = null;
|
||||
try {
|
||||
|
|
@ -171,7 +169,6 @@ public class KVMHABase {
|
|||
}
|
||||
poolName = pool.getName();
|
||||
}
|
||||
|
||||
} catch (LibvirtException e) {
|
||||
logger.debug("Ignoring libvirt error.", e);
|
||||
} finally {
|
||||
|
|
@ -234,7 +231,7 @@ public class KVMHABase {
|
|||
return result;
|
||||
}
|
||||
|
||||
public Boolean checkingHeartBeat() {
|
||||
public Boolean hasHeartBeat() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,44 +26,43 @@ import com.cloud.agent.api.to.HostTO;
|
|||
public class KVMHAChecker extends KVMHABase implements Callable<Boolean> {
|
||||
private List<HAStoragePool> storagePools;
|
||||
private HostTO host;
|
||||
private boolean reportFailureIfOneStorageIsDown;
|
||||
private boolean reportIfHeartBeatFailedForOneStoragePool;
|
||||
|
||||
public KVMHAChecker(List<HAStoragePool> pools, HostTO host, boolean reportFailureIfOneStorageIsDown) {
|
||||
public KVMHAChecker(List<HAStoragePool> pools, HostTO host, boolean reportIfHeartBeatFailedForOneStoragePool) {
|
||||
this.storagePools = pools;
|
||||
this.host = host;
|
||||
this.reportFailureIfOneStorageIsDown = reportFailureIfOneStorageIsDown;
|
||||
this.reportIfHeartBeatFailedForOneStoragePool = reportIfHeartBeatFailedForOneStoragePool;
|
||||
}
|
||||
|
||||
/*
|
||||
* True means heartbeaing is on going, or we can't get it's status. False
|
||||
* means heartbeating is stopped definitely
|
||||
* True means heart beating is on going, or we can't get it's status.
|
||||
* False means heart beating is stopped definitely.
|
||||
*/
|
||||
@Override
|
||||
public Boolean checkingHeartBeat() {
|
||||
boolean validResult = false;
|
||||
|
||||
String hostAndPools = String.format("host IP [%s] in pools [%s]", host.getPrivateNetwork().getIp(), storagePools.stream().map(pool -> pool.getPoolUUID()).collect(Collectors.joining(", ")));
|
||||
|
||||
logger.debug(String.format("Checking heart beat with KVMHAChecker for %s", hostAndPools));
|
||||
public Boolean hasHeartBeat() {
|
||||
String hostAndPools = String.format("host IP [%s] in pools [%s]", host.getPrivateNetwork().getIp(),
|
||||
storagePools.stream().map(pool -> pool.getPoolUUID()).collect(Collectors.joining(", ")));
|
||||
logger.debug("Checking heart beat with KVMHAChecker for {}", hostAndPools);
|
||||
|
||||
boolean heartBeatCheckResult = false;
|
||||
for (HAStoragePool pool : storagePools) {
|
||||
validResult = pool.getPool().checkingHeartBeat(pool, host);
|
||||
if (reportFailureIfOneStorageIsDown && !validResult) {
|
||||
heartBeatCheckResult = pool.getPool().hasHeartBeat(pool, host);
|
||||
if (reportIfHeartBeatFailedForOneStoragePool && !heartBeatCheckResult) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validResult) {
|
||||
logger.warn(String.format("All checks with KVMHAChecker for %s considered it as dead. It may cause a shutdown of the host.", hostAndPools));
|
||||
if (!heartBeatCheckResult) {
|
||||
logger.warn("All checks with KVMHAChecker for {} considered it as dead. It may cause a shutdown of the host.", hostAndPools);
|
||||
}
|
||||
|
||||
return validResult;
|
||||
return heartBeatCheckResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
// logger.addAppender(new org.apache.log4j.ConsoleAppender(new
|
||||
// org.apache.log4j.PatternLayout(), "System.out"));
|
||||
return checkingHeartBeat();
|
||||
return hasHeartBeat();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,53 +34,49 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
|
||||
public class KVMHAMonitor extends KVMHABase implements Runnable {
|
||||
|
||||
private final Map<String, HAStoragePool> storagePool = new ConcurrentHashMap<>();
|
||||
private final Map<String, HAStoragePool> haStoragePools = new ConcurrentHashMap<>();
|
||||
private final boolean rebootHostAndAlertManagementOnHeartbeatTimeout;
|
||||
|
||||
private final String hostPrivateIp;
|
||||
|
||||
public KVMHAMonitor(HAStoragePool pool, String host) {
|
||||
if (pool != null) {
|
||||
storagePool.put(pool.getPoolUUID(), pool);
|
||||
}
|
||||
public KVMHAMonitor(String host) {
|
||||
hostPrivateIp = host;
|
||||
|
||||
rebootHostAndAlertManagementOnHeartbeatTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.REBOOT_HOST_AND_ALERT_MANAGEMENT_ON_HEARTBEAT_TIMEOUT);
|
||||
}
|
||||
|
||||
public void addStoragePool(HAStoragePool pool) {
|
||||
synchronized (storagePool) {
|
||||
storagePool.put(pool.getPoolUUID(), pool);
|
||||
synchronized (haStoragePools) {
|
||||
haStoragePools.put(pool.getPoolUUID(), pool);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeStoragePool(String uuid) {
|
||||
synchronized (storagePool) {
|
||||
HAStoragePool pool = storagePool.get(uuid);
|
||||
synchronized (haStoragePools) {
|
||||
HAStoragePool pool = haStoragePools.get(uuid);
|
||||
if (pool != null) {
|
||||
Script.runSimpleBashScript("umount " + pool.getMountDestPath());
|
||||
storagePool.remove(uuid);
|
||||
haStoragePools.remove(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<HAStoragePool> getStoragePools() {
|
||||
synchronized (storagePool) {
|
||||
return new ArrayList<>(storagePool.values());
|
||||
synchronized (haStoragePools) {
|
||||
return new ArrayList<>(haStoragePools.values());
|
||||
}
|
||||
}
|
||||
|
||||
public HAStoragePool getStoragePool(String uuid) {
|
||||
synchronized (storagePool) {
|
||||
return storagePool.get(uuid);
|
||||
synchronized (haStoragePools) {
|
||||
return haStoragePools.get(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
protected void runHeartBeat() {
|
||||
synchronized (storagePool) {
|
||||
synchronized (haStoragePools) {
|
||||
Set<String> removedPools = new HashSet<>();
|
||||
for (String uuid : storagePool.keySet()) {
|
||||
HAStoragePool primaryStoragePool = storagePool.get(uuid);
|
||||
for (String uuid : haStoragePools.keySet()) {
|
||||
HAStoragePool primaryStoragePool = haStoragePools.get(uuid);
|
||||
if (HighAvailabilityManager.LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(primaryStoragePool.getPool().getType())) {
|
||||
checkForNotExistingLibvirtStoragePools(removedPools, uuid);
|
||||
if (removedPools.contains(uuid)) {
|
||||
|
|
@ -91,7 +87,7 @@ public class KVMHAMonitor extends KVMHABase implements Runnable {
|
|||
result = executePoolHeartBeatCommand(uuid, primaryStoragePool, result);
|
||||
|
||||
if (result != null && rebootHostAndAlertManagementOnHeartbeatTimeout) {
|
||||
logger.warn(String.format("Write heartbeat for pool [%s] failed: %s; stopping cloudstack-agent.", uuid, result));
|
||||
logger.warn("Write heartbeat for pool [{}] failed: {}; stopping cloudstack-agent.", uuid, result);
|
||||
primaryStoragePool.getPool().createHeartBeatCommand(primaryStoragePool, null, false);;
|
||||
}
|
||||
}
|
||||
|
|
@ -104,20 +100,18 @@ public class KVMHAMonitor extends KVMHABase implements Runnable {
|
|||
}
|
||||
|
||||
private String executePoolHeartBeatCommand(String uuid, HAStoragePool primaryStoragePool, String result) {
|
||||
for (int i = 1; i <= _heartBeatUpdateMaxTries; i++) {
|
||||
for (int attempt = 1; attempt <= _heartBeatUpdateMaxTries; attempt++) {
|
||||
result = primaryStoragePool.getPool().createHeartBeatCommand(primaryStoragePool, hostPrivateIp, true);
|
||||
|
||||
if (result != null) {
|
||||
logger.warn(String.format("Write heartbeat for pool [%s] failed: %s; try: %s of %s.", uuid, result, i, _heartBeatUpdateMaxTries));
|
||||
try {
|
||||
Thread.sleep(_heartBeatUpdateRetrySleep);
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("[IGNORED] Interrupted between heartbeat retries.", e);
|
||||
}
|
||||
} else {
|
||||
if (result == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
logger.warn("Write heartbeat for pool [{}] failed: {}; try: {} of {}.", uuid, result, attempt, _heartBeatUpdateMaxTries);
|
||||
try {
|
||||
Thread.sleep(_heartBeatUpdateRetrySleepInMs);
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("[IGNORED] Interrupted between heartbeat retries.", e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -128,21 +122,21 @@ public class KVMHAMonitor extends KVMHABase implements Runnable {
|
|||
StoragePool storage = conn.storagePoolLookupByUUIDString(uuid);
|
||||
if (storage == null || storage.getInfo().state != StoragePoolState.VIR_STORAGE_POOL_RUNNING) {
|
||||
if (storage == null) {
|
||||
logger.debug(String.format("Libvirt storage pool [%s] not found, removing from HA list.", uuid));
|
||||
logger.debug("Libvirt storage pool [{}] not found, removing from HA list.", uuid);
|
||||
} else {
|
||||
logger.debug(String.format("Libvirt storage pool [%s] found, but not running, removing from HA list.", uuid));
|
||||
logger.debug("Libvirt storage pool [{}] found, but not running, removing from HA list.", uuid);
|
||||
}
|
||||
|
||||
removedPools.add(uuid);
|
||||
}
|
||||
|
||||
logger.debug(String.format("Found NFS storage pool [%s] in libvirt, continuing.", uuid));
|
||||
logger.debug("Found NFS storage pool [{}] in libvirt, continuing.", uuid);
|
||||
|
||||
} catch (LibvirtException e) {
|
||||
logger.debug(String.format("Failed to lookup libvirt storage pool [%s].", uuid), e);
|
||||
logger.debug("Failed to lookup libvirt storage pool [{}].", uuid, e);
|
||||
|
||||
if (e.toString().contains("pool not found")) {
|
||||
logger.debug(String.format("Removing pool [%s] from HA monitor since it was deleted.", uuid));
|
||||
logger.debug("Removing pool [{}] from HA monitor since it was deleted.", uuid);
|
||||
removedPools.add(uuid);
|
||||
}
|
||||
}
|
||||
|
|
@ -155,11 +149,10 @@ public class KVMHAMonitor extends KVMHABase implements Runnable {
|
|||
runHeartBeat();
|
||||
|
||||
try {
|
||||
Thread.sleep(_heartBeatUpdateFreq);
|
||||
Thread.sleep(_heartBeatUpdateFreqInMs);
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("[IGNORED] Interrupted between heartbeats.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,12 +39,12 @@ public class KVMHAVMActivityChecker extends KVMHABase implements Callable<Boolea
|
|||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkingHeartBeat() {
|
||||
return this.storagePool.getPool().vmActivityCheck(storagePool, host, activityScriptTimeout, volumeUuidList, vmActivityCheckPath, suspectTimeInSeconds);
|
||||
public Boolean hasHeartBeat() {
|
||||
return this.storagePool.getPool().hasVmActivity(storagePool, host, activityScriptTimeout, volumeUuidList, vmActivityCheckPath, suspectTimeInSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
return checkingHeartBeat();
|
||||
return hasHeartBeat();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ import org.libvirt.DomainSnapshot;
|
|||
import org.libvirt.LibvirtException;
|
||||
import org.libvirt.MemoryStatistic;
|
||||
import org.libvirt.Network;
|
||||
import org.libvirt.SchedLongParameter;
|
||||
import org.libvirt.SchedParameter;
|
||||
import org.libvirt.SchedUlongParameter;
|
||||
import org.libvirt.Secret;
|
||||
|
|
@ -883,6 +884,25 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
SUCCESS, FAILURE, IGNORE
|
||||
}
|
||||
|
||||
public enum CpuSchedulerParameter {
|
||||
CPU_SHARES("cpu_shares"), PERIOD("vcpu_period"), QUOTA("vcpu_quota");
|
||||
|
||||
private String name;
|
||||
|
||||
CpuSchedulerParameter(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
protected BridgeType bridgeType;
|
||||
|
||||
protected StorageSubsystemCommandHandler storageHandler;
|
||||
|
|
@ -1388,9 +1408,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
|
||||
final String[] info = NetUtils.getNetworkParams(privateNic);
|
||||
|
||||
kvmhaMonitor = new KVMHAMonitor(null, info[0]);
|
||||
final Thread ha = new Thread(kvmhaMonitor);
|
||||
ha.start();
|
||||
kvmhaMonitor = new KVMHAMonitor(info[0]);
|
||||
final Thread haMonitorThread = new Thread(kvmhaMonitor);
|
||||
haMonitorThread.start();
|
||||
|
||||
storagePoolManager = new KVMStoragePoolManager(storageLayer, kvmhaMonitor);
|
||||
|
||||
|
|
@ -2965,25 +2985,63 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
protected void setQuotaAndPeriod(VirtualMachineTO vmTO, CpuTuneDef ctd) {
|
||||
if (vmTO.isLimitCpuUse() && vmTO.getCpuQuotaPercentage() != null) {
|
||||
Double cpuQuotaPercentage = vmTO.getCpuQuotaPercentage();
|
||||
int period = CpuTuneDef.DEFAULT_PERIOD;
|
||||
int quota = (int) (period * cpuQuotaPercentage);
|
||||
if (quota < CpuTuneDef.MIN_QUOTA) {
|
||||
LOGGER.info("Calculated quota (" + quota + ") below the minimum (" + CpuTuneDef.MIN_QUOTA + ") for VM domain " + vmTO.getUuid() + ", setting it to minimum " +
|
||||
"and calculating period instead of using the default");
|
||||
quota = CpuTuneDef.MIN_QUOTA;
|
||||
period = (int) ((double) quota / cpuQuotaPercentage);
|
||||
if (period > CpuTuneDef.MAX_PERIOD) {
|
||||
LOGGER.info("Calculated period (" + period + ") exceeds the maximum (" + CpuTuneDef.MAX_PERIOD +
|
||||
"), setting it to the maximum");
|
||||
period = CpuTuneDef.MAX_PERIOD;
|
||||
}
|
||||
}
|
||||
ctd.setQuota(quota);
|
||||
ctd.setPeriod(period);
|
||||
LOGGER.info("Setting quota=" + quota + ", period=" + period + " to VM domain " + vmTO.getUuid());
|
||||
Pair<Integer, Long> periodAndQuota = getPeriodAndQuota(cpuQuotaPercentage);
|
||||
ctd.setPeriod(periodAndQuota.first());
|
||||
ctd.setQuota(periodAndQuota.second());
|
||||
LOGGER.info("Setting quota = [{}] and period = [{}] to VM domain [{}].", periodAndQuota.second(), periodAndQuota.first(), vmTO.getUuid());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the CPU period and quota based on the quota percentage defined by the Management Server
|
||||
* @param cpuQuotaPercentage CPU quota percentage defined by the Management Server
|
||||
* @return The period and quota to be defined for the VM's domain
|
||||
*/
|
||||
protected Pair<Integer, Long> getPeriodAndQuota(double cpuQuotaPercentage) {
|
||||
int period = CpuTuneDef.DEFAULT_PERIOD;
|
||||
long quota = (long) (period * cpuQuotaPercentage);
|
||||
if (quota < CpuTuneDef.MIN_QUOTA) {
|
||||
LOGGER.info("Calculated quota ({}) below the minimum ({}), setting it to minimum and calculating period instead of using the default", quota, CpuTuneDef.MIN_QUOTA);
|
||||
quota = CpuTuneDef.MIN_QUOTA;
|
||||
period = (int) ((double) quota / cpuQuotaPercentage);
|
||||
if (period > CpuTuneDef.MAX_PERIOD) {
|
||||
LOGGER.info("Calculated period ({}) exceeds the maximum ({}), setting it to the maximum", period, CpuTuneDef.MAX_PERIOD);
|
||||
period = CpuTuneDef.MAX_PERIOD;
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("Calculated period = [{}] and quota = [{}] given the [{}] quota percentage.", period, quota, cpuQuotaPercentage);
|
||||
return new Pair<>(period, quota);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically updates the domain's "vcpu_quota" and "period" fields of the CPU tune definition.
|
||||
* This is required because the values of the fields must change according to the new CPU speed of the VM.
|
||||
* When the CPU limitation is removed from the domain, the "vcpu_quota" field is set to 17,592,186,044,415.
|
||||
* @param domain VM's domain.
|
||||
* @param vmTO VM's transfer object, which contains the required fields to update the "vcpu_quota" and "period" fields.
|
||||
* @param limitCpuUseChange Indicates whether the CPU limitation for the VM has changed.
|
||||
* @throws org.libvirt.LibvirtException
|
||||
**/
|
||||
public void updateCpuQuotaAndPeriod(Domain domain, VirtualMachineTO vmTO, boolean limitCpuUseChange) throws LibvirtException {
|
||||
if (hypervisorLibvirtVersion < MIN_LIBVIRT_VERSION_FOR_GUEST_CPU_TUNE || (!limitCpuUseChange && !vmTO.isLimitCpuUse())) {
|
||||
logger.info("Not updating the [{}] and [{}] for the [{}] domain, because [{}].",
|
||||
CpuSchedulerParameter.QUOTA, CpuSchedulerParameter.PERIOD, domain.getName(), hypervisorLibvirtVersion < MIN_LIBVIRT_VERSION_FOR_GUEST_CPU_TUNE ?
|
||||
"the current Libvirt version does not support CPU tune" : "it was not requested to remove, change or apply CPU limitation for the instance.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (limitCpuUseChange && !vmTO.isLimitCpuUse()) {
|
||||
logger.info("Updating the [{}] of the [{}] domain to [{}], because CPU limitation has been removed.", CpuSchedulerParameter.QUOTA, domain.getName(), CpuTuneDef.MAX_CPU_QUOTA);
|
||||
LibvirtComputingResource.setQuota(domain, CpuTuneDef.MAX_CPU_QUOTA);
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<Integer, Long> periodAndQuota = getPeriodAndQuota(vmTO.getCpuQuotaPercentage());
|
||||
LibvirtComputingResource.setPeriod(domain, periodAndQuota.first());
|
||||
LibvirtComputingResource.setQuota(domain, periodAndQuota.second());
|
||||
}
|
||||
|
||||
protected void enlightenWindowsVm(VirtualMachineTO vmTO, FeaturesDef features) {
|
||||
if (vmTO.getOs().contains("Windows PV")) {
|
||||
// If OS is Windows PV, then enable the features. Features supported on Windows 2008 and later
|
||||
|
|
@ -3445,10 +3503,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
|
||||
grd.setMemBalloning(!noMemBalloon);
|
||||
|
||||
Long maxRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMaxRam());
|
||||
|
||||
grd.setMemorySize(maxRam);
|
||||
grd.setCurrentMem(getCurrentMemAccordingToMemBallooning(vmTO, maxRam));
|
||||
long requestedRam = ByteScaleUtils.bytesToKibibytes(vmTO.getRequestedRam());
|
||||
long minRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMinRam());
|
||||
grd.setCurrentMem(getCurrentMemAccordingToMemBallooning(vmTO, requestedRam, minRam));
|
||||
grd.setMaxMemory(ByteScaleUtils.bytesToKibibytes(vmTO.getMaxRam()));
|
||||
|
||||
int vcpus = vmTO.getCpus();
|
||||
Integer maxVcpus = vmTO.getVcpuMaxLimit();
|
||||
|
|
@ -3459,18 +3517,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
return grd;
|
||||
}
|
||||
|
||||
protected long getCurrentMemAccordingToMemBallooning(VirtualMachineTO vmTO, long maxRam) {
|
||||
long retVal = maxRam;
|
||||
protected long getCurrentMemAccordingToMemBallooning(VirtualMachineTO vmTO, long requestedRam, long minRam) {
|
||||
if (noMemBalloon) {
|
||||
LOGGER.warn(String.format("Setting VM's [%s] current memory as max memory [%s] due to memory ballooning is disabled. If you are using a custom service offering, verify if memory ballooning really should be disabled.", vmTO.toString(), maxRam));
|
||||
} else if (vmTO != null && vmTO.getType() != VirtualMachine.Type.User) {
|
||||
LOGGER.warn(String.format("Setting System VM's [%s] current memory as max memory [%s].", vmTO.toString(), maxRam));
|
||||
} else {
|
||||
long minRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMinRam());
|
||||
LOGGER.debug(String.format("Setting VM's [%s] current memory as min memory [%s] due to memory ballooning is enabled.", vmTO.toString(), minRam));
|
||||
retVal = minRam;
|
||||
LOGGER.warn("Setting VM's [{}] current memory as requested memory [{}] due to memory ballooning is disabled.", vmTO.toString(), requestedRam);
|
||||
return requestedRam;
|
||||
}
|
||||
return retVal;
|
||||
|
||||
if (vmTO != null && vmTO.getType() != VirtualMachine.Type.User) {
|
||||
LOGGER.warn("Setting System VM's [{}] current memory as requested memory [{}].", vmTO.toString(), requestedRam);
|
||||
return requestedRam;
|
||||
}
|
||||
|
||||
LOGGER.debug("Setting VM's [{}] current memory as min memory [{}] due to memory ballooning is enabled.", vmTO.toString(), minRam);
|
||||
return minRam;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -6255,29 +6314,59 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
**/
|
||||
public static Integer getCpuShares(Domain dm) throws LibvirtException {
|
||||
for (SchedParameter c : dm.getSchedulerParameters()) {
|
||||
if (c.field.equals("cpu_shares")) {
|
||||
if (c.field.equals(CpuSchedulerParameter.CPU_SHARES.getName())) {
|
||||
return Integer.parseInt(c.getValueAsString());
|
||||
}
|
||||
}
|
||||
LOGGER.warn(String.format("Could not get cpu_shares of domain: [%s]. Returning default value of 0. ", dm.getName()));
|
||||
LOGGER.warn("Could not get [{}] of domain: [{}]. Returning default value of 0. ", CpuSchedulerParameter.CPU_SHARES.getName(), dm.getName());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cpu_shares (priority) of the running VM <br/>
|
||||
* Updates the cpu_shares (priority) of the running VM.
|
||||
* @param dm domain of the VM.
|
||||
* @param cpuShares new priority of the running VM.
|
||||
* @throws org.libvirt.LibvirtException
|
||||
**/
|
||||
public static void setCpuShares(Domain dm, Integer cpuShares) throws LibvirtException {
|
||||
LOGGER.info("Dynamically updating the [{}] of the [{}] VM to [{}].", CpuSchedulerParameter.CPU_SHARES.getName(), dm.getName(), cpuShares);
|
||||
SchedUlongParameter[] params = new SchedUlongParameter[1];
|
||||
params[0] = new SchedUlongParameter();
|
||||
params[0].field = "cpu_shares";
|
||||
params[0].field = CpuSchedulerParameter.CPU_SHARES.getName();
|
||||
params[0].value = cpuShares;
|
||||
|
||||
dm.setSchedulerParameters(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the period of the running VM.
|
||||
* @param domain domain of the VM.
|
||||
* @param period new period of the running VM.
|
||||
**/
|
||||
public static void setPeriod(Domain domain, int period) throws LibvirtException {
|
||||
LOGGER.info("Dynamically updating the [{}] of the [{}] VM to [{}].", CpuSchedulerParameter.PERIOD.getName(), domain.getName(), period);
|
||||
SchedUlongParameter[] params = new SchedUlongParameter[1];
|
||||
params[0] = new SchedUlongParameter();
|
||||
params[0].field = CpuSchedulerParameter.PERIOD.getName();
|
||||
params[0].value = period;
|
||||
|
||||
domain.setSchedulerParameters(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the quota of the running VM.
|
||||
* @param domain domain of the VM.
|
||||
* @param quota new quota of the running VM.
|
||||
**/
|
||||
public static void setQuota(Domain domain, long quota) throws LibvirtException {
|
||||
LOGGER.info("Dynamically updating the [{}] of the [{}] VM to [{}].", CpuSchedulerParameter.QUOTA.getName(), domain.getName(), quota);
|
||||
SchedLongParameter[] params = new SchedLongParameter[1];
|
||||
params[0] = new SchedLongParameter();
|
||||
params[0].field = CpuSchedulerParameter.QUOTA.getName();
|
||||
params[0].value = quota;
|
||||
|
||||
domain.setSchedulerParameters(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a libvirt secret for a volume. If Libvirt says that a secret already exists for this volume path, we use its uuid.
|
||||
* The UUID of the secret needs to be prescriptive such that we can register the same UUID on target host during live migration
|
||||
|
|
|
|||
|
|
@ -542,7 +542,7 @@ public class LibvirtDomainXMLParser {
|
|||
|
||||
final String quota = getTagValue("quota", cpuTuneDefElement);
|
||||
if (StringUtils.isNotBlank(quota)) {
|
||||
cpuTuneDef.setQuota((Integer.parseInt(quota)));
|
||||
cpuTuneDef.setQuota((Long.parseLong(quota)));
|
||||
}
|
||||
|
||||
final String period = getTagValue("period", cpuTuneDefElement);
|
||||
|
|
|
|||
|
|
@ -443,15 +443,15 @@ public class LibvirtVMDef {
|
|||
}
|
||||
|
||||
public static class GuestResourceDef {
|
||||
private long memory;
|
||||
private long maxMemory;
|
||||
private long currentMemory = -1;
|
||||
private int vcpu = -1;
|
||||
private int maxVcpu = -1;
|
||||
private boolean memoryBalloning = false;
|
||||
private int memoryBalloonStatsPeriod = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VM_MEMBALLOON_STATS_PERIOD);
|
||||
|
||||
public void setMemorySize(long mem) {
|
||||
this.memory = mem;
|
||||
public void setMaxMemory(long mem) {
|
||||
this.maxMemory = mem;
|
||||
}
|
||||
|
||||
public void setCurrentMem(long currMem) {
|
||||
|
|
@ -484,8 +484,8 @@ public class LibvirtVMDef {
|
|||
response.append(String.format("<memory>%s</memory>\n", this.currentMemory));
|
||||
response.append(String.format("<currentMemory>%s</currentMemory>\n", this.currentMemory));
|
||||
|
||||
if (this.memory > this.currentMemory) {
|
||||
response.append(String.format("<maxMemory slots='16' unit='KiB'>%s</maxMemory>\n", this.memory));
|
||||
if (this.maxMemory > this.currentMemory) {
|
||||
response.append(String.format("<maxMemory slots='16' unit='KiB'>%s</maxMemory>\n", this.maxMemory));
|
||||
response.append(String.format("<cpu> <numa> <cell id='0' cpus='0-%s' memory='%s' unit='KiB'/> </numa> </cpu>\n", this.maxVcpu - 1, this.currentMemory));
|
||||
}
|
||||
|
||||
|
|
@ -1920,11 +1920,12 @@ public class LibvirtVMDef {
|
|||
|
||||
public static class CpuTuneDef {
|
||||
private int _shares = 0;
|
||||
private int quota = 0;
|
||||
private long quota = 0;
|
||||
private int period = 0;
|
||||
static final int DEFAULT_PERIOD = 10000;
|
||||
static final int MIN_QUOTA = 1000;
|
||||
static final int MAX_PERIOD = 1000000;
|
||||
public static final long MAX_CPU_QUOTA = 17592186044415L;
|
||||
|
||||
public void setShares(int shares) {
|
||||
_shares = shares;
|
||||
|
|
@ -1934,11 +1935,11 @@ public class LibvirtVMDef {
|
|||
return _shares;
|
||||
}
|
||||
|
||||
public int getQuota() {
|
||||
public long getQuota() {
|
||||
return quota;
|
||||
}
|
||||
|
||||
public void setQuota(int quota) {
|
||||
public void setQuota(long quota) {
|
||||
this.quota = quota;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.Future;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.CheckOnHostAnswer;
|
||||
import com.cloud.agent.api.CheckOnHostCommand;
|
||||
import com.cloud.agent.api.to.HostTO;
|
||||
import com.cloud.hypervisor.kvm.resource.KVMHABase.HAStoragePool;
|
||||
|
|
@ -45,20 +46,21 @@ public final class LibvirtCheckOnHostCommandWrapper extends CommandWrapper<Check
|
|||
|
||||
final List<HAStoragePool> pools = monitor.getStoragePools();
|
||||
final HostTO host = command.getHost();
|
||||
final KVMHAChecker ha = new KVMHAChecker(pools, host, command.isCheckFailedOnOneStorage());
|
||||
|
||||
final KVMHAChecker ha = new KVMHAChecker(pools, host, command.shouldReportIfHeartBeatFailedForOneStoragePool());
|
||||
|
||||
final Future<Boolean> future = executors.submit(ha);
|
||||
try {
|
||||
final Boolean result = future.get();
|
||||
if (result) {
|
||||
return new Answer(command, false, "Heart is beating...");
|
||||
final Boolean hasHeartBeat = future.get();
|
||||
if (hasHeartBeat) {
|
||||
return new CheckOnHostAnswer(command, true, "Heart is beating");
|
||||
} else {
|
||||
return new Answer(command);
|
||||
return new CheckOnHostAnswer(command, "Heart is not beating");
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
return new Answer(command, false, "CheckOnHostCommand: can't get status of host: InterruptedException");
|
||||
return new CheckOnHostAnswer(command, "CheckOnHostCommand: can't get status of host: InterruptedException");
|
||||
} catch (final ExecutionException e) {
|
||||
return new Answer(command, false, "CheckOnHostCommand: can't get status of host: ExecutionException");
|
||||
return new CheckOnHostAnswer(command, "CheckOnHostCommand: can't get status of host: ExecutionException");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ public final class LibvirtCheckVMActivityOnStoragePoolCommandWrapper extends Com
|
|||
KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid());
|
||||
|
||||
if (primaryPool.isPoolSupportHA()) {
|
||||
final HAStoragePool nfspool = monitor.getStoragePool(pool.getUuid());
|
||||
final KVMHAVMActivityChecker ha = new KVMHAVMActivityChecker(nfspool, command.getHost(), command.getVolumeList(), libvirtComputingResource.getVmActivityCheckPath(), command.getSuspectTimeInSeconds());
|
||||
final HAStoragePool haPool = monitor.getStoragePool(pool.getUuid());
|
||||
final KVMHAVMActivityChecker ha = new KVMHAVMActivityChecker(haPool, command.getHost(), command.getVolumeList(), libvirtComputingResource.getVmActivityCheckPath(), command.getSuspectTimeInSeconds());
|
||||
final Future<Boolean> future = executors.submit(ha);
|
||||
try {
|
||||
final Boolean result = future.get();
|
||||
|
|
|
|||
|
|
@ -49,10 +49,11 @@ public class LibvirtScaleVmCommandWrapper extends CommandWrapper<ScaleVmCommand,
|
|||
conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
|
||||
Domain dm = conn.domainLookupByName(vmName);
|
||||
|
||||
logger.debug(String.format("Scaling %s.", scalingDetails));
|
||||
logger.debug("Scaling {}.", scalingDetails);
|
||||
scaleMemory(dm, newMemory, vmDefinition);
|
||||
scaleVcpus(dm, newVcpus, vmDefinition);
|
||||
updateCpuShares(dm, newCpuShares);
|
||||
libvirtComputingResource.updateCpuQuotaAndPeriod(dm, vmSpec, command.getLimitCpuUseChange());
|
||||
|
||||
return new ScaleVmAnswer(command, true, String.format("Successfully scaled %s.", scalingDetails));
|
||||
} catch (LibvirtException | CloudRuntimeException e) {
|
||||
|
|
@ -74,7 +75,7 @@ public class LibvirtScaleVmCommandWrapper extends CommandWrapper<ScaleVmCommand,
|
|||
|
||||
if (oldCpuShares < newCpuShares) {
|
||||
LibvirtComputingResource.setCpuShares(dm, newCpuShares);
|
||||
logger.info(String.format("Successfully increased cpu_shares of VM [%s] from [%s] to [%s].", dm.getName(), oldCpuShares, newCpuShares));
|
||||
logger.info("Successfully increased cpu_shares of VM [{}] from [{}] to [{}].", dm.getName(), oldCpuShares, newCpuShares);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -208,12 +208,12 @@ public class IscsiAdmStoragePool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkingHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
public Boolean hasHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
public Boolean hasVmActivity(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,35 +33,33 @@ import com.cloud.storage.Storage.StoragePoolType;
|
|||
|
||||
public interface KVMStoragePool {
|
||||
|
||||
public static final long HeartBeatUpdateTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HEARTBEAT_UPDATE_TIMEOUT);
|
||||
public static final long HeartBeatUpdateFreq = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_FREQUENCY);
|
||||
public static final long HeartBeatUpdateMaxTries = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_MAX_TRIES);
|
||||
public static final long HeartBeatUpdateRetrySleep = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_RETRY_SLEEP);
|
||||
public static final long HeartBeatCheckerTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_CHECKER_TIMEOUT);
|
||||
long HeartBeatUpdateTimeoutInMs = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HEARTBEAT_UPDATE_TIMEOUT);
|
||||
long HeartBeatUpdateFreqInMs = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_FREQUENCY);
|
||||
long HeartBeatCheckerTimeoutInMs = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_CHECKER_TIMEOUT);
|
||||
|
||||
public default KVMPhysicalDisk createPhysicalDisk(String volumeUuid, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, Long usableSize, byte[] passphrase) {
|
||||
default KVMPhysicalDisk createPhysicalDisk(String volumeUuid, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, Long usableSize, byte[] passphrase) {
|
||||
return createPhysicalDisk(volumeUuid, format, provisioningType, size, passphrase);
|
||||
}
|
||||
|
||||
public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase);
|
||||
KVMPhysicalDisk createPhysicalDisk(String volumeUuid, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase);
|
||||
|
||||
public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, Storage.ProvisioningType provisioningType, long size, byte[] passphrase);
|
||||
KVMPhysicalDisk createPhysicalDisk(String volumeUuid, Storage.ProvisioningType provisioningType, long size, byte[] passphrase);
|
||||
|
||||
public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details);
|
||||
boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details);
|
||||
|
||||
public KVMPhysicalDisk getPhysicalDisk(String volumeUuid);
|
||||
KVMPhysicalDisk getPhysicalDisk(String volumeUuid);
|
||||
|
||||
public boolean disconnectPhysicalDisk(String volumeUuid);
|
||||
boolean disconnectPhysicalDisk(String volumeUuid);
|
||||
|
||||
public boolean deletePhysicalDisk(String volumeUuid, Storage.ImageFormat format);
|
||||
boolean deletePhysicalDisk(String volumeUuid, Storage.ImageFormat format);
|
||||
|
||||
public List<KVMPhysicalDisk> listPhysicalDisks();
|
||||
List<KVMPhysicalDisk> listPhysicalDisks();
|
||||
|
||||
public String getUuid();
|
||||
String getUuid();
|
||||
|
||||
public long getCapacity();
|
||||
long getCapacity();
|
||||
|
||||
public long getUsed();
|
||||
long getUsed();
|
||||
|
||||
default Long getCapacityIops() {
|
||||
return null;
|
||||
|
|
@ -71,51 +69,51 @@ public interface KVMStoragePool {
|
|||
return null;
|
||||
}
|
||||
|
||||
public long getAvailable();
|
||||
long getAvailable();
|
||||
|
||||
public boolean refresh();
|
||||
boolean refresh();
|
||||
|
||||
public boolean isExternalSnapshot();
|
||||
boolean isExternalSnapshot();
|
||||
|
||||
public String getLocalPath();
|
||||
String getLocalPath();
|
||||
|
||||
public String getSourceHost();
|
||||
String getSourceHost();
|
||||
|
||||
public String getSourceDir();
|
||||
String getSourceDir();
|
||||
|
||||
public int getSourcePort();
|
||||
int getSourcePort();
|
||||
|
||||
public String getAuthUserName();
|
||||
String getAuthUserName();
|
||||
|
||||
public String getAuthSecret();
|
||||
String getAuthSecret();
|
||||
|
||||
public StoragePoolType getType();
|
||||
StoragePoolType getType();
|
||||
|
||||
public boolean delete();
|
||||
boolean delete();
|
||||
|
||||
PhysicalDiskFormat getDefaultFormat();
|
||||
|
||||
public boolean createFolder(String path);
|
||||
boolean createFolder(String path);
|
||||
|
||||
public boolean supportsConfigDriveIso();
|
||||
boolean supportsConfigDriveIso();
|
||||
|
||||
public Map<String, String> getDetails();
|
||||
Map<String, String> getDetails();
|
||||
|
||||
default String getLocalPathFor(String relativePath) {
|
||||
return String.format("%s%s%s", getLocalPath(), File.separator, relativePath);
|
||||
}
|
||||
|
||||
public boolean isPoolSupportHA();
|
||||
boolean isPoolSupportHA();
|
||||
|
||||
public String getHearthBeatPath();
|
||||
String getHearthBeatPath();
|
||||
|
||||
public String createHeartBeatCommand(HAStoragePool primaryStoragePool, String hostPrivateIp, boolean hostValidation);
|
||||
String createHeartBeatCommand(HAStoragePool primaryStoragePool, String hostPrivateIp, boolean hostValidation);
|
||||
|
||||
public String getStorageNodeId();
|
||||
String getStorageNodeId();
|
||||
|
||||
public Boolean checkingHeartBeat(HAStoragePool pool, HostTO host);
|
||||
Boolean hasHeartBeat(HAStoragePool pool, HostTO host);
|
||||
|
||||
public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration);
|
||||
Boolean hasVmActivity(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration);
|
||||
|
||||
default LibvirtVMDef.DiskDef.BlockIOSize getSupportedLogicalBlockSize() {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -2047,7 +2047,7 @@ public class KVMStorageProcessor implements StorageProcessor {
|
|||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new CloudRuntimeException(e);
|
||||
logger.trace("Thread that was tracking the progress for backup of VM [{}] was interrupted. Ignoring.", vmName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -345,16 +345,14 @@ public class LibvirtStoragePool implements KVMStoragePool {
|
|||
|
||||
|
||||
public String createHeartBeatCommand(HAStoragePool primaryStoragePool, String hostPrivateIp, boolean hostValidation) {
|
||||
Script cmd = new Script(primaryStoragePool.getPool().getHearthBeatPath(), HeartBeatUpdateTimeout, logger);
|
||||
Script cmd = new Script(primaryStoragePool.getPool().getHearthBeatPath(), HeartBeatUpdateTimeoutInMs, logger);
|
||||
cmd.add("-i", primaryStoragePool.getPoolIp());
|
||||
cmd.add("-p", primaryStoragePool.getPoolMountSourcePath());
|
||||
cmd.add("-m", primaryStoragePool.getMountDestPath());
|
||||
|
||||
if (hostValidation) {
|
||||
cmd.add("-h", hostPrivateIp);
|
||||
}
|
||||
|
||||
if (!hostValidation) {
|
||||
} else {
|
||||
cmd.add("-c");
|
||||
}
|
||||
|
||||
|
|
@ -372,53 +370,53 @@ public class LibvirtStoragePool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkingHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
boolean validResult = false;
|
||||
public Boolean hasHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
String hostIp = host.getPrivateNetwork().getIp();
|
||||
Script cmd = new Script(getHearthBeatPath(), HeartBeatCheckerTimeout, logger);
|
||||
Script cmd = new Script(getHearthBeatPath(), HeartBeatCheckerTimeoutInMs, logger);
|
||||
cmd.add("-i", pool.getPoolIp());
|
||||
cmd.add("-p", pool.getPoolMountSourcePath());
|
||||
cmd.add("-m", pool.getMountDestPath());
|
||||
cmd.add("-h", hostIp);
|
||||
cmd.add("-r");
|
||||
cmd.add("-t", String.valueOf(HeartBeatUpdateFreq / 1000));
|
||||
cmd.add("-t", String.valueOf(HeartBeatUpdateFreqInMs / 1000));
|
||||
OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser();
|
||||
String result = cmd.execute(parser);
|
||||
String parsedLine = parser.getLine();
|
||||
|
||||
logger.debug(String.format("Checking heart beat with KVMHAChecker [{command=\"%s\", result: \"%s\", log: \"%s\", pool: \"%s\"}].", cmd.toString(), result, parsedLine,
|
||||
pool.getPoolIp()));
|
||||
logger.debug("Checking heart beat for host IP {} with KVMHAChecker [{command=\"{}\", result: \"{}\", log: \"{}\", pool: \"{}\"}].", hostIp, cmd.toString(), result, parsedLine, pool.getPoolIp());
|
||||
|
||||
if (result == null && parsedLine.contains("DEAD")) {
|
||||
logger.warn(String.format("Checking heart beat with KVMHAChecker command [%s] returned [%s]. [%s]. It may cause a shutdown of host IP [%s].", cmd.toString(),
|
||||
result, parsedLine, hostIp));
|
||||
logger.warn("Checking heart beat for host IP {} with KVMHAChecker command [{}] returned [{}]. It may cause a shutdown of the host.", hostIp, cmd.toString(), parsedLine);
|
||||
return false;
|
||||
} else {
|
||||
validResult = true;
|
||||
logger.debug("Checking heart beat for host IP {} with KVMHAChecker command [{}] succeeded.", hostIp, cmd.toString());
|
||||
return true;
|
||||
}
|
||||
return validResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
public Boolean hasVmActivity(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
String hostIp = host.getPrivateNetwork().getIp();
|
||||
Script cmd = new Script(vmActivityCheckPath, activityScriptTimeout.getStandardSeconds(), logger);
|
||||
cmd.add("-i", pool.getPoolIp());
|
||||
cmd.add("-p", pool.getPoolMountSourcePath());
|
||||
cmd.add("-m", pool.getMountDestPath());
|
||||
cmd.add("-h", host.getPrivateNetwork().getIp());
|
||||
cmd.add("-h", hostIp);
|
||||
cmd.add("-u", volumeUUIDListString);
|
||||
cmd.add("-t", String.valueOf(String.valueOf(System.currentTimeMillis() / 1000)));
|
||||
cmd.add("-t", String.valueOf(System.currentTimeMillis() / 1000));
|
||||
cmd.add("-d", String.valueOf(duration));
|
||||
OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser();
|
||||
|
||||
String result = cmd.execute(parser);
|
||||
String parsedLine = parser.getLine();
|
||||
|
||||
logger.debug(String.format("Checking heart beat with KVMHAVMActivityChecker [{command=\"%s\", result: \"%s\", log: \"%s\", pool: \"%s\"}].", cmd.toString(), result, parsedLine, pool.getPoolIp()));
|
||||
logger.debug("Checking VM activity for host IP {} with KVMHAVMActivityChecker [{command=\"{}\", result: \"{}\", log: \"{}\", pool: \"{}\"}].", hostIp, cmd.toString(), result, parsedLine, pool.getPoolIp());
|
||||
|
||||
if (result == null && parsedLine.contains("DEAD")) {
|
||||
logger.warn(String.format("Checking heart beat with KVMHAVMActivityChecker command [%s] returned [%s]. It is [%s]. It may cause a shutdown of host IP [%s].", cmd.toString(), result, parsedLine, host.getPrivateNetwork().getIp()));
|
||||
logger.warn("Checking VM activity for host IP {} with KVMHAVMActivityChecker command [{}] returned [{}]. It may cause a shutdown of the host.", hostIp, cmd.toString(), parsedLine);
|
||||
return false;
|
||||
} else {
|
||||
logger.debug("Checking VM activity for host IP {} with KVMHAVMActivityChecker command [{}] succeeded.", hostIp, cmd.toString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,13 +225,13 @@ public class MultipathSCSIPool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkingHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
public Boolean hasHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activityScriptTimeout,
|
||||
String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
public Boolean hasVmActivity(HAStoragePool pool, HostTO host, Duration activityScriptTimeout,
|
||||
String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -236,12 +236,12 @@ public class ScaleIOStoragePool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkingHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
public Boolean hasHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
public Boolean hasVmActivity(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,38 +19,37 @@ package org.apache.cloudstack.kvm.ha;
|
|||
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
public class KVMHAConfig {
|
||||
public interface KVMHAConfig {
|
||||
|
||||
public static final ConfigKey<Long> KvmHAHealthCheckTimeout = new ConfigKey<>("Advanced", Long.class, "kvm.ha.health.check.timeout", "10",
|
||||
ConfigKey<Long> KvmHAHealthCheckTimeout = new ConfigKey<>("Advanced", Long.class, "kvm.ha.health.check.timeout", "10",
|
||||
"The maximum length of time, in seconds, expected for an health check to complete.", true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public static final ConfigKey<Long> KvmHAActivityCheckTimeout = new ConfigKey<>("Advanced", Long.class, "kvm.ha.activity.check.timeout", "60",
|
||||
ConfigKey<Long> KvmHAActivityCheckTimeout = new ConfigKey<>("Advanced", Long.class, "kvm.ha.activity.check.timeout", "60",
|
||||
"The maximum length of time, in seconds, expected for an activity check to complete.", true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public static final ConfigKey<Long> KvmHAActivityCheckInterval = new ConfigKey<>("Advanced", Long.class, "kvm.ha.activity.check.interval", "60",
|
||||
ConfigKey<Long> KvmHAActivityCheckInterval = new ConfigKey<>("Advanced", Long.class, "kvm.ha.activity.check.interval", "60",
|
||||
"The interval, in seconds, between activity checks.", true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public static final ConfigKey<Long> KvmHAActivityCheckMaxAttempts = new ConfigKey<>("Advanced", Long.class, "kvm.ha.activity.check.max.attempts", "10",
|
||||
ConfigKey<Long> KvmHAActivityCheckMaxAttempts = new ConfigKey<>("Advanced", Long.class, "kvm.ha.activity.check.max.attempts", "10",
|
||||
"The maximum number of activity check attempts to perform before deciding to recover or degrade a resource.", true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public static final ConfigKey<Double> KvmHAActivityCheckFailureThreshold = new ConfigKey<>("Advanced", Double.class, "kvm.ha.activity.check.failure.ratio", "0.7",
|
||||
ConfigKey<Double> KvmHAActivityCheckFailureThreshold = new ConfigKey<>("Advanced", Double.class, "kvm.ha.activity.check.failure.ratio", "0.7",
|
||||
"The activity check failure threshold ratio. This is used with the activity check maximum attempts for deciding to recover or degrade a resource. For most environments, please keep this value above 0.5.",
|
||||
true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public static final ConfigKey<Long> KvmHADegradedMaxPeriod = new ConfigKey<>("Advanced", Long.class, "kvm.ha.degraded.max.period", "300",
|
||||
ConfigKey<Long> KvmHADegradedMaxPeriod = new ConfigKey<>("Advanced", Long.class, "kvm.ha.degraded.max.period", "300",
|
||||
"The maximum length of time, in seconds, a resource can be in degraded state where only health checks are performed.", true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public static final ConfigKey<Long> KvmHARecoverTimeout = new ConfigKey<>("Advanced", Long.class, "kvm.ha.recover.timeout", "60",
|
||||
ConfigKey<Long> KvmHARecoverTimeout = new ConfigKey<>("Advanced", Long.class, "kvm.ha.recover.timeout", "60",
|
||||
"The maximum length of time, in seconds, expected for a recovery operation to complete.", true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public static final ConfigKey<Long> KvmHARecoverWaitPeriod = new ConfigKey<>("Advanced", Long.class, "kvm.ha.recover.wait.period", "600",
|
||||
ConfigKey<Long> KvmHARecoverWaitPeriod = new ConfigKey<>("Advanced", Long.class, "kvm.ha.recover.wait.period", "600",
|
||||
"The maximum length of time, in seconds, to wait for a resource to recover.", true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public static final ConfigKey<Long> KvmHARecoverAttemptThreshold = new ConfigKey<>("Advanced", Long.class, "kvm.ha.recover.failure.threshold", "1",
|
||||
ConfigKey<Long> KvmHARecoverAttemptThreshold = new ConfigKey<>("Advanced", Long.class, "kvm.ha.recover.failure.threshold", "1",
|
||||
"The maximum recovery attempts to be made for a resource, after which the resource is fenced. The recovery counter resets when a health check passes for a resource.",
|
||||
true, ConfigKey.Scope.Cluster);
|
||||
|
||||
public static final ConfigKey<Long> KvmHAFenceTimeout = new ConfigKey<>("Advanced", Long.class, "kvm.ha.fence.timeout", "60",
|
||||
ConfigKey<Long> KvmHAFenceTimeout = new ConfigKey<>("Advanced", Long.class, "kvm.ha.fence.timeout", "60",
|
||||
"The maximum length of time, in seconds, expected for a fence operation to complete.", true, ConfigKey.Scope.Cluster);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,17 +68,18 @@ public final class KVMHAProvider extends HAAbstractHostProvider implements HAPro
|
|||
|
||||
@Override
|
||||
public boolean recover(Host r) throws HARecoveryException {
|
||||
logger.debug("Recover the host {}", r);
|
||||
try {
|
||||
if (outOfBandManagementService.isOutOfBandManagementEnabled(r)){
|
||||
if (outOfBandManagementService.isOutOfBandManagementEnabled(r)) {
|
||||
final OutOfBandManagementResponse resp = outOfBandManagementService.executePowerOperation(r, PowerOperation.RESET, null);
|
||||
return resp.getSuccess();
|
||||
} else {
|
||||
logger.warn("OOBM recover operation failed for the host {}", r);
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e){
|
||||
} catch (Exception e) {
|
||||
logger.warn("OOBM service is not configured or enabled for this host {} error is {}", r, e.getMessage());
|
||||
throw new HARecoveryException(String.format(" OOBM service is not configured or enabled for this host %s", r), e);
|
||||
throw new HARecoveryException(String.format("OOBM service is not configured or enabled for this host %s", r), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.apache.cloudstack.kvm.ha;
|
|||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.CheckOnHostAnswer;
|
||||
import com.cloud.agent.api.CheckOnHostCommand;
|
||||
import com.cloud.agent.api.CheckVMActivityOnStoragePoolCommand;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
|
|
@ -61,7 +62,7 @@ public class KVMHostActivityChecker extends AdapterBase implements ActivityCheck
|
|||
@Inject
|
||||
private AgentManager agentMgr;
|
||||
@Inject
|
||||
private PrimaryDataStoreDao storagePool;
|
||||
private PrimaryDataStoreDao storagePoolDao;
|
||||
@Inject
|
||||
private StorageManager storageManager;
|
||||
@Inject
|
||||
|
|
@ -70,11 +71,11 @@ public class KVMHostActivityChecker extends AdapterBase implements ActivityCheck
|
|||
@Override
|
||||
public boolean isActive(Host r, DateTime suspectTime) throws HACheckerException {
|
||||
try {
|
||||
return isVMActivityOnHost(r, suspectTime);
|
||||
return hasVMActivityOnHost(r, suspectTime);
|
||||
} catch (HACheckerException e) {
|
||||
//Re-throwing the exception to avoid poluting the 'HACheckerException' already thrown
|
||||
//Re-throwing the exception to avoid polluting the 'HACheckerException' already thrown
|
||||
throw e;
|
||||
} catch (Exception e){
|
||||
} catch (Exception e) {
|
||||
String message = String.format("Operation timed out, probably the %s is not reachable.", r.toString());
|
||||
logger.warn(message, e);
|
||||
throw new HACheckerException(message, e);
|
||||
|
|
@ -83,82 +84,115 @@ public class KVMHostActivityChecker extends AdapterBase implements ActivityCheck
|
|||
|
||||
@Override
|
||||
public boolean isHealthy(Host r) {
|
||||
return isAgentActive(r);
|
||||
return isHostAgentUp(r);
|
||||
}
|
||||
|
||||
private boolean isAgentActive(Host agent) {
|
||||
if (agent.getHypervisorType() != Hypervisor.HypervisorType.KVM && agent.getHypervisorType() != Hypervisor.HypervisorType.LXC) {
|
||||
throw new IllegalStateException(String.format("Calling KVM investigator for non KVM Host of type [%s].", agent.getHypervisorType()));
|
||||
private boolean isHostAgentUp(Host host) {
|
||||
if (host.getHypervisorType() != Hypervisor.HypervisorType.KVM && host.getHypervisorType() != Hypervisor.HypervisorType.LXC) {
|
||||
throw new IllegalStateException(String.format("Calling KVM investigator for non KVM Host of type [%s].", host.getHypervisorType()));
|
||||
}
|
||||
Status hostStatus = Status.Unknown;
|
||||
Status neighbourStatus = Status.Unknown;
|
||||
final CheckOnHostCommand cmd = new CheckOnHostCommand(agent, HighAvailabilityManager.KvmHAFenceHostIfHeartbeatFailsOnStorage.value());
|
||||
try {
|
||||
logger.debug(String.format("Checking %s status...", agent.toString()));
|
||||
Answer answer = agentMgr.easySend(agent.getId(), cmd);
|
||||
if (answer != null) {
|
||||
hostStatus = answer.getResult() ? Status.Down : Status.Up;
|
||||
logger.debug(String.format("%s has the status [%s].", agent.toString(), hostStatus));
|
||||
|
||||
if ( hostStatus == Status.Up ){
|
||||
return true;
|
||||
Status hostStatus = getHostAgentStatus(host);
|
||||
|
||||
logger.debug("{} has the status [{}].", host.toString(), hostStatus);
|
||||
return hostStatus == Status.Up;
|
||||
}
|
||||
|
||||
public Status getHostAgentStatus(Host host) {
|
||||
if (host.getHypervisorType() != Hypervisor.HypervisorType.KVM && host.getHypervisorType() != Hypervisor.HypervisorType.LXC) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Status hostStatusFromItself = checkHostStatusWithSameHost(host);
|
||||
if (hostStatusFromItself == Status.Up) {
|
||||
return Status.Up;
|
||||
}
|
||||
|
||||
Status hostStatusFromNeighbour = checkHostStatusWithNeighbourHosts(host);
|
||||
Status hostStatus = hostStatusFromItself;
|
||||
if (hostStatusFromNeighbour == Status.Up && (hostStatusFromItself == Status.Disconnected || hostStatusFromItself == Status.Down)) {
|
||||
hostStatus = Status.Disconnected;
|
||||
}
|
||||
if (hostStatusFromNeighbour == Status.Down && (hostStatusFromItself == Status.Disconnected || hostStatusFromItself == Status.Down)) {
|
||||
hostStatus = Status.Down;
|
||||
}
|
||||
|
||||
logger.debug("HA: HOST is ineligible legacy state {} for host {}", hostStatus, host);
|
||||
return hostStatus;
|
||||
}
|
||||
|
||||
private Status checkHostStatusWithSameHost(Host host) {
|
||||
Status hostStatus;
|
||||
boolean reportFailureIfOneStorageIsDown = HighAvailabilityManager.KvmHAFenceHostIfHeartbeatFailsOnStorage.value();
|
||||
final CheckOnHostCommand cmd = new CheckOnHostCommand(host, reportFailureIfOneStorageIsDown);
|
||||
try {
|
||||
logger.debug("Checking {} status...", host.toString());
|
||||
Answer answer = agentMgr.easySend(host.getId(), cmd);
|
||||
if (answer != null) {
|
||||
if (answer.getResult()) {
|
||||
hostStatus = ((CheckOnHostAnswer)answer).isAlive() ? Status.Up : Status.Down;
|
||||
} else {
|
||||
logger.debug("{} is not active according to itself, details: {}.", host.toString(), answer.getDetails());
|
||||
hostStatus = Status.Down;
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.debug(String.format("Setting %s to \"Disconnected\" status.", agent.toString()));
|
||||
logger.debug("{} has the status [{}].", host.toString(), hostStatus);
|
||||
} else {
|
||||
logger.debug("Setting {} to \"Disconnected\" status.", host.toString());
|
||||
hostStatus = Status.Disconnected;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn(String.format("Failed to send command CheckOnHostCommand to %s.", agent.toString()), e);
|
||||
logger.warn("Failed to send command CheckOnHostCommand to {}.", host.toString(), e);
|
||||
hostStatus = Status.Disconnected;
|
||||
}
|
||||
|
||||
List<HostVO> neighbors = resourceManager.listHostsInClusterByStatus(agent.getClusterId(), Status.Up);
|
||||
return hostStatus;
|
||||
}
|
||||
|
||||
private Status checkHostStatusWithNeighbourHosts(Host host) {
|
||||
Status hostStatusFromNeighbour = Status.Unknown;
|
||||
boolean reportFailureIfOneStorageIsDown = HighAvailabilityManager.KvmHAFenceHostIfHeartbeatFailsOnStorage.value();
|
||||
final CheckOnHostCommand cmd = new CheckOnHostCommand(host, reportFailureIfOneStorageIsDown);
|
||||
List<HostVO> neighbors = resourceManager.listHostsInClusterByStatus(host.getClusterId(), Status.Up);
|
||||
for (HostVO neighbor : neighbors) {
|
||||
if (neighbor.getId() == agent.getId() || (neighbor.getHypervisorType() != Hypervisor.HypervisorType.KVM && neighbor.getHypervisorType() != Hypervisor.HypervisorType.LXC)) {
|
||||
if (neighbor.getId() == host.getId()
|
||||
|| (neighbor.getHypervisorType() != Hypervisor.HypervisorType.KVM && neighbor.getHypervisorType() != Hypervisor.HypervisorType.LXC)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug(String.format("Investigating %s via neighbouring %s.", agent.toString(), neighbor.toString()));
|
||||
|
||||
logger.debug("Investigating {} via neighboring {}.", host.toString(), neighbor.toString());
|
||||
Answer answer = agentMgr.easySend(neighbor.getId(), cmd);
|
||||
if (answer != null) {
|
||||
neighbourStatus = answer.getResult() ? Status.Down : Status.Up;
|
||||
|
||||
logger.debug(String.format("Neighbouring %s returned status [%s] for the investigated %s.", neighbor.toString(), neighbourStatus, agent.toString()));
|
||||
|
||||
if (neighbourStatus == Status.Up) {
|
||||
break;
|
||||
if (answer.getResult()) {
|
||||
hostStatusFromNeighbour = ((CheckOnHostAnswer)answer).isAlive() ? Status.Up : Status.Down;
|
||||
logger.debug("Neighboring {} returned status [{}] for the investigated {}.", neighbor.toString(), hostStatusFromNeighbour, host.toString());
|
||||
if (hostStatusFromNeighbour == Status.Up) {
|
||||
return hostStatusFromNeighbour;
|
||||
}
|
||||
} else {
|
||||
logger.debug("{} is not active according to neighbor {}, details: {}.", host.toString(), neighbor.toString(), answer.getDetails());
|
||||
}
|
||||
} else {
|
||||
logger.debug(String.format("Neighbouring %s is Disconnected.", neighbor.toString()));
|
||||
logger.debug("Neighboring {} is Disconnected.", neighbor.toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn(String.format("Failed to send command CheckOnHostCommand to %s.", neighbor.toString()), e);
|
||||
logger.warn("Failed to send command CheckOnHostCommand to neighbor {}.", neighbor.toString(), e);
|
||||
}
|
||||
}
|
||||
if (neighbourStatus == Status.Up && (hostStatus == Status.Disconnected || hostStatus == Status.Down)) {
|
||||
hostStatus = Status.Disconnected;
|
||||
}
|
||||
if (neighbourStatus == Status.Down && (hostStatus == Status.Disconnected || hostStatus == Status.Down)) {
|
||||
hostStatus = Status.Down;
|
||||
}
|
||||
|
||||
logger.debug(String.format("%s has the status [%s].", agent.toString(), hostStatus));
|
||||
|
||||
return hostStatus == Status.Up;
|
||||
return hostStatusFromNeighbour;
|
||||
}
|
||||
|
||||
private boolean isVMActivityOnHost(Host agent, DateTime suspectTime) throws HACheckerException {
|
||||
if (agent.getHypervisorType() != Hypervisor.HypervisorType.KVM && agent.getHypervisorType() != Hypervisor.HypervisorType.LXC) {
|
||||
throw new IllegalStateException(String.format("Calling KVM investigator for non KVM Host of type [%s].", agent.getHypervisorType()));
|
||||
private boolean hasVMActivityOnHost(Host host, DateTime suspectTime) throws HACheckerException {
|
||||
if (host.getHypervisorType() != Hypervisor.HypervisorType.KVM && host.getHypervisorType() != Hypervisor.HypervisorType.LXC) {
|
||||
throw new IllegalStateException(String.format("Calling KVM investigator for non KVM Host of type [%s].", host.getHypervisorType()));
|
||||
}
|
||||
boolean activityStatus = true;
|
||||
HashMap<StoragePool, List<Volume>> poolVolMap = getVolumeUuidOnHost(agent);
|
||||
for (StoragePool pool : poolVolMap.keySet()) {
|
||||
activityStatus = verifyActivityOfStorageOnHost(poolVolMap, pool, agent, suspectTime, activityStatus);
|
||||
HashMap<StoragePool, List<Volume>> poolVolumeMap = getStoragePoolAndVolumeInfoOnHost(host);
|
||||
for (StoragePool pool : poolVolumeMap.keySet()) {
|
||||
activityStatus = verifyActivityOfStorageOnHost(poolVolumeMap, pool, host, suspectTime, activityStatus);
|
||||
if (!activityStatus) {
|
||||
logger.warn("It seems that the storage pool [{}] does not have activity on {}.", pool, agent);
|
||||
logger.warn("It seems that the storage pool [{}] does not have activity on {}.", pool, host);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -166,66 +200,64 @@ public class KVMHostActivityChecker extends AdapterBase implements ActivityCheck
|
|||
return activityStatus;
|
||||
}
|
||||
|
||||
protected boolean verifyActivityOfStorageOnHost(HashMap<StoragePool, List<Volume>> poolVolMap, StoragePool pool, Host agent, DateTime suspectTime, boolean activityStatus) throws HACheckerException, IllegalStateException {
|
||||
protected boolean verifyActivityOfStorageOnHost(HashMap<StoragePool, List<Volume>> poolVolMap, StoragePool pool, Host host, DateTime suspectTime, boolean activityStatus) throws HACheckerException, IllegalStateException {
|
||||
List<Volume> volume_list = poolVolMap.get(pool);
|
||||
final CheckVMActivityOnStoragePoolCommand cmd = new CheckVMActivityOnStoragePoolCommand(agent, pool, volume_list, suspectTime);
|
||||
final CheckVMActivityOnStoragePoolCommand cmd = new CheckVMActivityOnStoragePoolCommand(host, pool, volume_list, suspectTime);
|
||||
|
||||
logger.debug("Checking VM activity for {} on storage pool [{}].", agent.toString(), pool);
|
||||
logger.debug("Checking VM activity for {} on storage pool [{}].", host.toString(), pool);
|
||||
try {
|
||||
Answer answer = storageManager.sendToPool(pool, getNeighbors(agent), cmd);
|
||||
|
||||
Answer answer = storageManager.sendToPool(pool, getNeighbors(host), cmd);
|
||||
if (answer != null) {
|
||||
activityStatus = !answer.getResult();
|
||||
logger.debug("{} {} activity on storage pool [{}]", agent.toString(), activityStatus ? "has" : "does not have", pool);
|
||||
logger.debug("{} {} activity on storage pool [{}]", host.toString(), activityStatus ? "has" : "does not have", pool);
|
||||
} else {
|
||||
String message = String.format("Did not get a valid response for VM activity check for %s on storage pool [%s].", agent.toString(), pool);
|
||||
String message = String.format("Did not get a valid response for VM activity check for %s on storage pool [%s].", host.toString(), pool);
|
||||
logger.debug(message);
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
} catch (StorageUnavailableException e){
|
||||
String message = String.format("Storage [%s] is unavailable to do the check, probably the %s is not reachable.", pool, agent);
|
||||
} catch (StorageUnavailableException e) {
|
||||
String message = String.format("Storage [%s] is unavailable to do the check, probably the %s is not reachable.", pool, host);
|
||||
logger.warn(message, e);
|
||||
throw new HACheckerException(message, e);
|
||||
}
|
||||
return activityStatus;
|
||||
}
|
||||
|
||||
private HashMap<StoragePool, List<Volume>> getVolumeUuidOnHost(Host agent) {
|
||||
List<VMInstanceVO> vm_list = vmInstanceDao.listByHostId(agent.getId());
|
||||
List<VolumeVO> volume_list = new ArrayList<VolumeVO>();
|
||||
for (VirtualMachine vm : vm_list) {
|
||||
private HashMap<StoragePool, List<Volume>> getStoragePoolAndVolumeInfoOnHost(Host host) {
|
||||
List<VMInstanceVO> vmListOnHost = vmInstanceDao.listByHostId(host.getId());
|
||||
List<VolumeVO> volumeListOnHost = new ArrayList<>();
|
||||
for (VirtualMachine vm : vmListOnHost) {
|
||||
logger.debug("Retrieving volumes of VM [{}]...", vm);
|
||||
List<VolumeVO> vm_volume_list = volumeDao.findByInstance(vm.getId());
|
||||
volume_list.addAll(vm_volume_list);
|
||||
List<VolumeVO> volumeListOfVM = volumeDao.findByInstance(vm.getId());
|
||||
volumeListOnHost.addAll(volumeListOfVM);
|
||||
}
|
||||
|
||||
HashMap<StoragePool, List<Volume>> poolVolMap = new HashMap<StoragePool, List<Volume>>();
|
||||
for (Volume vol : volume_list) {
|
||||
StoragePool sp = storagePool.findById(vol.getPoolId());
|
||||
logger.debug("Retrieving storage pool [{}] of volume [{}]...", sp, vol);
|
||||
if (!poolVolMap.containsKey(sp)) {
|
||||
List<Volume> list = new ArrayList<Volume>();
|
||||
list.add(vol);
|
||||
HashMap<StoragePool, List<Volume>> poolVolumeMap = new HashMap<>();
|
||||
for (Volume volume : volumeListOnHost) {
|
||||
StoragePool pool = storagePoolDao.findById(volume.getPoolId());
|
||||
logger.debug("Retrieving storage pool [{}] of volume [{}]...", pool, volume);
|
||||
if (!poolVolumeMap.containsKey(pool)) {
|
||||
List<Volume> volList = new ArrayList<>();
|
||||
volList.add(volume);
|
||||
|
||||
poolVolMap.put(sp, list);
|
||||
poolVolumeMap.put(pool, volList);
|
||||
} else {
|
||||
poolVolMap.get(sp).add(vol);
|
||||
poolVolumeMap.get(pool).add(volume);
|
||||
}
|
||||
}
|
||||
return poolVolMap;
|
||||
return poolVolumeMap;
|
||||
}
|
||||
|
||||
public long[] getNeighbors(Host agent) {
|
||||
List<Long> neighbors = new ArrayList<Long>();
|
||||
List<HostVO> cluster_hosts = resourceManager.listHostsInClusterByStatus(agent.getClusterId(), Status.Up);
|
||||
logger.debug("Retrieving all \"Up\" hosts from cluster [{}]...", clusterDao.findById(agent.getClusterId()));
|
||||
for (HostVO host : cluster_hosts) {
|
||||
if (host.getId() == agent.getId() || (host.getHypervisorType() != Hypervisor.HypervisorType.KVM && host.getHypervisorType() != Hypervisor.HypervisorType.LXC)) {
|
||||
public long[] getNeighbors(Host host) {
|
||||
List<Long> neighbors = new ArrayList<>();
|
||||
List<HostVO> clusterHosts = resourceManager.listHostsInClusterByStatus(host.getClusterId(), Status.Up);
|
||||
logger.debug("Retrieving all \"Up\" hosts from cluster [{}]...", clusterDao.findById(host.getClusterId()));
|
||||
for (HostVO clusterHost : clusterHosts) {
|
||||
if (clusterHost.getId() == host.getId() || (clusterHost.getHypervisorType() != Hypervisor.HypervisorType.KVM && clusterHost.getHypervisorType() != Hypervisor.HypervisorType.LXC)) {
|
||||
continue;
|
||||
}
|
||||
neighbors.add(host.getId());
|
||||
neighbors.add(clusterHost.getId());
|
||||
}
|
||||
return ArrayUtils.toPrimitive(neighbors.toArray(new Long[neighbors.size()]));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3133,7 +3133,7 @@ public class LibvirtComputingResourceTest {
|
|||
assertNotNull(wrapper);
|
||||
|
||||
final Answer answer = wrapper.execute(command, libvirtComputingResourceMock);
|
||||
assertTrue(answer.getResult());
|
||||
assertFalse(answer.getResult());
|
||||
|
||||
verify(libvirtComputingResourceMock, times(1)).getMonitor();
|
||||
}
|
||||
|
|
@ -5642,35 +5642,45 @@ public class LibvirtComputingResourceTest {
|
|||
Mockito.verify(vmDef, times(1)).addComp(any());
|
||||
}
|
||||
|
||||
public void validateGetCurrentMemAccordingToMemBallooningWithoutMemBalooning(){
|
||||
@Test
|
||||
public void getCurrentMemAccordingToMemBallooningTestValidateCurrentMemoryWithoutMemBallooning(){
|
||||
VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
|
||||
Mockito.when(vmTo.getType()).thenReturn(Type.User);
|
||||
LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource();
|
||||
libvirtComputingResource.noMemBalloon = true;
|
||||
long maxMemory = 2048;
|
||||
long requestedMemory = 1024 * 1024;
|
||||
long minMemory = 512 * 1024;
|
||||
|
||||
long currentMemory = libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, maxMemory);
|
||||
Assert.assertEquals(maxMemory, currentMemory);
|
||||
Mockito.verify(vmTo, Mockito.times(0)).getMinRam();
|
||||
long currentMemory = libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, requestedMemory, minMemory);
|
||||
Assert.assertEquals(requestedMemory, currentMemory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateGetCurrentMemAccordingToMemBallooningWithtMemBalooning(){
|
||||
public void getCurrentMemAccordingToMemBallooningTestValidateCurrentMemoryWithMemoryBallooning(){
|
||||
LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource();
|
||||
libvirtComputingResource.noMemBalloon = false;
|
||||
|
||||
long maxMemory = 2048;
|
||||
long minMemory = ByteScaleUtils.mebibytesToBytes(64);
|
||||
|
||||
VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
|
||||
Mockito.when(vmTo.getType()).thenReturn(Type.User);
|
||||
Mockito.when(vmTo.getMinRam()).thenReturn(minMemory);
|
||||
long requestedMemory = 1024 * 1024;
|
||||
long minMemory = 512 * 1024;
|
||||
|
||||
long currentMemory = libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, maxMemory);
|
||||
Assert.assertEquals(ByteScaleUtils.bytesToKibibytes(minMemory), currentMemory);
|
||||
Mockito.verify(vmTo).getMinRam();
|
||||
long currentMemory = libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, requestedMemory, minMemory);
|
||||
Assert.assertEquals(minMemory, currentMemory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentMemAccordingToMemBallooningTestValidateCurrentMemoryForSystemVms() {
|
||||
LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource();
|
||||
libvirtComputingResource.noMemBalloon = false;
|
||||
|
||||
VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
|
||||
Mockito.when(vmTo.getType()).thenReturn(Type.SecondaryStorageVm);
|
||||
long requestedMemory = 1024 * 1024;
|
||||
long minMemory = 512 * 1024;
|
||||
|
||||
long currentMemory = libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, requestedMemory, minMemory);
|
||||
Assert.assertEquals(requestedMemory, currentMemory);
|
||||
}
|
||||
@Test
|
||||
public void validateCreateGuestResourceDefWithVcpuMaxLimit(){
|
||||
LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource();
|
||||
|
|
@ -7234,4 +7244,82 @@ public class LibvirtComputingResourceTest {
|
|||
|
||||
libvirtComputingResourceSpy.getInterface(connMock, vmName, invalidMacAddress);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCpuQuotaAndPeriodTestAssertPeriodAndQuotaAreNotUpdatedWhenLibvirtVersionIsLessThanTheMinimum() throws LibvirtException {
|
||||
libvirtComputingResourceSpy.hypervisorLibvirtVersion = 8999;
|
||||
libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, null, false);
|
||||
Mockito.verify(domainMock, Mockito.never()).setSchedulerParameters(Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCpuQuotaAndPeriodTestAssertPeriodAndQuotaAreNotUpdatedWhenThereIsNoCapCapChangeAndNoCpuLimitationIsApplied() throws LibvirtException {
|
||||
Mockito.when(vmTO.isLimitCpuUse()).thenReturn(false);
|
||||
libvirtComputingResourceSpy.hypervisorLibvirtVersion = 9000;
|
||||
libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, vmTO, false);
|
||||
Mockito.verify(domainMock, Mockito.never()).setSchedulerParameters(Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCpuQuotaAndPeriodTestAssertQuotaIsRemovedWhenThereIsCpuCapChangeAndNoCpuLimitationIsApplied() throws LibvirtException {
|
||||
Mockito.when(vmTO.isLimitCpuUse()).thenReturn(false);
|
||||
Mockito.when(domainMock.getName()).thenReturn("i-2-10-VM");
|
||||
libvirtComputingResourceSpy.hypervisorLibvirtVersion = 9000;
|
||||
libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, vmTO, true);
|
||||
Mockito.verify(domainMock, Mockito.times(1)).setSchedulerParameters(Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCpuQuotaAndPeriodTestAssertPeriodAndQuotaAreUpdatedWhenThereIsNotCpuCapChangeAndCpuLimitationIsApplied() throws LibvirtException {
|
||||
Mockito.when(vmTO.isLimitCpuUse()).thenReturn(true);
|
||||
double cpuQuotaPercentage = 0.03;
|
||||
Mockito.when(vmTO.getCpuQuotaPercentage()).thenReturn(cpuQuotaPercentage);
|
||||
Mockito.doReturn(new Pair<>(1000, 300L)).when(libvirtComputingResourceSpy).getPeriodAndQuota(cpuQuotaPercentage);
|
||||
Mockito.when(domainMock.getName()).thenReturn("i-2-10-VM");
|
||||
libvirtComputingResourceSpy.hypervisorLibvirtVersion = 9000;
|
||||
libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, vmTO, false);
|
||||
Mockito.verify(domainMock, Mockito.times(2)).setSchedulerParameters(Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCpuQuotaAndPeriodTestAssertPeriodAndQuotaAreUpdatedWhenThereIsCpuCapChangeAndCpuLimitationIsApplied() throws LibvirtException {
|
||||
Mockito.when(vmTO.isLimitCpuUse()).thenReturn(true);
|
||||
double cpuQuotaPercentage = 0.03;
|
||||
Mockito.when(vmTO.getCpuQuotaPercentage()).thenReturn(cpuQuotaPercentage);
|
||||
Mockito.doReturn(new Pair<>(1000, 300L)).when(libvirtComputingResourceSpy).getPeriodAndQuota(cpuQuotaPercentage);
|
||||
Mockito.when(domainMock.getName()).thenReturn("i-2-10-VM");
|
||||
libvirtComputingResourceSpy.hypervisorLibvirtVersion = 9000;
|
||||
libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, vmTO, true);
|
||||
Mockito.verify(domainMock, Mockito.times(2)).setSchedulerParameters(Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPeriodAndQuotaTestAssertQuotaIsEqualToPeriodMultipliedByQuotaPercentage() {
|
||||
double cpuQuotaPercentage = 0.3;
|
||||
int expectedPeriod = CpuTuneDef.DEFAULT_PERIOD;
|
||||
long expectedQuota = (long) (expectedPeriod * cpuQuotaPercentage);
|
||||
Pair<Integer, Long> expectedResult = new Pair<>(expectedPeriod, expectedQuota);
|
||||
Pair<Integer, Long> result = libvirtComputingResourceSpy.getPeriodAndQuota(cpuQuotaPercentage);
|
||||
Assert.assertEquals(expectedResult, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPeriodAndQuotaTestQuotaIsEqualToMinimumWhenRequired() {
|
||||
double cpuQuotaPercentage = 0.03;
|
||||
long expectedQuota = CpuTuneDef.MIN_QUOTA;
|
||||
int expectedPeriod = (int) ((double) expectedQuota / cpuQuotaPercentage);
|
||||
Pair<Integer, Long> expectedResult = new Pair<>(expectedPeriod, expectedQuota);
|
||||
Pair<Integer, Long> result = libvirtComputingResourceSpy.getPeriodAndQuota(cpuQuotaPercentage);
|
||||
Assert.assertEquals(expectedResult, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPeriodAndQuotaTestPeriodIsEqualToMaximumWhenRequired() {
|
||||
double cpuQuotaPercentage = 0.0003;
|
||||
long expectedQuota = CpuTuneDef.MIN_QUOTA;
|
||||
int expectedPeriod = CpuTuneDef.MAX_PERIOD;
|
||||
Pair<Integer, Long> expectedResult = new Pair<>(expectedPeriod, expectedQuota);
|
||||
Pair<Integer, Long> result = libvirtComputingResourceSpy.getPeriodAndQuota(cpuQuotaPercentage);
|
||||
Assert.assertEquals(expectedResult, result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,13 +54,13 @@ public class SimulatorInvestigator extends AdapterBase implements Investigator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Status isAgentAlive(Host agent) {
|
||||
public Status getHostAgentStatus(Host agent) {
|
||||
if (agent.getHypervisorType() != HypervisorType.Simulator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (haManager.isHAEligible(agent)) {
|
||||
return haManager.getHostStatus(agent);
|
||||
return haManager.getHostStatusFromHAConfig(agent);
|
||||
}
|
||||
|
||||
CheckOnHostCommand cmd = new CheckOnHostCommand(agent);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class VmwareInvestigator extends AdapterBase implements Investigator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Status isAgentAlive(Host agent) {
|
||||
public Status getHostAgentStatus(Host agent) {
|
||||
if (agent.getHypervisorType() == HypervisorType.VMware)
|
||||
return Status.Disconnected;
|
||||
|
||||
|
|
|
|||
|
|
@ -124,16 +124,6 @@
|
|||
<version>${cs.hamcrest.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>${cs.bcprov.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bctls-jdk15on</artifactId>
|
||||
<version>${cs.bcprov.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
|
|
|
|||
|
|
@ -203,13 +203,13 @@
|
|||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.5.2</version>
|
||||
<version>${cs.minio.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio-admin</artifactId>
|
||||
<version>8.5.2</version>
|
||||
<version>${cs.minio.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -46,12 +46,24 @@
|
|||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.5.2</version>
|
||||
<version>${cs.minio.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio-admin</artifactId>
|
||||
<version>8.5.2</version>
|
||||
<version>${cs.minio.version}</version>
|
||||
</dependency>
|
||||
<!-- Pin okhttp3 to the version required by minio 8.6.0+, overriding the older
|
||||
version transitively pulled by influxdb-java -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>5.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
<version>5.1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -46,12 +46,12 @@
|
|||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.5.2</version>
|
||||
<version>${cs.minio.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio-admin</artifactId>
|
||||
<version>8.5.2</version>
|
||||
<version>${cs.minio.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -217,13 +217,14 @@ public class AdaptiveDataStoreLifeCycleImpl extends BasePrimaryDataStoreLifeCycl
|
|||
// validate the provided details are correct/valid for the provider
|
||||
api.validate();
|
||||
|
||||
// if we have user-provided capacity bytes, validate they do not exceed the manaaged storage capacity bytes
|
||||
// User-provided capacityBytes always wins; validate against storage stats only when
|
||||
// the provider could actually report them. If the provider cannot (empty pod with no
|
||||
// footprint, no quota set, transient probe failure), fall through and use what the
|
||||
// user supplied rather than failing the whole registration.
|
||||
ProviderVolumeStorageStats stats = api.getManagedStorageStats();
|
||||
if (capacityBytes != null && capacityBytes != 0 && stats != null) {
|
||||
if (stats.getCapacityInBytes() > 0) {
|
||||
if (stats.getCapacityInBytes() < capacityBytes) {
|
||||
throw new InvalidParameterValueException("Capacity bytes provided exceeds the capacity of the storage endpoint: provided by user: " + capacityBytes + ", storage capacity from storage provider: " + stats.getCapacityInBytes());
|
||||
}
|
||||
if (capacityBytes != null && capacityBytes > 0) {
|
||||
if (stats != null && stats.getCapacityInBytes() > 0 && stats.getCapacityInBytes() < capacityBytes) {
|
||||
throw new InvalidParameterValueException("Provided capacity bytes exceed the capacity of the storage endpoint: provided by user: " + capacityBytes + ", storage capacity from storage provider: " + stats.getCapacityInBytes());
|
||||
}
|
||||
parameters.setCapacityBytes(capacityBytes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ import java.net.URL;
|
|||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
|
@ -88,6 +89,9 @@ public class FlashArrayAdapter implements ProviderAdapter {
|
|||
static final ObjectMapper mapper = new ObjectMapper();
|
||||
public String pod = null;
|
||||
public String hostgroup = null;
|
||||
private static final DateTimeFormatter DELETION_TIMESTAMP_FORMAT =
|
||||
DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(ZoneOffset.UTC);
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
private String accessToken;
|
||||
|
|
@ -200,28 +204,63 @@ public class FlashArrayAdapter implements ProviderAdapter {
|
|||
|
||||
@Override
|
||||
public void delete(ProviderAdapterContext context, ProviderAdapterDataObject dataObject) {
|
||||
// first make sure we are disconnected
|
||||
removeVlunsAll(context, pod, dataObject.getExternalName());
|
||||
String fullName = normalizeName(pod, dataObject.getExternalName());
|
||||
|
||||
FlashArrayVolume volume = new FlashArrayVolume();
|
||||
// Snapshots live under /volume-snapshots and already use the array's
|
||||
// reserved form <volume>.<suffix>, which legitimately contains ".".
|
||||
// The stricter [A-Za-z0-9_-] naming rule applies to regular volume
|
||||
// names and free-form rename targets, not to these reserved snapshot
|
||||
// names. Since FlashArray snapshot names are system-defined rather
|
||||
// than arbitrary rename targets, we skip the usual timestamped rename
|
||||
// and only mark snapshots destroyed; the array's own ".N" suffix
|
||||
// already disambiguates them in the recycle bin.
|
||||
if (dataObject.getType() == ProviderAdapterDataObject.Type.SNAPSHOT) {
|
||||
try {
|
||||
FlashArrayVolume destroy = new FlashArrayVolume();
|
||||
destroy.setDestroyed(true);
|
||||
PATCH("/volume-snapshots?names=" + fullName, destroy, new TypeReference<FlashArrayList<FlashArrayVolume>>() {
|
||||
});
|
||||
} catch (CloudRuntimeException e) {
|
||||
String msg = e.getMessage();
|
||||
if (msg != null && (msg.contains("No such volume or snapshot")
|
||||
|| msg.contains("Volume does not exist"))) {
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// rename as we delete so it doesn't conflict if the template or volume is ever recreated
|
||||
// pure keeps the volume(s) around in a Destroyed bucket for a period of time post delete
|
||||
String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new java.util.Date());
|
||||
volume.setExternalName(fullName + "-" + timestamp);
|
||||
// first make sure we are disconnected
|
||||
removeVlunsAll(context, pod, dataObject.getExternalName());
|
||||
|
||||
// Rename then destroy: FlashArray keeps destroyed volumes in a recycle
|
||||
// bin (default 24h) from which they can be recovered. Renaming with a
|
||||
// deletion timestamp gives operators a forensic trail when browsing the
|
||||
// array - they can see when each destroyed copy was deleted on the
|
||||
// CloudStack side. FlashArray rejects a single PATCH that combines
|
||||
// {name, destroyed}, so the rename and the destroy must be issued as
|
||||
// two separate requests each carrying only its own field.
|
||||
// Use UTC so the rename suffix is stable regardless of the management
|
||||
// server's local timezone or DST changes - operators correlating the
|
||||
// CloudStack delete event with the array's audit log get a consistent
|
||||
// wall-clock value.
|
||||
String timestamp = DELETION_TIMESTAMP_FORMAT.format(java.time.Instant.now());
|
||||
String renamedName = fullName + "-" + timestamp;
|
||||
|
||||
try {
|
||||
PATCH("/volumes?names=" + fullName, volume, new TypeReference<FlashArrayList<FlashArrayVolume>>() {
|
||||
FlashArrayVolume rename = new FlashArrayVolume();
|
||||
rename.setExternalName(renamedName);
|
||||
PATCH("/volumes?names=" + fullName, rename, new TypeReference<FlashArrayList<FlashArrayVolume>>() {
|
||||
});
|
||||
|
||||
// now delete it with new name
|
||||
volume.setDestroyed(true);
|
||||
|
||||
PATCH("/volumes?names=" + fullName + "-" + timestamp, volume, new TypeReference<FlashArrayList<FlashArrayVolume>>() {
|
||||
FlashArrayVolume destroy = new FlashArrayVolume();
|
||||
destroy.setDestroyed(true);
|
||||
PATCH("/volumes?names=" + renamedName, destroy, new TypeReference<FlashArrayList<FlashArrayVolume>>() {
|
||||
});
|
||||
} catch (CloudRuntimeException e) {
|
||||
if (e.toString().contains("Volume does not exist")) {
|
||||
String msg = e.getMessage();
|
||||
if (msg != null && msg.contains("Volume does not exist")) {
|
||||
return;
|
||||
} else {
|
||||
throw e;
|
||||
|
|
|
|||
|
|
@ -514,6 +514,17 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
|
|||
ApiCallRcList answers = api.resourceDefinitionDelete(rd.getName());
|
||||
checkLinstorAnswersThrow(answers);
|
||||
deleted = true;
|
||||
|
||||
// LINSTOR can return success here while the resource lingers in DELETING state
|
||||
// on the controller (down peer, lost quorum, etc.). Confirm it's actually gone
|
||||
// — if not, log a WARN so operators can clear it manually. Don't throw: the
|
||||
// CloudStack-side accounting has already moved on.
|
||||
if (!LinstorUtil.waitForResourceDefinitionDeleted(api, rd.getName(),
|
||||
LinstorUtil.DEFAULT_RD_DELETE_VERIFY_TIMEOUT_MILLIS)) {
|
||||
logger.warn("Linstor: resource {} still present {}ms after delete returned success — " +
|
||||
"may be stuck in DELETING. Check the LINSTOR controller (linstor resource list).",
|
||||
rd.getName(), LinstorUtil.DEFAULT_RD_DELETE_VERIFY_TIMEOUT_MILLIS);
|
||||
}
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
|
|
@ -585,8 +596,8 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
|
|||
Path propFile = diskPath.getParent().resolve("template.properties");
|
||||
if (Files.exists(propFile)) {
|
||||
java.util.Properties templateProps = new java.util.Properties();
|
||||
try {
|
||||
templateProps.load(new FileInputStream(propFile.toFile()));
|
||||
try (FileInputStream in = new FileInputStream(propFile.toFile())) {
|
||||
templateProps.load(in);
|
||||
String desc = templateProps.getProperty("description");
|
||||
if (desc != null && desc.startsWith("SystemVM Template")) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -228,11 +228,11 @@ public class LinstorStoragePool implements KVMStoragePool {
|
|||
public String createHeartBeatCommand(HAStoragePool pool, String hostPrivateIp,
|
||||
boolean hostValidation) {
|
||||
LOGGER.trace(String.format("Linstor.createHeartBeatCommand: %s, %s, %b", pool.getPoolIp(), hostPrivateIp, hostValidation));
|
||||
boolean isStorageNodeUp = checkingHeartBeat(pool, null);
|
||||
boolean isStorageNodeUp = hasHeartBeat(pool, null);
|
||||
if (!isStorageNodeUp && !hostValidation) {
|
||||
//restart the host
|
||||
LOGGER.debug(String.format("The host [%s] will be restarted because the health check failed for the storage pool [%s]", hostPrivateIp, pool.getPool().getType()));
|
||||
Script cmd = new Script(pool.getPool().getHearthBeatPath(), Duration.millis(HeartBeatUpdateTimeout), LOGGER);
|
||||
Script cmd = new Script(pool.getPool().getHearthBeatPath(), Duration.millis(HeartBeatUpdateTimeoutInMs), LOGGER);
|
||||
cmd.add("-c");
|
||||
cmd.execute();
|
||||
return "Down";
|
||||
|
|
@ -258,7 +258,7 @@ public class LinstorStoragePool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkingHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
public Boolean hasHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
String hostName;
|
||||
if (host == null) {
|
||||
hostName = localNodeName;
|
||||
|
|
@ -274,7 +274,7 @@ public class LinstorStoragePool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
private String executeDrbdSetupStatus(OutputInterpreter.AllLinesParser parser) {
|
||||
Script sc = new Script("drbdsetup", Duration.millis(HeartBeatUpdateTimeout), LOGGER);
|
||||
Script sc = new Script("drbdsetup", Duration.millis(HeartBeatUpdateTimeoutInMs), LOGGER);
|
||||
sc.add("status");
|
||||
sc.add("--json");
|
||||
return sc.execute(parser);
|
||||
|
|
@ -329,7 +329,7 @@ public class LinstorStoragePool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
private String executeDrbdEventsNow(OutputInterpreter.AllLinesParser parser) {
|
||||
Script sc = new Script("drbdsetup", Duration.millis(HeartBeatUpdateTimeout), LOGGER);
|
||||
Script sc = new Script("drbdsetup", Duration.millis(HeartBeatUpdateTimeoutInMs), LOGGER);
|
||||
sc.add("events2");
|
||||
sc.add("--now");
|
||||
return sc.execute(parser);
|
||||
|
|
@ -369,8 +369,8 @@ public class LinstorStoragePool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
public Boolean hasVmActivity(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUUIDListString, String vmActivityCheckPath, long duration) {
|
||||
LOGGER.trace(String.format("Linstor.vmActivityCheck: %s, %s", pool.getPoolIp(), host.getPrivateNetwork().getIp()));
|
||||
return checkingHeartBeat(pool, host);
|
||||
return hasHeartBeat(pool, host);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,6 +232,20 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
|
|||
throw new CloudRuntimeException("Linstor: Unable to delete resource definition: " + rscDefName);
|
||||
}
|
||||
logger.info("Linstor: Deleted resource {}", rscDefName);
|
||||
|
||||
// LINSTOR can return success on the delete API call while the resource lingers in
|
||||
// DELETING state (peer issues, lost quorum, satellite down). Verify the resource is
|
||||
// actually gone — if not, log a WARN so operators see it. We deliberately do NOT
|
||||
// throw here: the volume is already considered gone on the CloudStack side, and
|
||||
// throwing would leave the CS DB and LINSTOR in different states.
|
||||
if (!LinstorUtil.waitForResourceDefinitionDeleted(linstorApi, rscDefName,
|
||||
LinstorUtil.DEFAULT_RD_DELETE_VERIFY_TIMEOUT_MILLIS))
|
||||
{
|
||||
logger.warn("Linstor: resource {} still present {}ms after delete returned success — " +
|
||||
"may be stuck in DELETING. Check the LINSTOR controller (linstor resource list) " +
|
||||
"and clear manually if the resource has no live peers.",
|
||||
rscDefName, LinstorUtil.DEFAULT_RD_DELETE_VERIFY_TIMEOUT_MILLIS);
|
||||
}
|
||||
} catch (ApiException apiEx)
|
||||
{
|
||||
logger.error("Linstor: ApiEx - " + apiEx.getMessage());
|
||||
|
|
|
|||
|
|
@ -401,6 +401,58 @@ public class LinstorUtil {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Default per-call timeout for {@link #waitForResourceDefinitionDeleted}. Long enough for a
|
||||
* healthy LINSTOR controller to finish a normal delete; short enough not to block the calling
|
||||
* thread for too long if the delete is genuinely stuck. Used both from the management server
|
||||
* (e.g. {@code LinstorPrimaryDataStoreDriverImpl}) and from KVM agent paths.
|
||||
*/
|
||||
public static final long DEFAULT_RD_DELETE_VERIFY_TIMEOUT_MILLIS = 30_000L;
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the named resource definition is no longer present on the LINSTOR
|
||||
* controller. Used after a {@code resourceDefinitionDelete} to verify the delete actually
|
||||
* completed (LINSTOR can return success on the API call while the resource lingers in
|
||||
* DELETING state due to peer issues, lost quorum, or down satellites). Uses the
|
||||
* controller-side name filter rather than scanning every RD on the cluster (cheap even
|
||||
* when polled once per second from {@link #waitForResourceDefinitionDeleted}).
|
||||
*/
|
||||
public static boolean isResourceDefinitionGone(DevelopersApi api, String rscName) throws ApiException {
|
||||
List<ResourceDefinition> matching =
|
||||
api.resourceDefinitionList(Collections.singletonList(rscName), false, null, null, null);
|
||||
return matching == null || matching.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the controller until the named resource definition is gone or the timeout elapses.
|
||||
* Returns {@code true} if the resource was confirmed gone, {@code false} if it was still
|
||||
* present (or the controller kept erroring) at the deadline. Callers should NOT throw on a
|
||||
* {@code false} return — the upstream API call already reported success and the operator
|
||||
* may need to investigate manually. Log a WARN with the resource name instead.
|
||||
*/
|
||||
public static boolean waitForResourceDefinitionDeleted(DevelopersApi api, String rscName, long timeoutMillis) {
|
||||
final long deadline = System.currentTimeMillis() + timeoutMillis;
|
||||
while (true) {
|
||||
try {
|
||||
if (isResourceDefinitionGone(api, rscName)) {
|
||||
return true;
|
||||
}
|
||||
} catch (ApiException e) {
|
||||
LOGGER.debug("LINSTOR delete-verify poll failed for {}: {}", rscName, e.getMessage());
|
||||
// Keep polling — controller may be transiently unavailable.
|
||||
}
|
||||
if (System.currentTimeMillis() >= deadline) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1_000L);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pair list of resource-definitions with ther 1:1 mapped resource-group objects that start with the
|
||||
* resource name `startWith`
|
||||
|
|
|
|||
|
|
@ -314,6 +314,58 @@ public class LinstorDataMotionStrategy implements DataMotionStrategy {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the destination KVM host is a registered LINSTOR satellite on the controller
|
||||
* backing every destination pool involved in this migration. Throws CloudRuntimeException
|
||||
* with a clear message when it isn't, instead of letting the resource creation later fail
|
||||
* obscurely inside auto-placement.
|
||||
*
|
||||
* Best-effort: a transient controller error during this check does not block the migration
|
||||
* — we log a warning and let the downstream resource-create surface the real issue. Only a
|
||||
* confirmed "host not in node list" outcome aborts the migration up-front.
|
||||
*/
|
||||
private void verifyDestinationIsLinstorSatellite(Map<VolumeInfo, DataStore> volumeDataStoreMap, Host destHost) {
|
||||
if (destHost == null || destHost.getName() == null) {
|
||||
// Without a destination host name to match, the only sensible thing is to let the
|
||||
// existing flow run and report whatever it would have reported.
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<VolumeInfo, DataStore> entry : volumeDataStoreMap.entrySet()) {
|
||||
DataStore destDataStore = entry.getValue();
|
||||
StoragePoolVO destStoragePool = _storagePool.findById(destDataStore.getId());
|
||||
if (destStoragePool == null
|
||||
|| destStoragePool.getPoolType() != Storage.StoragePoolType.Linstor) {
|
||||
continue;
|
||||
}
|
||||
DevelopersApi api = LinstorUtil.getLinstorAPI(destStoragePool.getHostAddress());
|
||||
try {
|
||||
List<String> nodes = LinstorUtil.getLinstorNodeNames(api);
|
||||
if (nodes == null) {
|
||||
logger.warn("LINSTOR controller {} returned null node list; skipping pre-flight",
|
||||
destStoragePool.getHostAddress());
|
||||
return;
|
||||
}
|
||||
if (!nodes.contains(destHost.getName())) {
|
||||
throw new CloudRuntimeException(String.format(
|
||||
"Cannot migrate to host '%s': it is not a registered LINSTOR satellite on " +
|
||||
"controller %s (pool '%s'). Known satellites: %s. Either register the " +
|
||||
"host with `linstor node create` or pick a different destination.",
|
||||
destHost.getName(),
|
||||
destStoragePool.getHostAddress(),
|
||||
destStoragePool.getName(),
|
||||
nodes));
|
||||
}
|
||||
} catch (ApiException apiEx) {
|
||||
// Don't block migration on a transient controller hiccup — log and let the
|
||||
// downstream resource creation handle the real failure.
|
||||
logger.warn("LINSTOR pre-flight check could not contact controller {}: {}; " +
|
||||
"letting downstream resource creation proceed",
|
||||
destStoragePool.getHostAddress(), apiEx.getBestMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMachineTO vmTO, Host srcHost,
|
||||
Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
|
|
@ -323,6 +375,15 @@ public class LinstorDataMotionStrategy implements DataMotionStrategy {
|
|||
String.format("Invalid hypervisor type [%s]. Only KVM supported", srcHost.getHypervisorType()));
|
||||
}
|
||||
|
||||
// Pre-flight: verify the destination KVM host is registered as a satellite on the
|
||||
// LINSTOR controller backing each destination pool. Without this check, resource
|
||||
// creation falls through to the resource-group's auto-placement filters and may
|
||||
// either silently place the resource on the wrong node or fail with an opaque
|
||||
// auto-place error from the LINSTOR API. Failing fast here gives operators a clear
|
||||
// actionable message instead of having to correlate the live-migration failure with
|
||||
// an unrelated LINSTOR controller log entry.
|
||||
verifyDestinationIsLinstorSatellite(volumeDataStoreMap, destHost);
|
||||
|
||||
String errMsg = null;
|
||||
VMInstanceVO vmInstance = _vmDao.findById(vmTO.getId());
|
||||
vmTO.setState(vmInstance.getState());
|
||||
|
|
|
|||
|
|
@ -198,11 +198,11 @@ public class StorPoolStoragePool implements KVMStoragePool {
|
|||
|
||||
@Override
|
||||
public String createHeartBeatCommand(HAStoragePool primaryStoragePool, String hostPrivateIp, boolean hostValidation) {
|
||||
boolean isStorageNodeUp = checkingHeartBeat(primaryStoragePool, null);
|
||||
boolean isStorageNodeUp = hasHeartBeat(primaryStoragePool, null);
|
||||
if (!isStorageNodeUp && !hostValidation) {
|
||||
//restart the host
|
||||
logger.debug(String.format("The host [%s] will be restarted because the health check failed for the storage pool [%s]", hostPrivateIp, primaryStoragePool.getPool().getType()));
|
||||
Script cmd = new Script(primaryStoragePool.getPool().getHearthBeatPath(), HeartBeatUpdateTimeout, logger);
|
||||
Script cmd = new Script(primaryStoragePool.getPool().getHearthBeatPath(), HeartBeatUpdateTimeoutInMs, logger);
|
||||
cmd.add("-c");
|
||||
cmd.execute();
|
||||
return "Down";
|
||||
|
|
@ -240,7 +240,7 @@ public class StorPoolStoragePool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Boolean checkingHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
public Boolean hasHeartBeat(HAStoragePool pool, HostTO host) {
|
||||
boolean isNodeWorking = false;
|
||||
OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
|
||||
|
||||
|
|
@ -300,8 +300,8 @@ public class StorPoolStoragePool implements KVMStoragePool {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUuidListString, String vmActivityCheckPath, long duration) {
|
||||
return checkingHeartBeat(pool, host);
|
||||
public Boolean hasVmActivity(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUuidListString, String vmActivityCheckPath, long duration) {
|
||||
return hasHeartBeat(pool, host);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -172,6 +172,18 @@
|
|||
<groupId>org.apache.directory.shared</groupId>
|
||||
<artifactId>shared-ldap-schema</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcutil-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
@ -191,12 +203,40 @@
|
|||
<artifactId>apacheds-core</artifactId>
|
||||
<version>${ads.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcutil-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.directory.server</groupId>
|
||||
<artifactId>apacheds-protocol-ldap</artifactId>
|
||||
<version>${ads.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcutil-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.directory.server</groupId>
|
||||
|
|
|
|||
23
pom.xml
23
pom.xml
|
|
@ -132,7 +132,7 @@
|
|||
<cs.axiom.version>1.2.8</cs.axiom.version>
|
||||
<cs.axis2.version>1.6.4</cs.axis2.version>
|
||||
<cs.batik.version>1.14</cs.batik.version>
|
||||
<cs.bcprov.version>1.70</cs.bcprov.version>
|
||||
<cs.bcprov.version>1.83</cs.bcprov.version>
|
||||
<cs.cglib.version>3.3.0</cs.cglib.version>
|
||||
<cs.checkstyle-lib.version>8.18</cs.checkstyle-lib.version>
|
||||
<cs.cron-utils.version>9.2.0</cs.cron-utils.version>
|
||||
|
|
@ -170,7 +170,7 @@
|
|||
<cs.libvirt-java.version>0.5.3</cs.libvirt-java.version>
|
||||
<cs.mail.version>1.5.0-b01</cs.mail.version>
|
||||
<cs.mustache.version>0.9.14</cs.mustache.version>
|
||||
<cs.mysql.version>8.0.33</cs.mysql.version>
|
||||
<cs.mysql.version>8.4.0</cs.mysql.version>
|
||||
<cs.neethi.version>2.0.4</cs.neethi.version>
|
||||
<cs.nitro.version>10.1</cs.nitro.version>
|
||||
<cs.opensaml.version>2.6.6</cs.opensaml.version>
|
||||
|
|
@ -191,6 +191,7 @@
|
|||
<cs.ini.version>0.5.4</cs.ini.version>
|
||||
<cs.caffeine.version>3.1.7</cs.caffeine.version>
|
||||
<cs.protobuf.version>3.25.5</cs.protobuf.version>
|
||||
<cs.minio.version>8.6.0</cs.minio.version>
|
||||
</properties>
|
||||
|
||||
<distributionManagement>
|
||||
|
|
@ -583,17 +584,17 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>${cs.bcprov.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>${cs.bcprov.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bctls-jdk15on</artifactId>
|
||||
<artifactId>bctls-jdk18on</artifactId>
|
||||
<version>${cs.bcprov.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
@ -668,6 +669,18 @@
|
|||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcutil-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ elif [ ! -f "$CACERT_FILE" ]; then
|
|||
fi
|
||||
|
||||
# Import cacerts into the keystore
|
||||
awk '/-----BEGIN CERTIFICATE-----?/{n++}{print > "cloudca." n }' "$CACERT_FILE"
|
||||
for caChain in $(ls cloudca.*); do
|
||||
awk 'BEGIN{n=0} /-----BEGIN CERTIFICATE-----/{n++} n>0{print > "cloudca." n }' "$CACERT_FILE"
|
||||
for caChain in $(ls cloudca.* 2>/dev/null); do
|
||||
keytool -delete -noprompt -alias "$caChain" -keystore "$KS_FILE" -storepass "$KS_PASS" > /dev/null 2>&1 || true
|
||||
keytool -import -noprompt -storepass "$KS_PASS" -trustcacerts -alias "$caChain" -file "$caChain" -keystore "$KS_FILE" > /dev/null 2>&1
|
||||
done
|
||||
|
|
@ -137,6 +137,22 @@ if [ -f "$SYSTEM_FILE" ]; then
|
|||
chmod 644 /usr/local/share/ca-certificates/cloudstack/ca.crt
|
||||
update-ca-certificates > /dev/null 2>&1 || true
|
||||
|
||||
# Import CA cert(s) into realhostip.keystore so the SSVM JVM
|
||||
# (which overrides the truststore via -Djavax.net.ssl.trustStore in _run.sh)
|
||||
# can trust servers signed by the CloudStack CA
|
||||
REALHOSTIP_KS_FILE="$(dirname "$(dirname "$PROPS_FILE")")/certs/realhostip.keystore"
|
||||
REALHOSTIP_PASS="vmops.com"
|
||||
if [ -f "$REALHOSTIP_KS_FILE" ]; then
|
||||
awk 'BEGIN{n=0} /-----BEGIN CERTIFICATE-----/{n++} n>0{print > "cloudca." n }' "$CACERT_FILE"
|
||||
for caChain in $(ls cloudca.* 2>/dev/null); do
|
||||
keytool -delete -noprompt -alias "$caChain" -keystore "$REALHOSTIP_KS_FILE" \
|
||||
-storepass "$REALHOSTIP_PASS" > /dev/null 2>&1 || true
|
||||
keytool -import -noprompt -trustcacerts -alias "$caChain" -file "$caChain" \
|
||||
-keystore "$REALHOSTIP_KS_FILE" -storepass "$REALHOSTIP_PASS" > /dev/null 2>&1
|
||||
done
|
||||
rm -f cloudca.*
|
||||
fi
|
||||
|
||||
# Ensure cloud service is running in systemvm
|
||||
if [ "$MODE" == "ssh" ]; then
|
||||
systemctl start cloud > /dev/null 2>&1
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ fi
|
|||
#delete VMs on this mountpoint
|
||||
deleteVMs() {
|
||||
local mountPoint=$1
|
||||
vmPids=$(ps aux| grep qemu | grep "$mountPoint" | awk '{print $2}' 2> /dev/null)
|
||||
vmPids=$(ps aux | grep qemu | grep "$mountPoint" | awk '{print $2}' 2> /dev/null)
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
return
|
||||
|
|
@ -93,7 +93,7 @@ deleteVMs() {
|
|||
}
|
||||
|
||||
#checking is there the same nfs server mounted under $MountPoint?
|
||||
mounts=$(cat /proc/mounts |grep nfs|grep $MountPoint)
|
||||
mounts=$(cat /proc/mounts | grep nfs | grep $MountPoint)
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
# remount it
|
||||
|
|
|
|||
|
|
@ -5034,16 +5034,13 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||
ex.addProxyObject(template.getUuid(), "templateId");
|
||||
throw ex;
|
||||
}
|
||||
|
||||
if (!template.isPublicTemplate() && caller.getType() == Account.Type.DOMAIN_ADMIN) {
|
||||
Account template_acc = accountMgr.getAccount(template.getAccountId());
|
||||
DomainVO domain = _domainDao.findById(template_acc.getDomainId());
|
||||
accountMgr.checkAccess(caller, domain);
|
||||
}
|
||||
|
||||
// if template is not public, perform permission check here
|
||||
else if (!template.isPublicTemplate() && caller.getType() != Account.Type.ADMIN) {
|
||||
accountMgr.checkAccess(caller, null, false, template);
|
||||
} else if (template.isPublicTemplate()) {
|
||||
} else if (template.isPublicTemplate() || caller.getType() != Account.Type.ADMIN) {
|
||||
// if template is not public or non-admin caller, perform permission check here
|
||||
accountMgr.checkAccess(caller, null, false, template);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1214,6 +1214,6 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager,
|
|||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[] {CpuOverprovisioningFactor, MemOverprovisioningFactor, StorageCapacityDisableThreshold, StorageOverprovisioningFactor,
|
||||
StorageAllocatedCapacityDisableThreshold, StorageOperationsExcludeCluster, ImageStoreNFSVersion, SecondaryStorageCapacityThreshold,
|
||||
StorageAllocatedCapacityDisableThresholdForVolumeSize, CapacityCalculateWorkers };
|
||||
StorageAllocatedCapacityDisableThresholdForVolumeSize, CapacityCalculateWorkers, KvmMemoryDynamicScalingCapacity, KvmCpuDynamicScalingCapacity };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public class CheckOnAgentInvestigator extends AdapterBase implements Investigato
|
|||
}
|
||||
|
||||
@Override
|
||||
public Status isAgentAlive(Host agent) {
|
||||
public Status getHostAgentStatus(Host agent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver
|
|||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.ha.HAConfig;
|
||||
import org.apache.cloudstack.ha.HAResource;
|
||||
import org.apache.cloudstack.ha.dao.HAConfigDao;
|
||||
import org.apache.cloudstack.managed.context.ManagedContext;
|
||||
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||
import org.apache.cloudstack.management.ManagementServerHost;
|
||||
|
|
@ -223,6 +226,8 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
@Inject
|
||||
ConfigurationDao _configDao;
|
||||
@Inject
|
||||
HAConfigDao _haConfigDao;
|
||||
@Inject
|
||||
VolumeOrchestrationService volumeMgr;
|
||||
|
||||
String _instance;
|
||||
|
|
@ -237,25 +242,53 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
long _timeBetweenCleanups;
|
||||
String _haTag = null;
|
||||
|
||||
protected HighAvailabilityManagerImpl() {
|
||||
}
|
||||
|
||||
private boolean vmHasPendingHAJob(final List<HaWorkVO> pendingHaWorks, final VMInstanceVO vm) {
|
||||
Optional<HaWorkVO> item = pendingHaWorks.stream()
|
||||
.filter(h -> h.getInstanceId() == vm.getId())
|
||||
.reduce((first, second) -> second);
|
||||
if (item.isPresent() && (item.get().getTimesTried() < _maxRetries ||
|
||||
!item.get().canScheduleNew(_timeBetweenFailures))) {
|
||||
logger.debug(String.format("Skipping HA on %s as there is already a running HA job for it", vm));
|
||||
logger.debug("Skipping HA on {} as there is already a running HA job for it", vm);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected HighAvailabilityManagerImpl() {
|
||||
private boolean isHostHAInspectionInProgress(long hostId) {
|
||||
final HAConfig haConfig = _haConfigDao.findHAResource(hostId, HAResource.ResourceType.Host);
|
||||
if (haConfig == null || !haConfig.isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HAConfig.HAState state = haConfig.getState();
|
||||
logger.debug("Checking Host HA inspection is in progress or not for the host {} from HAConfig, HA state is {}", hostId, state);
|
||||
if (state == HAConfig.HAState.Suspect || state == HAConfig.HAState.Checking) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state == HAConfig.HAState.Recovered || state == HAConfig.HAState.Available) {
|
||||
// If the host HA state is Recovered, it indicates that the host has restarted successfully.
|
||||
// If the host HA state is Available, it means the host has restarted successfully and the recovery waiting period has completed.
|
||||
// In both states, the agent can connect as soon as the host is ready (and can move to Suspect -> Checking HA state if the agent connection fails again before Fencing).
|
||||
final HostVO host = _hostDao.findById(hostId);
|
||||
if (host != null && host.getStatus() != Status.Up) {
|
||||
logger.debug("{} is in {} status and HA state is {}, considering Host HA inspection is still in progress" +
|
||||
" until we are sure the host is ready after a recovery wait period and agent is connected/Up", host, host.getStatus(), state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status investigate(final long hostId) {
|
||||
final HostVO host = _hostDao.findById(hostId);
|
||||
if (host == null) {
|
||||
logger.warn("Host with id {} is removed or doesn't exists.", hostId);
|
||||
return Status.Alert;
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +303,7 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
|
||||
Status hostState = null;
|
||||
for (Investigator investigator : investigators) {
|
||||
hostState = investigator.isAgentAlive(host);
|
||||
hostState = investigator.getHostAgentStatus(host);
|
||||
if (hostState != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{} was able to determine host {} is in {}", investigator.getName(), host, hostState.toString());
|
||||
|
|
@ -278,7 +311,7 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
return hostState;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(investigator.getName() + " unable to determine the state of the host. Moving on.");
|
||||
logger.debug("{} unable to determine the state of the host. Moving on.", investigator.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -570,9 +603,9 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
protected Long restart(final HaWorkVO work) {
|
||||
logger.debug("RESTART with HAWORK");
|
||||
logger.debug("RESTART with HA WORK");
|
||||
List<HaWorkVO> items = _haDao.listFutureHaWorkForVm(work.getInstanceId(), work.getId());
|
||||
if (items.size() > 0) {
|
||||
if (!items.isEmpty()) {
|
||||
StringBuilder str = new StringBuilder("Cancelling this work item because newer ones have been scheduled. Work Ids = [");
|
||||
for (HaWorkVO item : items) {
|
||||
str.append(item.getId()).append(", ");
|
||||
|
|
@ -583,7 +616,7 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
items = _haDao.listRunningHaWorkForVm(work.getInstanceId());
|
||||
if (items.size() > 0) {
|
||||
if (!items.isEmpty()) {
|
||||
StringBuilder str = new StringBuilder("Waiting because there's HA work being executed on an item currently. Work Ids =[");
|
||||
for (HaWorkVO item : items) {
|
||||
str.append(item.getId()).append(", ");
|
||||
|
|
@ -597,21 +630,21 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
|
||||
VirtualMachine vm = _itMgr.findById(work.getInstanceId());
|
||||
if (vm == null) {
|
||||
logger.info("Unable to find vm: " + vmId);
|
||||
logger.info("Unable to find vm: {}", vmId);
|
||||
return null;
|
||||
}
|
||||
if (checkAndCancelWorkIfNeeded(work)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("HA on " + vm);
|
||||
logger.info("HA on {}", vm);
|
||||
if (vm.getState() != work.getPreviousState() || vm.getUpdated() != work.getUpdateTime()) {
|
||||
logger.info("VM " + vm + " has been changed. Current State = " + vm.getState() + " Previous State = " + work.getPreviousState() + " last updated = " +
|
||||
vm.getUpdated() + " previous updated = " + work.getUpdateTime());
|
||||
logger.info("VM {} has been changed. Current State = {} Previous State = {} last updated = {} previous updated = {}",
|
||||
vm, vm.getState(), work.getPreviousState(), vm.getUpdated(), work.getUpdateTime());
|
||||
return null;
|
||||
}
|
||||
if (vm.getHostId() != null && !vm.getHostId().equals(work.getHostId())) {
|
||||
logger.info("VM " + vm + " has been changed. Current host id = " + vm.getHostId() + " Previous host id = " + work.getHostId());
|
||||
logger.info("VM {} has been changed. Current host id = {} Previous host id = {}", vm, vm.getHostId(), work.getHostId());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -628,10 +661,13 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
boolean isHostRemoved = false;
|
||||
if (host == null) {
|
||||
host = _hostDao.findByIdIncludingRemoved(work.getHostId());
|
||||
if (host != null) {
|
||||
logger.debug("VM {} is now no longer on host {} as the host is removed", vm, host);
|
||||
isHostRemoved = true;
|
||||
if (host == null) {
|
||||
logger.debug("VM {} is now no longer on host {}, the host doesn't exist", vm, work.getHostId());
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug("VM {} is now no longer on host {} as the host is removed", vm, host);
|
||||
isHostRemoved = true;
|
||||
}
|
||||
|
||||
DataCenterVO dcVO = _dcDao.findById(host.getDataCenterId());
|
||||
|
|
@ -652,40 +688,39 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
try
|
||||
{
|
||||
alive = investigator.isVmAlive(vm, host);
|
||||
logger.info(investigator.getName() + " found " + vm + " to be alive? " + alive);
|
||||
logger.info("{} found {} to be alive? {}", investigator.getName(), vm, alive);
|
||||
break;
|
||||
} catch (UnknownVM e) {
|
||||
logger.info(investigator.getName() + " could not find " + vm);
|
||||
logger.info("{} could not find {}", investigator.getName(), vm);
|
||||
}
|
||||
}
|
||||
|
||||
boolean fenced = false;
|
||||
if (alive == null) {
|
||||
logger.debug("Fencing off VM that we don't know the state of");
|
||||
logger.debug("Fencing off VM {} that we don't know the state of", vm);
|
||||
for (FenceBuilder fb : fenceBuilders) {
|
||||
Boolean result = fb.fenceOff(vm, host);
|
||||
logger.info("Fencer " + fb.getName() + " returned " + result);
|
||||
logger.info("Fencer {} returned {}", fb.getName(), result);
|
||||
if (result != null && result) {
|
||||
fenced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (!alive) {
|
||||
fenced = true;
|
||||
} else {
|
||||
logger.debug("VM {} is found to be alive by {}", vm, investigator.getName());
|
||||
logger.debug("VM {} is found to be alive by {} on host {}", vm, investigator.getName(), host);
|
||||
if (host.getStatus() == Status.Up) {
|
||||
logger.info(vm + " is alive and host is up. No need to restart it.");
|
||||
logger.info("{} is alive and host {} is up. No need to restart it.", vm, host);
|
||||
return null;
|
||||
} else {
|
||||
logger.debug("Rescheduling because the host is not up but the vm is alive");
|
||||
logger.debug("Rescheduling because the host {} is not up but the vm {} is alive", host, vm);
|
||||
return (System.currentTimeMillis() >> 10) + _investigateRetryInterval;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fenced) {
|
||||
logger.debug("We were unable to fence off the VM " + vm);
|
||||
logger.debug("We were unable to fence off the VM {}", vm);
|
||||
_alertMgr.sendAlert(alertType, vm.getDataCenterId(), vm.getPodIdToDeployIn(), "Unable to restart " + vm.getHostName() +
|
||||
" which was running on host " + hostDesc, "Insufficient capacity to restart VM, name: " + vm.getHostName() + ", id: " + vmId +
|
||||
" which was running on host " + hostDesc);
|
||||
|
|
@ -728,15 +763,15 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
|
||||
if (!ForceHA.value() && !vm.isHaEnabled()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("VM is not HA enabled so we're done.");
|
||||
logger.debug("VM {} is not HA enabled so we're done.", vm);
|
||||
}
|
||||
return null; // VM doesn't require HA
|
||||
}
|
||||
|
||||
if ((host == null || host.getRemoved() != null || host.getState() != Status.Up)
|
||||
if ((host.getRemoved() != null || host.getState() != Status.Up)
|
||||
&& !volumeMgr.canVmRestartOnAnotherServer(vm.getId())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("VM can not restart on another server.");
|
||||
logger.debug("VM {} can not restart on another server.", vm);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -777,13 +812,13 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
if (started != null && started.getState() == VirtualMachine.State.Running) {
|
||||
String message = String.format("HA starting VM: %s (%s)", started.getHostName(), started.getInstanceName());
|
||||
HostVO hostVmHasStarted = _hostDao.findById(started.getHostId());
|
||||
logger.info(String.format("HA is now restarting %s on %s", started, hostVmHasStarted));
|
||||
logger.info("HA is now restarting {} on {}", started, hostVmHasStarted);
|
||||
_alertMgr.sendAlert(alertType, vm.getDataCenterId(), vm.getPodIdToDeployIn(), message, message);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Rescheduling VM " + vm.toString() + " to try again in " + _restartRetryInterval);
|
||||
logger.debug("Rescheduling VM {} to try again in {}", vm.toString(), _restartRetryInterval);
|
||||
}
|
||||
} catch (final InsufficientCapacityException e) {
|
||||
logger.warn("Unable to restart " + vm.toString() + " due to " + e.getMessage());
|
||||
|
|
@ -815,6 +850,9 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
if (!CancellableWorkReasonTypes.contains(work.getReasonType())) {
|
||||
return false;
|
||||
}
|
||||
if (isHostHAInspectionInProgress(work.getHostId())) {
|
||||
return false;
|
||||
}
|
||||
Status hostStatus = investigate(work.getHostId());
|
||||
if (!Status.Up.equals(hostStatus)) {
|
||||
return false;
|
||||
|
|
@ -825,13 +863,14 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
public Long migrate(final HaWorkVO work) {
|
||||
logger.debug("MIGRATE with HA WORK");
|
||||
long vmId = work.getInstanceId();
|
||||
long srcHostId = work.getHostId();
|
||||
HostVO srcHost = _hostDao.findById(srcHostId);
|
||||
|
||||
VMInstanceVO vm = _instanceDao.findById(vmId);
|
||||
if (vm == null) {
|
||||
logger.info("Unable to find vm: " + vmId + ", skipping migrate.");
|
||||
logger.info("Unable to find vm: {}, skipping migrate.", vmId);
|
||||
return null;
|
||||
}
|
||||
if (checkAndCancelWorkIfNeeded(work)) {
|
||||
|
|
@ -840,11 +879,11 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
logger.info("Migration attempt: for {} from {}. Starting attempt: {}/{} times.", vm, srcHost, 1 + work.getTimesTried(), _maxRetries);
|
||||
|
||||
if (VirtualMachine.State.Stopped.equals(vm.getState())) {
|
||||
logger.info(String.format("vm %s is Stopped, skipping migrate.", vm));
|
||||
logger.info("vm {} is Stopped, skipping migrate.", vm);
|
||||
return null;
|
||||
}
|
||||
if (VirtualMachine.State.Running.equals(vm.getState()) && srcHostId != vm.getHostId()) {
|
||||
logger.info(String.format("VM %s is running on a different host %s, skipping migration", vm, vm.getHostId()));
|
||||
logger.info("VM {} is running on a different host {}, skipping migration", vm, vm.getHostId());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -879,7 +918,7 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
final HaWorkVO work = new HaWorkVO(vm.getId(), vm.getType(), WorkType.Destroy, Step.Scheduled, hostId, vm.getState(), 0, vm.getUpdated(), reasonType);
|
||||
_haDao.persist(work);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Scheduled " + work.toString());
|
||||
logger.debug("{}}", work.toString());
|
||||
}
|
||||
wakeupWorkers();
|
||||
return true;
|
||||
|
|
@ -897,7 +936,7 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
private void destroyVM(VirtualMachine vm, boolean expunge) throws OperationTimedoutException, AgentUnavailableException {
|
||||
logger.info("Destroying " + vm.toString());
|
||||
logger.info("Destroying {}", vm.toString());
|
||||
if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) {
|
||||
consoleProxyManager.destroyProxy(vm.getId());
|
||||
} else if (VirtualMachine.Type.SecondaryStorageVm.equals(vm.getType())) {
|
||||
|
|
@ -908,9 +947,10 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
protected Long destroyVM(final HaWorkVO work) {
|
||||
logger.debug("DESTROY with HA WORK");
|
||||
final VirtualMachine vm = _itMgr.findById(work.getInstanceId());
|
||||
if (vm == null) {
|
||||
logger.info("No longer can find VM " + work.getInstanceId() + ". Throwing away " + work);
|
||||
logger.info("No longer can find VM {}. Throwing away {}", work.getInstanceId(), work);
|
||||
return null;
|
||||
}
|
||||
if (checkAndCancelWorkIfNeeded(work)) {
|
||||
|
|
@ -944,20 +984,21 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
protected Long stopVM(final HaWorkVO work) throws ConcurrentOperationException {
|
||||
logger.debug("STOP with HA WORK");
|
||||
VirtualMachine vm = _itMgr.findById(work.getInstanceId());
|
||||
if (vm == null) {
|
||||
logger.info("No longer can find VM " + work.getInstanceId() + ". Throwing away " + work);
|
||||
logger.info("No longer can find VM {}. Throwing away {}", work.getInstanceId(), work);
|
||||
work.setStep(Step.Done);
|
||||
return null;
|
||||
}
|
||||
if (checkAndCancelWorkIfNeeded(work)) {
|
||||
return null;
|
||||
}
|
||||
logger.info("Stopping " + vm);
|
||||
logger.info("Stopping {}", vm);
|
||||
try {
|
||||
if (work.getWorkType() == WorkType.Stop) {
|
||||
_itMgr.advanceStop(vm.getUuid(), false);
|
||||
logger.info("Successfully stopped " + vm);
|
||||
logger.info("Successfully stopped {}", vm);
|
||||
return null;
|
||||
} else if (work.getWorkType() == WorkType.CheckStop) {
|
||||
if ((vm.getState() != work.getPreviousState()) || vm.getUpdated() != work.getUpdateTime() || vm.getHostId() == null ||
|
||||
|
|
@ -969,7 +1010,7 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
_itMgr.advanceStop(vm.getUuid(), false);
|
||||
logger.info("Stop for " + vm + " was successful");
|
||||
logger.info("Stop for {} was successful", vm);
|
||||
return null;
|
||||
} else if (work.getWorkType() == WorkType.ForceStop) {
|
||||
if ((vm.getState() != work.getPreviousState()) || vm.getUpdated() != work.getUpdateTime() || vm.getHostId() == null ||
|
||||
|
|
@ -981,13 +1022,13 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
_itMgr.advanceStop(vm.getUuid(), true);
|
||||
logger.info("Stop for " + vm + " was successful");
|
||||
logger.info("Stop for {} was successful", vm);
|
||||
return null;
|
||||
} else {
|
||||
assert false : "Who decided there's other steps but didn't modify the guy who does the work?";
|
||||
}
|
||||
} catch (final ResourceUnavailableException e) {
|
||||
logger.debug("Agnet is not available" + e.getMessage());
|
||||
logger.debug("Agent is not available" + e.getMessage());
|
||||
} catch (OperationTimedoutException e) {
|
||||
logger.debug("operation timed out: " + e.getMessage());
|
||||
}
|
||||
|
|
@ -1043,7 +1084,8 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
try {
|
||||
if (vm != null && !VmHaEnabled.valueIn(vm.getDataCenterId())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("VM high availability manager is disabled, rescheduling the HA work %s, for the VM %s (id) to retry later in case VM high availability manager is enabled on retry attempt", work, vm.getName(), vm.getId()));
|
||||
logger.debug("VM high availability manager is disabled, rescheduling the HA work {} for the VM {} ({}) " +
|
||||
"to retry later in case VM high availability manager is enabled on retry attempt", work, vm.getName(), vm.getId());
|
||||
}
|
||||
long nextTime = getRescheduleTime(wt);
|
||||
rescheduleWork(work, nextTime);
|
||||
|
|
@ -1065,13 +1107,13 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
}
|
||||
|
||||
if (nextTime == null) {
|
||||
logger.info("Completed work " + work + ". Took " + (work.getTimesTried() + 1) + "/" + _maxRetries + " attempts.");
|
||||
logger.info("Completed work {}. Took {}/{} attempts.", work, work.getTimesTried() + 1, _maxRetries);
|
||||
work.setStep(Step.Done);
|
||||
} else {
|
||||
rescheduleWork(work, nextTime.longValue());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Encountered unhandled exception during HA process, reschedule work", e);
|
||||
logger.warn("Encountered unhandled exception during HA process, reschedule work {}", work, e);
|
||||
|
||||
long nextTime = getRescheduleTime(wt);
|
||||
rescheduleWork(work, nextTime);
|
||||
|
|
@ -1085,11 +1127,11 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
|
|||
} finally {
|
||||
if (!Step.Done.equals(work.getStep())) {
|
||||
if (work.getTimesTried() >= _maxRetries) {
|
||||
logger.warn("Giving up, retried max " + work.getTimesTried() + "/" + _maxRetries + " times for work: " + work);
|
||||
logger.warn("Giving up, retried max {}/{} times for work: {}", work.getTimesTried(), _maxRetries, work);
|
||||
work.setStep(Step.Done);
|
||||
} else {
|
||||
logger.warn("Rescheduling work " + work + " to try again at " + new Date(work.getTimeToTry() << 10) +
|
||||
". Finished attempt " + work.getTimesTried() + "/" + _maxRetries + " times.");
|
||||
logger.warn("Rescheduling work {} to try again at {}. Finished attempt {}/{} times.",
|
||||
work, new Date(work.getTimeToTry() << 10), work.getTimesTried(), _maxRetries);
|
||||
}
|
||||
}
|
||||
_haDao.update(work.getId(), work);
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public class KVMFencer extends AdapterBase implements FenceBuilder {
|
|||
@Override
|
||||
public Boolean fenceOff(VirtualMachine vm, Host host) {
|
||||
if (host.getHypervisorType() != HypervisorType.KVM && host.getHypervisorType() != HypervisorType.LXC) {
|
||||
logger.warn("Don't know how to fence non kvm hosts " + host.getHypervisorType());
|
||||
logger.warn("Don't know how to fence non kvm hosts {}", host.getHypervisorType());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -97,11 +97,8 @@ public class KVMFencer extends AdapterBase implements FenceBuilder {
|
|||
FenceAnswer answer;
|
||||
try {
|
||||
answer = (FenceAnswer)_agentMgr.send(h.getId(), fence);
|
||||
} catch (AgentUnavailableException e) {
|
||||
logger.info("Moving on to the next host because " + h.toString() + " is unavailable", e);
|
||||
continue;
|
||||
} catch (OperationTimedoutException e) {
|
||||
logger.info("Moving on to the next host because " + h.toString() + " is unavailable", e);
|
||||
} catch (AgentUnavailableException | OperationTimedoutException e) {
|
||||
logger.info("Moving on to the next host because {} is unavailable", h.toString(), e);
|
||||
continue;
|
||||
}
|
||||
if (answer != null && answer.getResult()) {
|
||||
|
|
@ -115,7 +112,7 @@ public class KVMFencer extends AdapterBase implements FenceBuilder {
|
|||
"Fencing off host " + host.getId() + " did not succeed after asking " + i + " hosts. " +
|
||||
"Check Agent logs for more information.");
|
||||
|
||||
logger.error("Unable to fence off " + vm.toString() + " on " + host.toString());
|
||||
logger.error("Unable to fence off {} on {}", vm.toString(), host.toString());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ public class ManagementIPSystemVMInvestigator extends AbstractInvestigatorImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Status isAgentAlive(Host agent) {
|
||||
public Status getHostAgentStatus(Host agent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ public class UserVmDomRInvestigator extends AbstractInvestigatorImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Status isAgentAlive(Host agent) {
|
||||
public Status getHostAgentStatus(Host agent) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("checking if agent ({}) is alive", agent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class XenServerInvestigator extends AdapterBase implements Investigator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Status isAgentAlive(Host agent) {
|
||||
public Status getHostAgentStatus(Host agent) {
|
||||
if (agent.getHypervisorType() != HypervisorType.XenServer) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -74,7 +74,7 @@ public class XenServerInvestigator extends AdapterBase implements Investigator {
|
|||
|
||||
@Override
|
||||
public boolean isVmAlive(VirtualMachine vm, Host host) throws UnknownVM {
|
||||
Status status = isAgentAlive(host);
|
||||
Status status = getHostAgentStatus(host);
|
||||
if (status == null) {
|
||||
throw new UnknownVM();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import com.cloud.agent.api.Command;
|
|||
import com.cloud.agent.api.to.DataObjectType;
|
||||
import com.cloud.agent.api.to.NicTO;
|
||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||
import com.cloud.configuration.ConfigurationManagerImpl;
|
||||
import com.cloud.capacity.CapacityManager;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.host.HostVO;
|
||||
|
|
@ -44,6 +44,7 @@ import com.cloud.vm.VirtualMachine;
|
|||
import com.cloud.vm.VirtualMachineProfile;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.storage.command.CopyCommand;
|
||||
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
|
@ -54,9 +55,7 @@ import java.math.RoundingMode;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
|
||||
@Inject
|
||||
|
|
@ -130,30 +129,47 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
|
|||
* @param vmProfile vm profile
|
||||
*/
|
||||
protected void setVmQuotaPercentage(VirtualMachineTO to, VirtualMachineProfile vmProfile) {
|
||||
if (to.isLimitCpuUse()) {
|
||||
VirtualMachine vm = vmProfile.getVirtualMachine();
|
||||
HostVO host = hostDao.findById(vm.getHostId());
|
||||
if (host == null) {
|
||||
logger.warn("Host is not available. Skipping setting CPU quota percentage for VM: {}", vm);
|
||||
return;
|
||||
}
|
||||
logger.debug("Limiting CPU usage for VM: {} on host: {}", vm, host);
|
||||
double hostMaxSpeed = getHostCPUSpeed(host);
|
||||
double maxSpeed = getVmSpeed(to);
|
||||
try {
|
||||
BigDecimal percent = new BigDecimal(maxSpeed / hostMaxSpeed);
|
||||
percent = percent.setScale(2, RoundingMode.HALF_DOWN);
|
||||
if (percent.compareTo(new BigDecimal(1)) == 1) {
|
||||
logger.debug("VM {} CPU MHz exceeded host {} CPU MHz, limiting VM CPU to the host maximum", vm, host);
|
||||
percent = new BigDecimal(1);
|
||||
}
|
||||
to.setCpuQuotaPercentage(percent.doubleValue());
|
||||
logger.debug("Host: {} max CPU speed = {} MHz, VM: {} max CPU speed = {} MHz. " +
|
||||
"Setting CPU quota percentage as: {}",
|
||||
host, hostMaxSpeed, vm, maxSpeed, percent.doubleValue());
|
||||
} catch (NumberFormatException e) {
|
||||
logger.error("Error calculating VM: {} quota percentage, it will not be set. Error: {}", vm, e.getMessage(), e);
|
||||
if (!to.isLimitCpuUse()) {
|
||||
return;
|
||||
}
|
||||
|
||||
VirtualMachine vm = vmProfile.getVirtualMachine();
|
||||
HostVO host = hostDao.findById(vm.getHostId());
|
||||
if (host == null) {
|
||||
logger.warn("Host is not available. Skipping setting CPU quota percentage for VM: [{}].", vm);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Limiting CPU usage for VM: [{}] on host: [{}].", vm, host);
|
||||
double maxSpeed = getVmSpeed(to);
|
||||
double hostMaxSpeed = getHostCPUSpeed(host);
|
||||
Double cpuQuotaPercentage = getCpuQuotaPercentage(maxSpeed, hostMaxSpeed);
|
||||
if (cpuQuotaPercentage != null) {
|
||||
to.setCpuQuotaPercentage(cpuQuotaPercentage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the VM quota percentage based on the VM and host CPU speeds.
|
||||
* @param vmSpeeed Speed of the VM.
|
||||
* @param hostSpeed Speed of the host.
|
||||
* @return The VM quota percentage.
|
||||
*/
|
||||
public Double getCpuQuotaPercentage(double vmSpeeed, double hostSpeed) {
|
||||
logger.debug("Calculating CPU quota percentage for VM with speed [{}] on host with speed [{}].", vmSpeeed, hostSpeed);
|
||||
try {
|
||||
BigDecimal percent = new BigDecimal(vmSpeeed / hostSpeed);
|
||||
percent = percent.setScale(2, RoundingMode.HALF_DOWN);
|
||||
if (percent.compareTo(new BigDecimal(1)) > 0) {
|
||||
logger.debug("VM CPU speed exceeded host CPU speed and, therefore, limiting VM CPU quota to the host maximum.");
|
||||
percent = new BigDecimal(1);
|
||||
}
|
||||
double quotaPercentage = percent.doubleValue();
|
||||
logger.info("Calculated CPU quota percentage for VM with speed [{}] on host with speed [{}] is [{}].", vmSpeeed, hostSpeed, quotaPercentage);
|
||||
return quotaPercentage;
|
||||
} catch (NumberFormatException e) {
|
||||
logger.info("Could not calculate CPU quota percentage for VM with speed [{}] on host with speed [{}]. Therefore, CPU limitation will not be set for the domain.", vmSpeeed, hostSpeed);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,28 +230,31 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
|
|||
Pair<Long, Integer> max = getHostMaxMemoryAndCpuCores(hostVo, virtualMachine, vmDescription);
|
||||
|
||||
Long maxHostMemory = max.first();
|
||||
Integer maxHostCpuCore = max.second();
|
||||
Integer maxHostCpuCores = max.second();
|
||||
|
||||
long minMemory = virtualMachineTo.getMinRam();
|
||||
Long maxMemory = virtualMachineTo.getMaxRam();
|
||||
int minCpuCores = virtualMachineTo.getCpus();
|
||||
Integer maxCpuCores = minCpuCores;
|
||||
long requestedMemory = maxMemory;
|
||||
|
||||
ServiceOfferingVO serviceOfferingVO = serviceOfferingDao.findById(virtualMachineProfile.getId(), virtualMachineProfile.getServiceOfferingId());
|
||||
if (isVmDynamicScalable(serviceOfferingVO, virtualMachineTo, virtualMachine)) {
|
||||
int minCpuCores = virtualMachineTo.getCpus();
|
||||
int maxCpuCores = minCpuCores;
|
||||
|
||||
if (isVmDynamicScalable(virtualMachineTo, virtualMachine)) {
|
||||
ServiceOfferingVO serviceOfferingVO = serviceOfferingDao.findById(virtualMachineProfile.getId(), virtualMachineProfile.getServiceOfferingId());
|
||||
serviceOfferingDao.loadDetails(serviceOfferingVO);
|
||||
|
||||
maxMemory = getVmMaxMemory(serviceOfferingVO, vmDescription, maxHostMemory);
|
||||
maxCpuCores = getVmMaxCpuCores(serviceOfferingVO, vmDescription, maxHostCpuCore);
|
||||
Long clusterId = hostVo != null ? hostVo.getClusterId() : null;
|
||||
maxMemory = getVmMaxMemory(serviceOfferingVO, vmDescription, maxHostMemory, clusterId);
|
||||
maxCpuCores = getVmMaxCpuCores(serviceOfferingVO, vmDescription, maxHostCpuCores, clusterId);
|
||||
}
|
||||
|
||||
virtualMachineTo.setRam(minMemory, maxMemory);
|
||||
virtualMachineTo.setRam(minMemory, maxMemory, requestedMemory);
|
||||
virtualMachineTo.setCpus(minCpuCores);
|
||||
virtualMachineTo.setVcpuMaxLimit(maxCpuCores);
|
||||
}
|
||||
|
||||
protected boolean isVmDynamicScalable(ServiceOfferingVO serviceOfferingVO, VirtualMachineTO virtualMachineTo, VirtualMachine virtualMachine) {
|
||||
return serviceOfferingVO.isDynamic() && virtualMachineTo.isEnableDynamicallyScaleVm() && UserVmManager.EnableDynamicallyScaleVm.valueIn(virtualMachine.getDataCenterId());
|
||||
protected boolean isVmDynamicScalable(VirtualMachineTO virtualMachineTo, VirtualMachine virtualMachine) {
|
||||
return virtualMachineTo.isEnableDynamicallyScaleVm() && UserVmManager.EnableDynamicallyScaleVm.valueIn(virtualMachine.getDataCenterId());
|
||||
}
|
||||
|
||||
protected Pair<Long, Integer> getHostMaxMemoryAndCpuCores(HostVO host, VirtualMachine virtualMachine, String vmDescription){
|
||||
|
|
@ -263,53 +282,34 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
|
|||
return new Pair<>(maxHostMemory, maxHostCpuCore);
|
||||
}
|
||||
|
||||
protected Long getVmMaxMemory(ServiceOfferingVO serviceOfferingVO, String vmDescription, Long maxHostMemory) {
|
||||
String serviceOfferingDescription = serviceOfferingVO.toString();
|
||||
|
||||
protected Long getVmMaxMemory(ServiceOfferingVO serviceOfferingVO, String vmDescription, Long maxHostMemory, Long clusterId) {
|
||||
Long maxMemory;
|
||||
Integer customOfferingMaxMemory = NumberUtils.createInteger(serviceOfferingVO.getDetail(ApiConstants.MAX_MEMORY));
|
||||
Integer maxMemoryConfig = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
|
||||
if (customOfferingMaxMemory != null) {
|
||||
logger.debug(String.format("Using 'Custom unconstrained' %s max memory value [%sMb] as %s memory.", serviceOfferingDescription, customOfferingMaxMemory, vmDescription));
|
||||
maxMemory = ByteScaleUtils.mebibytesToBytes(customOfferingMaxMemory);
|
||||
ConfigKey<Integer> maxMemoryConfig = CapacityManager.KvmMemoryDynamicScalingCapacity;
|
||||
Integer maxMemoryConfigValue = maxMemoryConfig.valueIn(clusterId);
|
||||
logger.info("[{}] is a dynamically scalable service offering. Using config [{}] value [{}] in cluster [ID: {}] as max [{}] memory.",
|
||||
serviceOfferingVO.toString(), maxMemoryConfig.key(), maxMemoryConfigValue, clusterId, vmDescription);
|
||||
if (maxMemoryConfigValue > 0) {
|
||||
maxMemory = ByteScaleUtils.mebibytesToBytes(maxMemoryConfigValue);
|
||||
} else {
|
||||
String maxMemoryConfigKey = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.key();
|
||||
|
||||
logger.info(String.format("%s is a 'Custom unconstrained' service offering. Using config [%s] value [%s] as max %s memory.",
|
||||
serviceOfferingDescription, maxMemoryConfigKey, maxMemoryConfig, vmDescription));
|
||||
|
||||
if (maxMemoryConfig > 0) {
|
||||
maxMemory = ByteScaleUtils.mebibytesToBytes(maxMemoryConfig);
|
||||
} else {
|
||||
logger.info(String.format("Config [%s] has value less or equal '0'. Using %s host or last host max memory [%s] as VM max memory in the hypervisor.", maxMemoryConfigKey, vmDescription, maxHostMemory));
|
||||
maxMemory = maxHostMemory;
|
||||
}
|
||||
logger.info("Config [{}] in cluster [ID: {}] has value less or equal '0'. Using [{}] host or last host max memory [{}] as VM max memory in the hypervisor.",
|
||||
maxMemoryConfig.key(), clusterId, vmDescription, maxHostMemory);
|
||||
maxMemory = maxHostMemory;
|
||||
}
|
||||
return maxMemory;
|
||||
}
|
||||
|
||||
protected Integer getVmMaxCpuCores(ServiceOfferingVO serviceOfferingVO, String vmDescription, Integer maxHostCpuCore) {
|
||||
String serviceOfferingDescription = serviceOfferingVO.toString();
|
||||
|
||||
protected Integer getVmMaxCpuCores(ServiceOfferingVO serviceOfferingVO, String vmDescription, Integer maxHostCpuCores, Long clusterId) {
|
||||
Integer maxCpuCores;
|
||||
Integer customOfferingMaxCpuCores = NumberUtils.createInteger(serviceOfferingVO.getDetail(ApiConstants.MAX_CPU_NUMBER));
|
||||
Integer maxCpuCoresConfig = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value();
|
||||
|
||||
if (customOfferingMaxCpuCores != null) {
|
||||
logger.debug(String.format("Using 'Custom unconstrained' %s max cpu cores [%s] as %s cpu cores.", serviceOfferingDescription, customOfferingMaxCpuCores, vmDescription));
|
||||
maxCpuCores = customOfferingMaxCpuCores;
|
||||
ConfigKey<Integer> maxCpuCoresConfig = CapacityManager.KvmCpuDynamicScalingCapacity;
|
||||
Integer maxCpuCoresConfigValue = maxCpuCoresConfig.valueIn(clusterId);
|
||||
logger.info("[{}] is a dynamically scalable service offering. Using config [{}] value [{}] in cluster [ID: {}] as max [{}] CPU cores.",
|
||||
serviceOfferingVO.toString(), maxCpuCoresConfig.key(), maxCpuCoresConfigValue, clusterId, vmDescription);
|
||||
if (maxCpuCoresConfigValue > 0) {
|
||||
maxCpuCores = maxCpuCoresConfigValue;
|
||||
} else {
|
||||
String maxCpuCoreConfigKey = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.key();
|
||||
|
||||
logger.info(String.format("%s is a 'Custom unconstrained' service offering. Using config [%s] value [%s] as max %s cpu cores.",
|
||||
serviceOfferingDescription, maxCpuCoreConfigKey, maxCpuCoresConfig, vmDescription));
|
||||
|
||||
if (maxCpuCoresConfig > 0) {
|
||||
maxCpuCores = maxCpuCoresConfig;
|
||||
} else {
|
||||
logger.info(String.format("Config [%s] has value less or equal '0'. Using %s host or last host max cpu cores [%s] as VM cpu cores in the hypervisor.", maxCpuCoreConfigKey, vmDescription, maxHostCpuCore));
|
||||
maxCpuCores = maxHostCpuCore;
|
||||
}
|
||||
logger.info("Config [{}] in cluster [ID: {}] has value less or equal '0'. Using [{}] host or last host max CPU cores [{}] as VM CPU cores in the hypervisor.",
|
||||
maxCpuCoresConfig.key(), clusterId, vmDescription, maxHostCpuCores);
|
||||
maxCpuCores = maxHostCpuCores;
|
||||
}
|
||||
return maxCpuCores;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import static com.cloud.configuration.ConfigurationManagerImpl.ADD_HOST_ON_SERVI
|
|||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
|
@ -32,11 +31,8 @@ import javax.naming.ConfigurationException;
|
|||
|
||||
import org.apache.cloudstack.agent.lb.IndirectAgentLB;
|
||||
import org.apache.cloudstack.ca.CAManager;
|
||||
import org.apache.cloudstack.ca.SetupCertificateCommand;
|
||||
import org.apache.cloudstack.direct.download.DirectDownloadManager;
|
||||
import org.apache.cloudstack.framework.ca.Certificate;
|
||||
import org.apache.cloudstack.utils.cache.LazyCache;
|
||||
import org.apache.cloudstack.utils.security.KeyStoreUtils;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.Listener;
|
||||
|
|
@ -66,7 +62,6 @@ import com.cloud.resource.DiscovererBase;
|
|||
import com.cloud.resource.ResourceStateAdapter;
|
||||
import com.cloud.resource.ServerResource;
|
||||
import com.cloud.resource.UnableDeleteHostException;
|
||||
import com.cloud.utils.PasswordGenerator;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.UuidUtils;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
|
@ -174,55 +169,7 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements
|
|||
throw new CloudRuntimeException("Cannot secure agent communication because SSH connection is invalid for host IP=" + agentIp);
|
||||
}
|
||||
|
||||
Integer validityPeriod = CAManager.CertValidityPeriod.value();
|
||||
if (validityPeriod < 1) {
|
||||
validityPeriod = 1;
|
||||
}
|
||||
|
||||
String keystorePassword = PasswordGenerator.generateRandomPassword(16);
|
||||
final SSHCmdHelper.SSHCmdResult keystoreSetupResult = SSHCmdHelper.sshExecuteCmdWithResult(sshConnection,
|
||||
String.format("sudo /usr/share/cloudstack-common/scripts/util/%s " +
|
||||
"/etc/cloudstack/agent/agent.properties " +
|
||||
"/etc/cloudstack/agent/%s " +
|
||||
"%s %d " +
|
||||
"/etc/cloudstack/agent/%s",
|
||||
KeyStoreUtils.KS_SETUP_SCRIPT,
|
||||
KeyStoreUtils.KS_FILENAME,
|
||||
keystorePassword,
|
||||
validityPeriod,
|
||||
KeyStoreUtils.CSR_FILENAME));
|
||||
|
||||
if (!keystoreSetupResult.isSuccess()) {
|
||||
throw new CloudRuntimeException("Failed to setup keystore on the KVM host: " + agentIp);
|
||||
}
|
||||
|
||||
final Certificate certificate = caManager.issueCertificate(keystoreSetupResult.getStdOut(), Arrays.asList(agentHostname, agentIp), Collections.singletonList(agentIp), null, null);
|
||||
if (certificate == null || certificate.getClientCertificate() == null) {
|
||||
throw new CloudRuntimeException("Failed to issue certificates for KVM host agent: " + agentIp);
|
||||
}
|
||||
|
||||
final SetupCertificateCommand certificateCommand = new SetupCertificateCommand(certificate);
|
||||
final SSHCmdHelper.SSHCmdResult setupCertResult = SSHCmdHelper.sshExecuteCmdWithResult(sshConnection,
|
||||
String.format("sudo /usr/share/cloudstack-common/scripts/util/%s " +
|
||||
"/etc/cloudstack/agent/agent.properties %s " +
|
||||
"/etc/cloudstack/agent/%s %s " +
|
||||
"/etc/cloudstack/agent/%s \"%s\" " +
|
||||
"/etc/cloudstack/agent/%s \"%s\" " +
|
||||
"/etc/cloudstack/agent/%s \"%s\"",
|
||||
KeyStoreUtils.KS_IMPORT_SCRIPT,
|
||||
keystorePassword,
|
||||
KeyStoreUtils.KS_FILENAME,
|
||||
KeyStoreUtils.SSH_MODE,
|
||||
KeyStoreUtils.CERT_FILENAME,
|
||||
certificateCommand.getEncodedCertificate(),
|
||||
KeyStoreUtils.CACERT_FILENAME,
|
||||
certificateCommand.getEncodedCaCertificates(),
|
||||
KeyStoreUtils.PKEY_FILENAME,
|
||||
certificateCommand.getEncodedPrivateKey()));
|
||||
|
||||
if (setupCertResult != null && !setupCertResult.isSuccess()) {
|
||||
throw new CloudRuntimeException("Failed to setup certificate in the KVM agent's keystore file, please see logs and configure manually!");
|
||||
}
|
||||
caManager.provisionCertificateViaSsh(sshConnection, agentIp, agentHostname, null);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Succeeded to import certificate in the keystore for agent on the KVM host: " + agentIp + ". Agent secured and trusted.");
|
||||
|
|
|
|||
|
|
@ -539,6 +539,9 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
AssignIpAddressSearch.and("allocated", AssignIpAddressSearch.entity().getAllocatedTime(), Op.NULL);
|
||||
AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.IN);
|
||||
AssignIpAddressSearch.and("forSystemVms", AssignIpAddressSearch.entity().isForSystemVms(), Op.EQ);
|
||||
AssignIpAddressSearch.and("id", AssignIpAddressSearch.entity().getId(), Op.NIN);
|
||||
AssignIpAddressSearch.and("requestedAddress", AssignIpAddressSearch.entity().getAddress(), Op.EQ);
|
||||
AssignIpAddressSearch.and("routerAddress", AssignIpAddressSearch.entity().getAddress(), Op.NEQ);
|
||||
|
||||
SearchBuilder<VlanVO> vlanSearch = _vlanDao.createSearchBuilder();
|
||||
vlanSearch.and("type", vlanSearch.entity().getVlanType(), Op.EQ);
|
||||
|
|
@ -945,10 +948,23 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
if (podId != null) {
|
||||
sc = AssignIpAddressFromPodVlanSearch.create();
|
||||
sc.setJoinParameters("podVlanMapSB", "podId", podId);
|
||||
errorMessage.append(" pod id=" + podId);
|
||||
errorMessage.append(" pod id=").append(podId);
|
||||
} else {
|
||||
sc = AssignIpAddressSearch.create();
|
||||
errorMessage.append(" zone id=" + dcId);
|
||||
errorMessage.append(" zone id=").append(dcId);
|
||||
}
|
||||
|
||||
if (lockOneRow) {
|
||||
logger.debug("Listing quarantined public IPs to ignore on search for public IP for system VM. The IPs ignored will be the ones that: were not associated to account [{}]; were not removed yet; and with quarantine end dates after [{}].", owner.getUuid(), new Date());
|
||||
|
||||
List<PublicIpQuarantineVO> quarantinedAddresses = publicIpQuarantineDao.listQuarantinedIpAddressesToUser(owner.getId(), new Date());
|
||||
List<Long> quarantinedAddressesIDs = quarantinedAddresses.stream().map(PublicIpQuarantineVO::getPublicIpAddressId).collect(Collectors.toList());
|
||||
|
||||
logger.debug("Found addresses with the following IDs: [{}] that will be ignored when searching for available public IPs.", quarantinedAddressesIDs);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(quarantinedAddressesIDs)) {
|
||||
sc.setParameters("id", quarantinedAddressesIDs.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
sc.setParameters("dc", dcId);
|
||||
|
|
@ -956,11 +972,11 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
// for direct network take ip addresses only from the vlans belonging to the network
|
||||
if (vlanUse == VlanType.DirectAttached) {
|
||||
sc.setJoinParameters("vlan", "networkId", guestNetworkId);
|
||||
errorMessage.append(", network id=" + guestNetworkId);
|
||||
errorMessage.append(", network id=").append(guestNetworkId);
|
||||
}
|
||||
if (requestedGateway != null) {
|
||||
sc.setJoinParameters("vlan", "vlanGateway", requestedGateway);
|
||||
errorMessage.append(", requested gateway=" + requestedGateway);
|
||||
errorMessage.append(", requested gateway=").append(requestedGateway);
|
||||
}
|
||||
sc.setJoinParameters("vlan", "type", vlanUse);
|
||||
|
||||
|
|
@ -970,38 +986,39 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
NetworkDetailVO routerIpDetail = _networkDetailsDao.findDetail(network.getId(), ApiConstants.ROUTER_IP);
|
||||
routerIpAddress = routerIpDetail != null ? routerIpDetail.getValue() : null;
|
||||
}
|
||||
|
||||
if (requestedIp != null) {
|
||||
sc.addAnd("address", SearchCriteria.Op.EQ, requestedIp);
|
||||
errorMessage.append(": requested ip " + requestedIp + " is not available");
|
||||
sc.setParameters("requestedAddress", requestedIp);
|
||||
errorMessage.append(": requested ip ").append(requestedIp).append(" is not available");
|
||||
} else if (routerIpAddress != null) {
|
||||
sc.addAnd("address", Op.NEQ, routerIpAddress);
|
||||
sc.setParameters("routerAddress", routerIpAddress);
|
||||
}
|
||||
|
||||
boolean ascOrder = ! forSystemVms;
|
||||
Filter filter = new Filter(IPAddressVO.class, "forSystemVms", ascOrder, 0l, 1l);
|
||||
Filter filter = new Filter(IPAddressVO.class, "forSystemVms", ascOrder, 0L, 1L);
|
||||
|
||||
filter.addOrderBy(IPAddressVO.class,"vlanId", true);
|
||||
|
||||
List<IPAddressVO> addrs = new ArrayList<>();
|
||||
List<IPAddressVO> addresses = new ArrayList<>();
|
||||
|
||||
if (forSystemVms) {
|
||||
// Get Public IPs for system vms in dedicated ranges
|
||||
sc.setParameters("forSystemVms", true);
|
||||
if (lockOneRow) {
|
||||
addrs = _ipAddressDao.lockRows(sc, filter, true);
|
||||
addresses = _ipAddressDao.lockRows(sc, filter, true);
|
||||
} else {
|
||||
addrs = new ArrayList<>(_ipAddressDao.search(sc, null));
|
||||
addresses = new ArrayList<>(_ipAddressDao.search(sc, null));
|
||||
}
|
||||
}
|
||||
if ((!lockOneRow || (lockOneRow && CollectionUtils.isEmpty(addrs))) &&
|
||||
if ((!lockOneRow || (lockOneRow && CollectionUtils.isEmpty(addresses))) &&
|
||||
!(forSystemVms && SystemVmPublicIpReservationModeStrictness.value())) {
|
||||
sc.setParameters("forSystemVms", false);
|
||||
// If owner has dedicated Public IP ranges, fetch IP from the dedicated range
|
||||
// Otherwise fetch IP from the system pool
|
||||
// Checking if network is null in the case of system VM's. At the time of allocation of IP address to systemVm, no network is present.
|
||||
if (network == null || !(network.getGuestType() == GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced)) {
|
||||
List<AccountVlanMapVO> maps = _accountVlanMapDao.listAccountVlanMapsByAccount(owner.getId());
|
||||
for (AccountVlanMapVO map : maps) {
|
||||
List<AccountVlanMapVO> accountVlanMaps = _accountVlanMapDao.listAccountVlanMapsByAccount(owner.getId());
|
||||
for (AccountVlanMapVO map : accountVlanMaps) {
|
||||
if (vlanDbIds == null || vlanDbIds.contains(map.getVlanDbId()))
|
||||
dedicatedVlanDbIds.add(map.getVlanDbId());
|
||||
}
|
||||
|
|
@ -1020,10 +1037,10 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
if (!dedicatedVlanDbIds.isEmpty()) {
|
||||
fetchFromDedicatedRange = true;
|
||||
sc.setParameters("vlanId", dedicatedVlanDbIds.toArray());
|
||||
errorMessage.append(", vlanId id=" + Arrays.toString(dedicatedVlanDbIds.toArray()));
|
||||
errorMessage.append(", vlanId id=").append(Arrays.toString(dedicatedVlanDbIds.toArray()));
|
||||
} else if (!nonDedicatedVlanDbIds.isEmpty()) {
|
||||
sc.setParameters("vlanId", nonDedicatedVlanDbIds.toArray());
|
||||
errorMessage.append(", vlanId id=" + Arrays.toString(nonDedicatedVlanDbIds.toArray()));
|
||||
errorMessage.append(", vlanId id=").append(Arrays.toString(nonDedicatedVlanDbIds.toArray()));
|
||||
} else {
|
||||
if (podId != null) {
|
||||
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", Pod.class, podId);
|
||||
|
|
@ -1037,13 +1054,13 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
}
|
||||
}
|
||||
if (lockOneRow) {
|
||||
addrs = _ipAddressDao.lockRows(sc, filter, true);
|
||||
addresses = _ipAddressDao.lockRows(sc, filter, true);
|
||||
} else {
|
||||
addrs = new ArrayList<>(_ipAddressDao.search(sc, null));
|
||||
addresses = new ArrayList<>(_ipAddressDao.search(sc, null));
|
||||
}
|
||||
|
||||
// If all the dedicated IPs of the owner are in use fetch an IP from the system pool
|
||||
if ((!lockOneRow || (lockOneRow && addrs.size() == 0)) && fetchFromDedicatedRange && vlanUse == VlanType.VirtualNetwork) {
|
||||
if ((!lockOneRow || (lockOneRow && addresses.isEmpty())) && fetchFromDedicatedRange && vlanUse == VlanType.VirtualNetwork) {
|
||||
// Verify if account is allowed to acquire IPs from the system
|
||||
boolean useSystemIps = UseSystemPublicIps.valueIn(owner.getId());
|
||||
if (useSystemIps && !nonDedicatedVlanDbIds.isEmpty()) {
|
||||
|
|
@ -1051,15 +1068,15 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
sc.setParameters("vlanId", nonDedicatedVlanDbIds.toArray());
|
||||
errorMessage.append(", vlanId id=" + Arrays.toString(nonDedicatedVlanDbIds.toArray()));
|
||||
if (lockOneRow) {
|
||||
addrs = _ipAddressDao.lockRows(sc, filter, true);
|
||||
addresses = _ipAddressDao.lockRows(sc, filter, true);
|
||||
} else {
|
||||
addrs.addAll(_ipAddressDao.search(sc, null));
|
||||
addresses.addAll(_ipAddressDao.search(sc, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lockOneRow && addrs.size() == 0) {
|
||||
if (lockOneRow && addresses.isEmpty()) {
|
||||
if (podId != null) {
|
||||
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", Pod.class, podId);
|
||||
// for now, we hardcode the table names, but we should ideally do a lookup for the tablename from the VO object.
|
||||
|
|
@ -1073,13 +1090,12 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
}
|
||||
|
||||
if (lockOneRow) {
|
||||
assert (addrs.size() == 1) : "Return size is incorrect: " + addrs.size();
|
||||
IpAddress ipAddress = addrs.get(0);
|
||||
boolean ipCanBeAllocated = canPublicIpAddressBeAllocated(ipAddress, owner);
|
||||
IPAddressVO allocatableIp = addresses.get(0);
|
||||
|
||||
if (!ipCanBeAllocated) {
|
||||
throw new InsufficientAddressCapacityException(String.format("Failed to allocate public IP address [%s] as it is in quarantine.", ipAddress.getAddress()),
|
||||
DataCenter.class, dcId);
|
||||
boolean isPublicIpAllocatable = canPublicIpAddressBeAllocated(allocatableIp, owner);
|
||||
|
||||
if (!isPublicIpAllocatable) {
|
||||
throw new InsufficientAddressCapacityException(String.format("Failed to allocate public IP [%s] as it is in quarantine.", allocatableIp.getAddress()), DataCenter.class, dcId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1088,12 +1104,12 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
try {
|
||||
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.public_ip);
|
||||
} catch (ResourceAllocationException ex) {
|
||||
logger.warn("Failed to allocate resource of type " + ex.getResourceType() + " for account " + owner);
|
||||
logger.warn("Failed to allocate resource of type {} for account {}", ex.getResourceType(), owner);
|
||||
throw new AccountLimitException("Maximum number of public IP addresses for account: " + owner.getAccountName() + " has been exceeded.");
|
||||
}
|
||||
}
|
||||
|
||||
return addrs;
|
||||
return addresses;
|
||||
}
|
||||
|
||||
@DB
|
||||
|
|
@ -2558,26 +2574,27 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
PublicIpQuarantineVO publicIpQuarantineVO = publicIpQuarantineDao.findByPublicIpAddressId(ip.getId());
|
||||
|
||||
if (publicIpQuarantineVO == null) {
|
||||
logger.debug(String.format("Public IP address [%s] is not in quarantine; therefore, it is allowed to be allocated.", ip));
|
||||
logger.debug("Public IP address [{}] is not in quarantine; therefore, it is allowed to be allocated.", ip);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isPublicIpAddressStillInQuarantine(publicIpQuarantineVO, new Date())) {
|
||||
logger.debug(String.format("Public IP address [%s] is no longer in quarantine; therefore, it is allowed to be allocated.", ip));
|
||||
logger.debug("Public IP address [{}] is no longer in quarantine; therefore, it is allowed to be allocated.", ip);
|
||||
removePublicIpAddressFromQuarantine(publicIpQuarantineVO.getId(), "IP was removed from quarantine because it was no longer in quarantine.");
|
||||
return true;
|
||||
}
|
||||
|
||||
Account previousOwner = _accountMgr.getAccount(publicIpQuarantineVO.getPreviousOwnerId());
|
||||
|
||||
if (Objects.equals(previousOwner.getUuid(), newOwner.getUuid())) {
|
||||
logger.debug(String.format("Public IP address [%s] is in quarantine; however, the Public IP previous owner [%s] is the same as the new owner [%s]; therefore the IP" +
|
||||
" can be allocated. The public IP address will be removed from quarantine.", ip, previousOwner, newOwner));
|
||||
logger.debug("Public IP address [{}] is in quarantine; however, the Public IP previous owner [{}] is the same as the new owner [{}]; therefore the IP" +
|
||||
" can be allocated. The public IP address will be removed from quarantine.", ip, previousOwner, newOwner);
|
||||
removePublicIpAddressFromQuarantine(publicIpQuarantineVO.getId(), "IP was removed from quarantine because it has been allocated by the previous owner");
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.error(String.format("Public IP address [%s] is in quarantine and the previous owner [%s] is different than the new owner [%s]; therefore, the IP cannot be " +
|
||||
"allocated.", ip, previousOwner, newOwner));
|
||||
logger.error("Public IP address [{}] is in quarantine and the previous owner [{}] is different than the new owner [{}]; therefore, the IP cannot be " +
|
||||
"allocated.", ip, previousOwner, newOwner);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -2628,7 +2645,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
|
|||
publicIpQuarantineVO.setRemovalReason(removalReason);
|
||||
publicIpQuarantineVO.setRemoverAccountId(removerAccountId);
|
||||
|
||||
logger.debug(String.format("Removing public IP Address [%s] from quarantine by updating the removed date to [%s].", ipAddress, removedDate));
|
||||
logger.debug("Removing public IP Address [{}] from quarantine by updating the removed date to [{}].", ipAddress, removedDate);
|
||||
publicIpQuarantineDao.persist(publicIpQuarantineVO);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1707,16 +1707,15 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
|
|||
|
||||
if (CollectionUtils.isEmpty(suitableHosts)) {
|
||||
logger.warn("No suitable hosts found.");
|
||||
} else {
|
||||
logger.debug("Hosts having capacity and are suitable for migration: {}", suitableHosts);
|
||||
return suitableHosts;
|
||||
}
|
||||
|
||||
logger.debug("Hosts having capacity and are suitable for migration: {}", suitableHosts);
|
||||
|
||||
// Only list hosts of the same architecture as the source Host in a multi-arch zone
|
||||
if (!suitableHosts.isEmpty()) {
|
||||
List<CPU.CPUArch> clusterArchs = ApiDBUtils.listZoneClustersArchs(vm.getDataCenterId());
|
||||
if (CollectionUtils.isNotEmpty(clusterArchs) && clusterArchs.size() > 1) {
|
||||
suitableHosts = suitableHosts.stream().filter(h -> h.getArch() == srcHost.getArch()).collect(Collectors.toList());
|
||||
}
|
||||
List<CPU.CPUArch> clusterArchs = ApiDBUtils.listZoneClustersArchs(vm.getDataCenterId());
|
||||
if (CollectionUtils.isNotEmpty(clusterArchs) && clusterArchs.size() > 1) {
|
||||
suitableHosts = suitableHosts.stream().filter(h -> h.getArch() == srcHost.getArch()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return suitableHosts;
|
||||
|
|
@ -3036,7 +3035,7 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
|
|||
final String hypervisor = cmd.getHypervisor();
|
||||
final String hypervisorVersion = cmd.getHypervisorVersion();
|
||||
|
||||
//throw exception if hypervisor name is not passed, but version is
|
||||
//throw exception if hypervisor name is not passed, but a version is
|
||||
if (hypervisorVersion != null && (hypervisor == null || hypervisor.isEmpty())) {
|
||||
throw new InvalidParameterValueException("Hypervisor version parameter cannot be used without specifying a hypervisor : XenServer, KVM or VMware");
|
||||
}
|
||||
|
|
@ -3054,7 +3053,7 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
|
|||
final SearchCriteria<GuestOSHypervisorVO> sc = sb.create();
|
||||
|
||||
if (id != null) {
|
||||
sc.setParameters("id", SearchCriteria.Op.EQ, id);
|
||||
sc.setParameters("id", id);
|
||||
}
|
||||
|
||||
if (osTypeId != null) {
|
||||
|
|
|
|||
|
|
@ -1392,20 +1392,19 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
|
||||
final String accountNameFinal = accountName;
|
||||
final Long domainIdFinal = domainId;
|
||||
final String accountUUIDFinal = accountUUID;
|
||||
final String resolvedAccountUUID = accountUUID != null ? accountUUID : UUID.randomUUID().toString();
|
||||
|
||||
// Check role escalation before the transaction — this is a read-only check
|
||||
// that iterates all API commands and doesn't need a write transaction open.
|
||||
AccountVO requestedAccount = new AccountVO(accountNameFinal, domainIdFinal, networkDomain, accountType, roleId, resolvedAccountUUID);
|
||||
checkRoleEscalation(getCurrentCallingAccount(), requestedAccount);
|
||||
|
||||
Pair<Long, Account> pair = Transaction.execute(new TransactionCallback<>() {
|
||||
@Override
|
||||
public Pair<Long, Account> doInTransaction(TransactionStatus status) {
|
||||
// create account
|
||||
String accountUUID = accountUUIDFinal;
|
||||
if (accountUUID == null) {
|
||||
accountUUID = UUID.randomUUID().toString();
|
||||
}
|
||||
AccountVO account = createAccount(accountNameFinal, accountType, roleId, domainIdFinal, networkDomain, details, accountUUID);
|
||||
AccountVO account = createAccount(accountNameFinal, accountType, roleId, domainIdFinal, networkDomain, details, resolvedAccountUUID);
|
||||
long accountId = account.getId();
|
||||
|
||||
checkRoleEscalation(getCurrentCallingAccount(), account);
|
||||
|
||||
// create the first user for the account
|
||||
UserVO user = createUser(accountId, userName, password, firstName, lastName, email, timezone, userUUID, source);
|
||||
|
||||
|
|
@ -3851,6 +3850,11 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
@Override
|
||||
public UserAccount getUserByApiKey(String apiKey) {
|
||||
ApiKeyPairVO keyPair = apiKeyPairDao.findByApiKey(apiKey);
|
||||
|
||||
if (keyPair == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return userAccountDao.findById(keyPair.getUserId());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,10 @@ public interface UserVmManager extends UserVmService {
|
|||
ConfigKey<Boolean> AllowDifferentHostTagsOfferingsForVmScale = new ConfigKey<>("Advanced", Boolean.class, "allow.different.host.tags.offerings.for.vm.scale", "false",
|
||||
"Enables/Disable allowing to change a VM offering to offerings with different host tags", true);
|
||||
|
||||
ConfigKey<Boolean> AutoMigrateVmOnLiveScaleInsufficientCapacity = new ConfigKey<>("Advanced", Boolean.class, "auto.migrate.vm.on.live.scale.insufficient.capacity",
|
||||
"true", "Defines whether a VM should be automatically migrated to a suitable host when the current host " +
|
||||
"lacks sufficient compute capacity to live scale the instance. Defaults to true.", true, ConfigKey.Scope.Cluster);
|
||||
|
||||
static final int MAX_USER_DATA_LENGTH_BYTES = 2048;
|
||||
|
||||
public static final String CKS_NODE = "cksnode";
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue