diff options
Diffstat (limited to '')
548 files changed, 31429 insertions, 17019 deletions
diff --git a/ansible_collections/hetzner/hcloud/.ansible-lint b/ansible_collections/hetzner/hcloud/.ansible-lint new file mode 100644 index 000000000..b8282450d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/.ansible-lint @@ -0,0 +1,27 @@ +--- +exclude_paths: + - .azure-pipelines + - .cache/ + - .git/ + - .github/ + - changelogs/ + - examples/ + - tests/integration/targets/certificate + - tests/integration/targets/floating_ip + - tests/integration/targets/load_balancer_network + - tests/integration/targets/load_balancer_service + - tests/integration/targets/load_balancer_target + - tests/integration/targets/network + - tests/integration/targets/placement_group + - tests/integration/targets/primary_ip + - tests/integration/targets/route + - tests/integration/targets/server + - tests/integration/targets/server_network + - tests/integration/targets/ssh_key + - tests/integration/targets/volume + +warn_list: + - internal-error + - sanity[cannot-ignore] + # TODO: remove once unsupported ansible-core version are dropped + - meta-runtime[unsupported-version] diff --git a/ansible_collections/hetzner/hcloud/.azure-pipelines/azure-pipelines.yml b/ansible_collections/hetzner/hcloud/.azure-pipelines/azure-pipelines.yml index d55524f27..8300381b4 100644 --- a/ansible_collections/hetzner/hcloud/.azure-pipelines/azure-pipelines.yml +++ b/ansible_collections/hetzner/hcloud/.azure-pipelines/azure-pipelines.yml @@ -1,22 +1,20 @@ trigger: batch: true branches: - include: - - main + include: [main, stable-1] pr: autoCancel: true + drafts: false branches: - include: - - main + include: [main, stable-1] schedules: - cron: 0 9 * * * displayName: Nightly always: true branches: - include: - - main + include: [main, stable-1] variables: - name: checkoutPath @@ -26,20 +24,20 @@ variables: - name: pipelinesCoverage value: coverage - name: entryPoint - value: tests/utils/shippable/shippable.sh + value: tests/utils/ci.sh - name: fetchDepth value: 0 resources: containers: - container: default - image: quay.io/ansible/azure-pipelines-test-container:3.0.0 + image: quay.io/ansible/azure-pipelines-test-container:4.0.1 pool: Standard stages: -### Sanity - - stage: Ansible_devel + ### Sanity + - stage: Sanity_devel displayName: Sanity devel dependsOn: [] jobs: @@ -47,9 +45,29 @@ stages: parameters: targets: - name: Sanity - test: 'devel/sanity/1' + test: devel/sanity - - stage: Ansible_2_14 + - stage: Sanity_2_16 + displayName: Sanity 2.16 + dependsOn: [] + jobs: + - template: templates/matrix.yml + parameters: + targets: + - name: Sanity + test: 2.16/sanity + + - stage: Sanity_2_15 + displayName: Sanity 2.15 + dependsOn: [] + jobs: + - template: templates/matrix.yml + parameters: + targets: + - name: Sanity + test: 2.15/sanity + + - stage: Sanity_2_14 displayName: Sanity 2.14 dependsOn: [] jobs: @@ -57,8 +75,9 @@ stages: parameters: targets: - name: Sanity - test: '2.14/sanity/1' - - stage: Ansible_2_13 + test: 2.14/sanity + + - stage: Sanity_2_13 displayName: Sanity 2.13 dependsOn: [] jobs: @@ -66,82 +85,133 @@ stages: parameters: targets: - name: Sanity - test: '2.13/sanity/1' - - stage: Ansible_2_12 - displayName: Sanity 2.12 + test: 2.13/sanity + + ### Units + - stage: Units_devel + displayName: Units devel dependsOn: [] jobs: - template: templates/matrix.yml parameters: targets: - - name: Sanity - test: '2.12/sanity/1' + - name: (py3.12) + test: devel/units/3.12 + + - stage: Units_2_16 + displayName: Units 2.16 + dependsOn: [] + jobs: + - template: templates/matrix.yml + parameters: + targets: + - name: (py3.10) + test: 2.16/units/3.10 + - stage: Units_2_15 + displayName: Units 2.15 + dependsOn: [] + jobs: + - template: templates/matrix.yml + parameters: + targets: + - name: (py3.9) + test: 2.15/units/3.9 + + - stage: Units_2_14 + displayName: Units 2.14 + dependsOn: [] + jobs: + - template: templates/matrix.yml + parameters: + targets: + - name: (py3.9) + test: 2.14/units/3.9 + + - stage: Units_2_13 + displayName: Units 2.13 + dependsOn: [] + jobs: + - template: templates/matrix.yml + parameters: + targets: + - name: (py3.8) + test: 2.13/units/3.8 + + ## Integration + - stage: Integration_devel + displayName: Integration devel + dependsOn: [] + jobs: + - template: templates/matrix.yml + parameters: + groups: [1, 2, 3] + targets: + - name: (py3.12) + test: devel/integration/3.12 -## Integration tests (remote) - - stage: Hetzner_devel - displayName: Hetzner devel + - stage: Integration_2_16 + displayName: Integration 2.16 dependsOn: [] jobs: - template: templates/matrix.yml parameters: - groups: - - 1 - - 2 + groups: [1, 2, 3] targets: - - name: hcloud - test: 'devel/hcloud/3.9' + - name: (py3.10) + test: 2.16/integration/3.10 - - stage: Hetzner_2_14 - displayName: Hetzner 2.14 + - stage: Integration_2_15 + displayName: Integration 2.15 dependsOn: [] jobs: - template: templates/matrix.yml parameters: - groups: - - 1 - - 2 + groups: [1, 2, 3] targets: - - name: hcloud - test: '2.14/hcloud/3.9' + - name: (py3.9) + test: 2.15/integration/3.9 - - stage: Hetzner_2_13 - displayName: Hetzner 2.13 + - stage: Integration_2_14 + displayName: Integration 2.14 dependsOn: [] jobs: - template: templates/matrix.yml parameters: - groups: - - 1 - - 2 + groups: [1, 2, 3] targets: - - name: hcloud - test: '2.13/hcloud/3.9' + - name: (py3.9) + test: 2.14/integration/3.9 - - stage: Hetzner_2_12 - displayName: Hetzner 2.12 + - stage: Integration_2_13 + displayName: Integration 2.13 dependsOn: [] jobs: - template: templates/matrix.yml parameters: - groups: - - 1 - - 2 + groups: [1, 2, 3] targets: - - name: hcloud - test: '2.12/hcloud/3.9' + - name: (py3.8) + test: 2.13/integration/3.8 -### Finally + ### Finally - stage: Summary condition: succeededOrFailed() dependsOn: - - Ansible_devel - - Ansible_2_14 - - Ansible_2_13 - - Ansible_2_12 - - Hetzner_devel - - Hetzner_2_14 - - Hetzner_2_13 - - Hetzner_2_12 + - Sanity_devel + - Sanity_2_16 + - Sanity_2_15 + - Sanity_2_14 + - Sanity_2_13 + - Units_devel + - Units_2_16 + - Units_2_15 + - Units_2_14 + - Units_2_13 + - Integration_devel + - Integration_2_16 + - Integration_2_15 + - Integration_2_14 + - Integration_2_13 jobs: - template: templates/coverage.yml diff --git a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/aggregate-coverage.sh b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/aggregate-coverage.sh index f3113dd0a..c196ab014 100755 --- a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/aggregate-coverage.sh +++ b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/aggregate-coverage.sh @@ -13,8 +13,8 @@ options=(--venv --venv-system-site-packages --color -v) ansible-test coverage combine --export "${agent_temp_directory}/coverage/" "${options[@]}" -if ansible-test coverage analyze targets generate --help >/dev/null 2>&1; then - # Only analyze coverage if the installed version of ansible-test supports it. - # Doing so allows this script to work unmodified for multiple Ansible versions. - ansible-test coverage analyze targets generate "${agent_temp_directory}/coverage/coverage-analyze-targets.json" "${options[@]}" +if ansible-test coverage analyze targets generate --help > /dev/null 2>&1; then + # Only analyze coverage if the installed version of ansible-test supports it. + # Doing so allows this script to work unmodified for multiple Ansible versions. + ansible-test coverage analyze targets generate "${agent_temp_directory}/coverage/coverage-analyze-targets.json" "${options[@]}" fi diff --git a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/combine-coverage.py b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/combine-coverage.py index 506ade646..bab3c4226 100755 --- a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/combine-coverage.py +++ b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/combine-coverage.py @@ -7,8 +7,7 @@ Keep in mind that Azure Pipelines does not enforce unique job display names (onl It is up to pipeline authors to avoid name collisions when deviating from the recommended format. """ -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type +from __future__ import annotations import os import re @@ -20,12 +19,12 @@ def main(): """Main program entry point.""" source_directory = sys.argv[1] - if '/ansible_collections/' in os.getcwd(): + if "/ansible_collections/" in os.getcwd(): output_path = "tests/output" else: output_path = "test/results" - destination_directory = os.path.join(output_path, 'coverage') + destination_directory = os.path.join(output_path, "coverage") if not os.path.exists(destination_directory): os.makedirs(destination_directory) @@ -34,27 +33,27 @@ def main(): count = 0 for name in os.listdir(source_directory): - match = re.search('^Coverage (?P<attempt>[0-9]+) (?P<label>.+)$', name) - label = match.group('label') - attempt = int(match.group('attempt')) + match = re.search("^Coverage (?P<attempt>[0-9]+) (?P<label>.+)$", name) + label = match.group("label") + attempt = int(match.group("attempt")) jobs[label] = max(attempt, jobs.get(label, 0)) for label, attempt in jobs.items(): - name = 'Coverage {attempt} {label}'.format(label=label, attempt=attempt) + name = f"Coverage {attempt} {label}" source = os.path.join(source_directory, name) source_files = os.listdir(source) for source_file in source_files: source_path = os.path.join(source, source_file) - destination_path = os.path.join(destination_directory, source_file + '.' + label) - print('"%s" -> "%s"' % (source_path, destination_path)) + destination_path = os.path.join(destination_directory, source_file + "." + label) + print(f'"{source_path}" -> "{destination_path}"') shutil.copyfile(source_path, destination_path) count += 1 - print('Coverage file count: %d' % count) - print('##vso[task.setVariable variable=coverageFileCount]%d' % count) - print('##vso[task.setVariable variable=outputPath]%s' % output_path) + print("Coverage file count: %d" % count) + print("##vso[task.setVariable variable=coverageFileCount]%d" % count) + print("##vso[task.setVariable variable=outputPath]%s" % output_path) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/publish-codecov.sh b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/publish-codecov.sh index 6d184f0b8..5dd7cfde7 100755 --- a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/publish-codecov.sh +++ b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/publish-codecov.sh @@ -10,18 +10,18 @@ output_path="$1" curl --silent --show-error https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh > codecov.sh for file in "${output_path}"/reports/coverage*.xml; do - name="${file}" - name="${name##*/}" # remove path - name="${name##coverage=}" # remove 'coverage=' prefix if present - name="${name%.xml}" # remove '.xml' suffix + name="${file}" + name="${name##*/}" # remove path + name="${name##coverage=}" # remove 'coverage=' prefix if present + name="${name%.xml}" # remove '.xml' suffix - bash codecov.sh \ - -f "${file}" \ - -n "${name}" \ - -X coveragepy \ - -X gcov \ - -X fix \ - -X search \ - -X xcode \ - || echo "Failed to upload code coverage report to codecov.io: ${file}" + bash codecov.sh \ + -f "${file}" \ + -n "${name}" \ + -X coveragepy \ + -X gcov \ + -X fix \ + -X search \ + -X xcode || + echo "Failed to upload code coverage report to codecov.io: ${file}" done diff --git a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/report-coverage.sh b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/report-coverage.sh index 1bd91bdc9..a397f63e7 100755 --- a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/report-coverage.sh +++ b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/report-coverage.sh @@ -5,11 +5,11 @@ set -o pipefail -eu PATH="${PWD}/bin:${PATH}" -if ! ansible-test --help >/dev/null 2>&1; then - # Install the devel version of ansible-test for generating code coverage reports. - # This is only used by Ansible Collections, which are typically tested against multiple Ansible versions (in separate jobs). - # Since a version of ansible-test is required that can work the output from multiple older releases, the devel version is used. - pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check +if ! ansible-test --help > /dev/null 2>&1; then + # Install the devel version of ansible-test for generating code coverage reports. + # This is only used by Ansible Collections, which are typically tested against multiple Ansible versions (in separate jobs). + # Since a version of ansible-test is required that can work the output from multiple older releases, the devel version is used. + pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check fi ansible-test coverage xml --stub --venv --venv-system-site-packages --color -v diff --git a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/run-tests.sh b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/run-tests.sh index a947fdf01..25a576cba 100755 --- a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/run-tests.sh +++ b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/run-tests.sh @@ -4,7 +4,7 @@ set -o pipefail -eu entry_point="$1" -test="$2" +entry_point_args="$2" read -r -a coverage_branches <<< "$3" # space separated list of branches to run code coverage on for scheduled builds export COMMIT_MESSAGE @@ -13,22 +13,22 @@ export COVERAGE export IS_PULL_REQUEST if [ "${SYSTEM_PULLREQUEST_TARGETBRANCH:-}" ]; then - IS_PULL_REQUEST=true - COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD^2) + IS_PULL_REQUEST=true + COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD^2) else - IS_PULL_REQUEST= - COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD) + IS_PULL_REQUEST=false + COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD) fi -COMPLETE= -COVERAGE= +COMPLETE=false +COVERAGE=false if [ "${BUILD_REASON}" = "Schedule" ]; then - COMPLETE=yes + COMPLETE=true - if printf '%s\n' "${coverage_branches[@]}" | grep -q "^${BUILD_SOURCEBRANCHNAME}$"; then - COVERAGE=yes - fi + if printf '%s\n' "${coverage_branches[@]}" | grep -q "^${BUILD_SOURCEBRANCHNAME}$"; then + COVERAGE=true + fi fi -"${entry_point}" "${test}" 2>&1 | "$(dirname "$0")/time-command.py" +"${entry_point}" "${entry_point_args}" 2>&1 | "$(dirname "$0")/time-command.py" diff --git a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/time-command.py b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/time-command.py index 5e8eb8d4c..783e745d8 100755 --- a/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/time-command.py +++ b/ansible_collections/hetzner/hcloud/.azure-pipelines/scripts/time-command.py @@ -1,8 +1,7 @@ #!/usr/bin/env python """Prepends a relative timestamp to each input line from stdin and writes it to stdout.""" -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type +from __future__ import annotations import sys import time @@ -12,14 +11,14 @@ def main(): """Main program entry point.""" start = time.time() - sys.stdin.reconfigure(errors='surrogateescape') - sys.stdout.reconfigure(errors='surrogateescape') + sys.stdin.reconfigure(errors="surrogateescape") + sys.stdout.reconfigure(errors="surrogateescape") for line in sys.stdin: seconds = time.time() - start - sys.stdout.write('%02d:%02d %s' % (seconds // 60, seconds % 60, line)) + sys.stdout.write("%02d:%02d %s" % (seconds // 60, seconds % 60, line)) sys.stdout.flush() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/hetzner/hcloud/.azure-pipelines/templates/matrix.yml b/ansible_collections/hetzner/hcloud/.azure-pipelines/templates/matrix.yml index 4e9555dd3..d0a659f71 100644 --- a/ansible_collections/hetzner/hcloud/.azure-pipelines/templates/matrix.yml +++ b/ansible_collections/hetzner/hcloud/.azure-pipelines/templates/matrix.yml @@ -45,11 +45,11 @@ jobs: parameters: jobs: - ${{ if eq(length(parameters.groups), 0) }}: - - ${{ each target in parameters.targets }}: - - name: ${{ format(parameters.nameFormat, coalesce(target.name, target.test)) }} - test: ${{ format(parameters.testFormat, coalesce(target.test, target.name)) }} - - ${{ if not(eq(length(parameters.groups), 0)) }}: - - ${{ each group in parameters.groups }}: - ${{ each target in parameters.targets }}: - - name: ${{ format(format(parameters.nameGroupFormat, parameters.nameFormat), coalesce(target.name, target.test), group) }} - test: ${{ format(format(parameters.testGroupFormat, parameters.testFormat), coalesce(target.test, target.name), group) }} + - name: ${{ format(parameters.nameFormat, coalesce(target.name, target.test)) }} + test: ${{ format(parameters.testFormat, coalesce(target.test, target.name)) }} + - ${{ if not(eq(length(parameters.groups), 0)) }}: + - ${{ each group in parameters.groups }}: + - ${{ each target in parameters.targets }}: + - name: ${{ format(format(parameters.nameGroupFormat, parameters.nameFormat), coalesce(target.name, target.test), group) }} + test: ${{ format(format(parameters.testGroupFormat, parameters.testFormat), coalesce(target.test, target.name), group) }} diff --git a/ansible_collections/hetzner/hcloud/.azure-pipelines/templates/test.yml b/ansible_collections/hetzner/hcloud/.azure-pipelines/templates/test.yml index 5250ed802..a4ea5a78d 100644 --- a/ansible_collections/hetzner/hcloud/.azure-pipelines/templates/test.yml +++ b/ansible_collections/hetzner/hcloud/.azure-pipelines/templates/test.yml @@ -9,37 +9,37 @@ parameters: jobs: - ${{ each job in parameters.jobs }}: - - job: test_${{ replace(replace(replace(job.test, '/', '_'), '.', '_'), '-', '_') }} - displayName: ${{ job.name }} - container: default - workspace: - clean: all - steps: - - checkout: self - fetchDepth: $(fetchDepth) - path: $(checkoutPath) - - bash: .azure-pipelines/scripts/run-tests.sh "$(entryPoint)" "${{ job.test }}" "$(coverageBranches)" - displayName: Run Tests - - bash: .azure-pipelines/scripts/process-results.sh - condition: succeededOrFailed() - displayName: Process Results - - bash: .azure-pipelines/scripts/aggregate-coverage.sh "$(Agent.TempDirectory)" - condition: eq(variables.haveCoverageData, 'true') - displayName: Aggregate Coverage Data - - task: PublishTestResults@2 - condition: eq(variables.haveTestResults, 'true') - inputs: - testResultsFiles: "$(outputPath)/junit/*.xml" - displayName: Publish Test Results - - task: PublishPipelineArtifact@1 - condition: eq(variables.haveBotResults, 'true') - displayName: Publish Bot Results - inputs: - targetPath: "$(outputPath)/bot/" - artifactName: "Bot $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)" - - task: PublishPipelineArtifact@1 - condition: eq(variables.haveCoverageData, 'true') - displayName: Publish Coverage Data - inputs: - targetPath: "$(Agent.TempDirectory)/coverage/" - artifactName: "Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)" + - job: test_${{ replace(replace(replace(job.test, '/', '_'), '.', '_'), '-', '_') }} + displayName: ${{ job.name }} + container: default + workspace: + clean: all + steps: + - checkout: self + fetchDepth: $(fetchDepth) + path: $(checkoutPath) + - bash: .azure-pipelines/scripts/run-tests.sh "$(entryPoint)" "${{ job.test }}" "$(coverageBranches)" + displayName: Run Tests + - bash: .azure-pipelines/scripts/process-results.sh + condition: succeededOrFailed() + displayName: Process Results + - bash: .azure-pipelines/scripts/aggregate-coverage.sh "$(Agent.TempDirectory)" + condition: eq(variables.haveCoverageData, 'true') + displayName: Aggregate Coverage Data + - task: PublishTestResults@2 + condition: eq(variables.haveTestResults, 'true') + inputs: + testResultsFiles: "$(outputPath)/junit/*.xml" + displayName: Publish Test Results + - task: PublishPipelineArtifact@1 + condition: eq(variables.haveBotResults, 'true') + displayName: Publish Bot Results + inputs: + targetPath: "$(outputPath)/bot/" + artifactName: "Bot $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)" + - task: PublishPipelineArtifact@1 + condition: eq(variables.haveCoverageData, 'true') + displayName: Publish Coverage Data + inputs: + targetPath: "$(Agent.TempDirectory)/coverage/" + artifactName: "Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)" diff --git a/ansible_collections/hetzner/hcloud/.flake8 b/ansible_collections/hetzner/hcloud/.flake8 new file mode 100644 index 000000000..9e9cd9200 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/.flake8 @@ -0,0 +1,3 @@ +[flake8] +extend-ignore = + E501 diff --git a/ansible_collections/hetzner/hcloud/.github/release-please-config.json b/ansible_collections/hetzner/hcloud/.github/release-please-config.json new file mode 100644 index 000000000..dc066f009 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/.github/release-please-config.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "bootstrap-sha": "359539fdd65b3a8849f7ea783cddc700c399c78c", + "include-component-in-tag": false, + "include-v-in-tag": false, + "draft-pull-request": true, + "packages": { + ".": { + "release-type": "simple", + "package-name": "hetzner.hcloud", + "changelog-path": "changelogs/dev-changelog.md", + "extra-files": ["galaxy.yml", "plugins/module_utils/version.py"] + } + } +} diff --git a/ansible_collections/hetzner/hcloud/.github/release-please-manifest.json b/ansible_collections/hetzner/hcloud/.github/release-please-manifest.json new file mode 100644 index 000000000..b2b35cf9b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/.github/release-please-manifest.json @@ -0,0 +1 @@ +{".":"2.5.0"} diff --git a/ansible_collections/hetzner/hcloud/.github/workflows/check-changelog-fragments.yml b/ansible_collections/hetzner/hcloud/.github/workflows/check-changelog-fragments.yml deleted file mode 100644 index 037ff7ec0..000000000 --- a/ansible_collections/hetzner/hcloud/.github/workflows/check-changelog-fragments.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: antsibull-changelog tests - -on: [push, pull_request] - -jobs: - build: - runs-on: ubuntu-latest - name: antsibull-changelog-lint - steps: - - uses: actions/checkout@v2 - - name: Setup python - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install antsibull-changelog - - name: test with lint - run: antsibull-changelog lint diff --git a/ansible_collections/hetzner/hcloud/.github/workflows/lint.yml b/ansible_collections/hetzner/hcloud/.github/workflows/lint.yml new file mode 100644 index 000000000..2205c461e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/.github/workflows/lint.yml @@ -0,0 +1,25 @@ +name: Lint + +on: + push: + branches: [main, stable-1] + pull_request: + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: pip + cache-dependency-path: requirements.txt + + - name: Install dependencies + run: make venv + + - name: Lint docs + run: make lint-docs diff --git a/ansible_collections/hetzner/hcloud/.github/workflows/release-please.yml b/ansible_collections/hetzner/hcloud/.github/workflows/release-please.yml new file mode 100644 index 000000000..a5b035c14 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/.github/workflows/release-please.yml @@ -0,0 +1,74 @@ +name: Release-please + +on: + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + release-please: + # Do not run on forks. + if: github.repository == 'ansible-collections/hetzner.hcloud' + + runs-on: ubuntu-latest + steps: + - id: release + uses: google-github-actions/release-please-action@v4 + with: + token: ${{ secrets.HCLOUD_BOT_TOKEN }} + config-file: .github/release-please-config.json + manifest-file: .github/release-please-manifest.json + + - name: Prepare outputs + id: outputs + if: steps.release.outputs.pr != '' + run: | + echo "pr-updated=true" >> "$GITHUB_OUTPUT" + echo "branch=${{ fromJSON(steps.release.outputs.pr).headBranchName }}" >> "$GITHUB_OUTPUT" + echo "version=$(echo "${{ fromJSON(steps.release.outputs.pr).title }}" | awk '{print $3}')" >> "$GITHUB_OUTPUT" + + outputs: + pr-updated: ${{ steps.outputs.outputs.pr-updated }} + branch: ${{ steps.outputs.outputs.branch }} + version: ${{ steps.outputs.outputs.version }} + + antsibull-changelog: + needs: [release-please] + if: needs.release-please.outputs.pr-updated + + runs-on: ubuntu-latest + steps: + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install dependencies + run: pip install antsibull-changelog + + - uses: actions/checkout@v4 + with: + ref: ${{ needs.release-please.outputs.branch }} + + - name: antsibull-changelog + run: antsibull-changelog release --version "${{ needs.release-please.outputs.version }}" + + - name: Check for diff + id: antsibull-diff + run: | + if [[ $(git status --porcelain) ]]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Commit & Push + if: ${{ steps.antsibull-diff.outputs.changed }} + run: | + git config user.name "Hetzner Cloud Bot" + git config user.email "45457231+hcloud-bot@users.noreply.github.com" + + git add changelogs/ CHANGELOG.rst + git commit -m "chore(main): changelog for version ${{ needs.release-please.outputs.version }}" + + git push --force origin ${{ needs.release-please.outputs.branch }} diff --git a/ansible_collections/hetzner/hcloud/.github/workflows/stale.yml b/ansible_collections/hetzner/hcloud/.github/workflows/stale.yml new file mode 100644 index 000000000..d9410ae91 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/.github/workflows/stale.yml @@ -0,0 +1,13 @@ +name: Close stale issues + +on: + schedule: + - cron: "30 12 * * *" + +jobs: + stale: + permissions: + issues: write + pull-requests: write + + uses: hetznercloud/.github/.github/workflows/stale.yml@main diff --git a/ansible_collections/hetzner/hcloud/.github/workflows/vendor.yml b/ansible_collections/hetzner/hcloud/.github/workflows/vendor.yml new file mode 100644 index 000000000..7fa92f4ef --- /dev/null +++ b/ansible_collections/hetzner/hcloud/.github/workflows/vendor.yml @@ -0,0 +1,26 @@ +name: Vendor + +on: + push: + branches: [main, stable-1] + paths: + - plugins/module_utils/vendor/** + - scripts/vendor.py + pull_request: + paths: + - plugins/module_utils/vendor/** + - scripts/vendor.py + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Ensure vendors are not diverging + run: make vendor-check diff --git a/ansible_collections/hetzner/hcloud/.gitignore b/ansible_collections/hetzner/hcloud/.gitignore index d6a37e5c5..cdfb360ad 100644 --- a/ansible_collections/hetzner/hcloud/.gitignore +++ b/ansible_collections/hetzner/hcloud/.gitignore @@ -1,3 +1,5 @@ +.venv +venv # Created by https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv # Edit at https://www.gitignore.io/?templates=git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv @@ -55,7 +57,6 @@ flycheck_*.el # network security /network-security.data - ### Git ### # Created by git for backups. To disable backups in Git: # $ git config --global mergetool.keepBackup false @@ -387,3 +388,4 @@ $RECYCLE.BIN/ # End of https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv cloud-config-hcloud.ini +tests/integration/inventory diff --git a/ansible_collections/hetzner/hcloud/.gitlab-ci.yml b/ansible_collections/hetzner/hcloud/.gitlab-ci.yml index 7748f7237..3387f825b 100644 --- a/ansible_collections/hetzner/hcloud/.gitlab-ci.yml +++ b/ansible_collections/hetzner/hcloud/.gitlab-ci.yml @@ -1,13 +1,23 @@ +include: + - project: cloud/integrations/ci + file: + - default.yml + - pre-commit.yml + stages: + - test - sanity - integration variables: - PYTHON_VERSION: "3.9" + PYTHON_VERSION: "3.10" default: image: python:$PYTHON_VERSION +pre-commit: + extends: [.pre-commit] + sanity: stage: sanity allow_failure: true @@ -15,7 +25,7 @@ sanity: - tags parallel: matrix: - - ANSIBLE_VERSION: ["devel", "2.12", "2.13", "2.14"] + - ANSIBLE_VERSION: ["devel", "2.13", "2.14", "2.15"] GROUP: [1] script: - bash tests/utils/gitlab/gitlab.sh ${ANSIBLE_VERSION}/sanity/${GROUP} diff --git a/ansible_collections/hetzner/hcloud/.pre-commit-config.yaml b/ansible_collections/hetzner/hcloud/.pre-commit-config.yaml new file mode 100644 index 000000000..d1d1c6a4d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/.pre-commit-config.yaml @@ -0,0 +1,85 @@ +--- +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +exclude: ^plugins/module_utils/vendor/hcloud/.*$ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-symlinks + - id: destroyed-symlinks + + - id: check-json + - id: check-yaml + - id: check-toml + + - id: check-merge-conflict + - id: end-of-file-fixer + - id: mixed-line-ending + - id: trailing-whitespace + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.1.0 + hooks: + - id: prettier + files: \.(md|ya?ml)$ + exclude: ^changelogs/(dev-changelog\.md|changelog\.yaml)$ + + - repo: https://github.com/asottile/pyupgrade + rev: v3.15.0 + hooks: + - id: pyupgrade + args: [--py38-plus] + + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 24.1.1 + hooks: + - id: black + + - repo: https://github.com/ansible-community/antsibull-changelog + rev: 0.23.0 + hooks: + - id: antsibull-changelog-lint + - id: antsibull-changelog-lint-changelog-yaml + + - repo: https://github.com/ansible/ansible-lint + rev: v6.22.2 + hooks: + - id: ansible-lint + name: ansible-lint + args: [--offline] + additional_dependencies: + - ansible-core>=2.13.3 + - netaddr + + - repo: local + hooks: + - id: shfmt + name: shfmt + description: Format shell scripts with shfmt + language: golang + additional_dependencies: [mvdan.cc/sh/v3/cmd/shfmt@v3.7.0] + entry: shfmt -i 2 -ci -sr -kp -w + types: [shell] + + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.9.0.6 + hooks: + - id: shellcheck + + - repo: local + hooks: + - id: check-integration-test-files + name: check integration test files + description: Ensure the integration test files are in sync + language: system + entry: scripts/integration-test-files.sh + pass_filenames: false + files: ^(scripts/integration-test-files.sh$|tests/integration) diff --git a/ansible_collections/hetzner/hcloud/CHANGELOG.rst b/ansible_collections/hetzner/hcloud/CHANGELOG.rst index e9eff3cad..2a40778f4 100644 --- a/ansible_collections/hetzner/hcloud/CHANGELOG.rst +++ b/ansible_collections/hetzner/hcloud/CHANGELOG.rst @@ -5,6 +5,208 @@ Hetzner Cloud Ansible Collection Release Notes .. contents:: Topics +v2.5.0 +====== + +Minor Changes +------------- + +- Replace deprecated `ansible.netcommon` ip utils with python `ipaddress` module. The `ansible.netcommon` collection is no longer required by the collections. +- firewall - Allow forcing the deletion of firewalls that are still in use. +- firewall - Do not silence 'firewall still in use' delete failures. +- firewall - Return resources the firewall is `applied_to`. +- firewall_info - Add new `firewall_info` module to gather firewalls info. +- firewall_resource - Add new `firewall_resource` module to manage firewalls resources. +- inventory - Add `hostvars_prefix` and hostvars_suffix` options to customize the inventory host variables keys. + +New Modules +----------- + +- firewall_resource - Manage Resources a Hetzner Cloud Firewall is applied to. + +v2.4.1 +====== + +Bugfixes +-------- + +- hcloud inventory - Ensure the API client use a new cache for every *cached session*. + +v2.4.0 +====== + +Minor Changes +------------- + +- Add the `hetzner.hcloud.all` group to configure all the modules using `module_defaults`. +- Allow to set the `api_endpoint` module argument using the `HCLOUD_ENDPOINT` environment variable. +- Removed the `hcloud_` prefix from all modules names, e.g. `hetzner.hcloud.hcloud_firewall` was renamed to `hetzner.hcloud.firewall`. Old module names will continue working. +- Renamed the `endpoint` module argument to `api_endpoint`, backward compatibility is maintained using an alias. +- hcloud inventory - Add the `api_endpoint` option. +- hcloud inventory - Deprecate the `api_token_env` option, suggest using a lookup plugin (`{{ lookup('ansible.builtin.env', 'YOUR_ENV_VAR') }}`) or use the well-known `HCLOUD_TOKEN` environment variable name. +- hcloud inventory - Rename the `token_env` option to `api_token_env`, use aliases for backward compatibility. +- hcloud inventory - Rename the `token` option to `api_token`, use aliases for backward compatibility. + +v2.3.0 +====== + +Minor Changes +------------- + +- hcloud_datacenter_info - Add `server_types` field +- hcloud_server - Add `created` field +- hcloud_server_info - Add `created` field + +v2.2.0 +====== + +Minor Changes +------------- + +- hcloud_iso_info - Add deprecation field +- hcloud_load_balancer_network - Allow selecting a `load_balancer` or `network` using its ID. +- hcloud_load_balancer_service - Allow selecting a `load_balancer` using its ID. +- hcloud_load_balancer_target - Allow selecting a `load_balancer` or `server` using its ID. +- hcloud_rdns - Allow selecting a `server`, `floating_ip`, `primary_ip` or `load_balancer` using its ID. +- hcloud_route - Allow selecting a `network` using its ID. +- hcloud_server_network - Allow selecting a `network` or `server` using its ID. +- hcloud_subnetwork - Allow selecting to a `network` using its ID. + +v2.1.2 +====== + +Bugfixes +-------- + +- hcloud_firewall - The port argument is required when the firewall rule protocol is `udp` or `tcp`. +- hcloud_load_balancer_service - In the returned data, the invalid `health_check.http.certificates` field was renamed to `health_check.http.status_codes`. + +v2.1.1 +====== + +Bugfixes +-------- + +- hcloud_server - Fix string formatting error on deprecated server type warning + +v2.1.0 +====== + +Minor Changes +------------- + +- Use the collection version in the hcloud user-agent instead of the ansible-core version. +- hcloud_floating_ip_info - Allow querying floating ip by name. +- hcloud_load_balancer_info - Add targets health status field. +- inventory - Allow caching the hcloud inventory. + +Bugfixes +-------- + +- `*_info` - Consistently fail on invalid ID in `*_info` modules. + +v2.0.0 +====== + +Release Summary +--------------- + +This release bundles the hcloud dependency in the collection, this allows us to ship new features or bug fixes without having to release new major versions and require the users to upgrade their version of the hcloud dependency. + +Minor Changes +------------- + +- Bundle hcloud python dependency inside the collection. +- python-dateutil >= 2.7.5 is now required by the collection. If you already have the hcloud package installed, this dependency should also be installed. +- requests >= 2.20 is now required by the collection. If you already have the hcloud package installed, this dependency should also be installed. + +Breaking Changes / Porting Guide +-------------------------------- + +- Drop support for ansible-core 2.12 +- Drop support for python 3.7 +- inventory plugin - Don't set the server image variables (`image_id`, `image_os_flavor` and `image_name`) when the server image is not defined. + +Removed Features (previously deprecated) +---------------------------------------- + +- hcloud_datacenter_facts Removed deprecated facts module +- hcloud_floating_ip_facts Removed deprecated facts module +- hcloud_image_facts Removed deprecated facts module +- hcloud_location_facts Removed deprecated facts module +- hcloud_server_facts Removed deprecated facts module +- hcloud_server_type_facts Removed deprecated facts module +- hcloud_ssh_key_facts Removed deprecated facts module +- hcloud_volume_facts Removed deprecated facts module + +v1.16.0 +======= + +Release Summary +--------------- + +This release bundles the hcloud dependency in the collection, this allows us to ship new features or bug fixes without having to release new major versions and require the users to upgrade their version of the hcloud dependency. + +Minor Changes +------------- + +- Bundle hcloud python dependency inside the collection. +- python-dateutil >= 2.7.5 is now required by the collection. If you already have the hcloud package installed, this dependency should also be installed. +- requests >= 2.20 is now required by the collection. If you already have the hcloud package installed, this dependency should also be installed. + +v1.15.0 +======= + +Minor Changes +------------- + +- hcloud_iso_info Create hcloud_iso_info module + +Bugfixes +-------- + +- hcloud_image_info Fix facts modules deprecated result key +- hcloud_location_info Fix facts modules deprecation warnings +- hcloud_server_type_info Fix facts modules deprecated result dict +- hcloud_server_type_info Fix facts modules deprecation warnings + +v1.14.0 +======= + +Minor Changes +------------- + +- hcloud_network Add expose_routes_to_vswitch field. +- hcloud_network_info Return expose_routes_to_vswitch for network. + +v1.13.0 +======= + +Minor Changes +------------- + +- hcloud_primary_ip_info Create hcloud_primary_ip_info module +- hcloud_server Show warning if used server_type is deprecated. +- hcloud_server_type_info Return deprecation info for server types. + +Bugfixes +-------- + +- hcloud_server - TypeError when trying to use deprecated image with allow_deprecated_image + +v1.12.0 +======= + +Minor Changes +------------- + +- hcloud_server_type_info - Add field included_traffic to returned server types + +Breaking Changes / Porting Guide +-------------------------------- + +- hcloud-python 1.20.0 is now required for full compatibility + v1.11.0 ======= diff --git a/ansible_collections/hetzner/hcloud/COPYING b/ansible_collections/hetzner/hcloud/COPYING index 10926e87f..94a9ed024 100644 --- a/ansible_collections/hetzner/hcloud/COPYING +++ b/ansible_collections/hetzner/hcloud/COPYING @@ -672,4 +672,3 @@ may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. - diff --git a/ansible_collections/hetzner/hcloud/FILES.json b/ansible_collections/hetzner/hcloud/FILES.json index 5d393e7b2..7fc09f2f0 100644 --- a/ansible_collections/hetzner/hcloud/FILES.json +++ b/ansible_collections/hetzner/hcloud/FILES.json @@ -25,14 +25,14 @@ "name": ".azure-pipelines/scripts/aggregate-coverage.sh", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "820353ffde6fd3ad655118772547549d84ccf0a7ba951e8fb1325f912ef640a0", + "chksum_sha256": "531abea7db6392f1de84b0809f72737b8505f7e64b11207b17a2e7e6e3bf5cf4", "format": 1 }, { "name": ".azure-pipelines/scripts/combine-coverage.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e34d4e863a65b9f53c4ca8ae37655858969898a949e050e9cb3cb0d5f02342d0", + "chksum_sha256": "decbefc6322e3d08d54fb229920166bad6fb36f59dc165d51f7cb98c32dbf0c6", "format": 1 }, { @@ -46,28 +46,28 @@ "name": ".azure-pipelines/scripts/publish-codecov.sh", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "70c795c8dbca2534b7909b17911630b7afaa693bbd7154e63a51340bc8b28dad", + "chksum_sha256": "e991f98ae3b734710a8c853e5ea3684c0b3333056f2c8d3be143e35efbe0943d", "format": 1 }, { "name": ".azure-pipelines/scripts/report-coverage.sh", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f6a373322759ccc2736fb25d25d8c402dfe16b5d9a57cfccb1ca8cb136e09663", + "chksum_sha256": "c9720aee235dfe28ab0c161797cf077c30b56feef7452feb84416b12b6165e6a", "format": 1 }, { "name": ".azure-pipelines/scripts/run-tests.sh", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cb08a3ec5715b00d476ae6d63ca22e11a9ad8887239439937d2a7ea342e5a623", + "chksum_sha256": "3134e5893e361cb651eddad6b860f7f2675404a8f29b27a4469bd8e4cf5cfd95", "format": 1 }, { "name": ".azure-pipelines/scripts/time-command.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0232f415efeb583ddff907c058986963b775441eaf129d7162aee0acb0d36834", + "chksum_sha256": "ee82a161d94a52873583501cae6c66f17cce0b8d2dd5c72b6bf470351c7ed3ac", "format": 1 }, { @@ -88,14 +88,14 @@ "name": ".azure-pipelines/templates/matrix.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4fb0d3ffb2125d5806c7597e4f9d4b2af69cf8c337e9d57803081eddd4a6b081", + "chksum_sha256": "b77f92bac77a91e2c4a4ead402920e338ec0e5813f0a75021d688e9c98f4575d", "format": 1 }, { "name": ".azure-pipelines/templates/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2cfa1271f94c71f05ffa0b1f763d8946394b5636e14579cda8ee14bb38bbcf1c", + "chksum_sha256": "e9e72d5efa3e0f5a568ea833bb5f5dc3441c31fb83c215e512ec4b6f18fceb20", "format": 1 }, { @@ -109,7 +109,7 @@ "name": ".azure-pipelines/azure-pipelines.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3c838b484ce8f956163c6fd6ae930f4b792075d5c1b6ddd1130196a48b9333d6", + "chksum_sha256": "2caa1318c14d2aa8d9ea75178d1fd142762b00712708d0dced4482bb254db4da", "format": 1 }, { @@ -127,10 +127,45 @@ "format": 1 }, { - "name": ".github/workflows/check-changelog-fragments.yml", + "name": ".github/workflows/lint.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c0c78648b73d122ec9cbc96b6b1640ef97fbf31cc8cbb847667ed4b68637f26d", + "chksum_sha256": "82e14b10a9c352180c4fa468a84ac330fcb07d3c1e15691b1857f26bfed1dcde", + "format": 1 + }, + { + "name": ".github/workflows/release-please.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5d798fc37f93510a52569257b31062faa3d076cf292ccb4956ee803c851e3061", + "format": 1 + }, + { + "name": ".github/workflows/stale.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "70c41e3f458fe3dd7a748d94e2f577baa52a65ac01300dc72b1a45e2f14bd826", + "format": 1 + }, + { + "name": ".github/workflows/vendor.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "055198ee5dd3ff87c482baa0178158674e50f8674f1b4ebc4238dae383462700", + "format": 1 + }, + { + "name": ".github/release-please-config.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "72c031fde659e0ba12c71a2d9a54f3cfbf68f4f76909882a8b9a79b582bffcd5", + "format": 1 + }, + { + "name": ".github/release-please-manifest.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "042044c5e6e65c58917f2ca482ea5c6ba22d79d9cd6cd42f422ac9e041573955", "format": 1 }, { @@ -165,14 +200,105 @@ "name": "changelogs/changelog.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3310467c01307e817d9efba1a431034cd39df741f4c758842fce4dd899822d6b", + "chksum_sha256": "be4d9f48a034d816590f2e2d5d3f3fe866273f355963f9ad4ab67460b0e72b76", "format": 1 }, { "name": "changelogs/config.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cfa06a718f8b3c226c944d5f33fd3ba5a07415293b07146631503539bea5d1ea", + "chksum_sha256": "c75f2c36674d58bbb6ce5936e60183e221367b24949bf8e11530a4b0ff64ee0e", + "format": 1 + }, + { + "name": "changelogs/dev-changelog.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ac62c11d1513004587ad3042473970d71d5f767e468f0e42905556df0456bc6c", + "format": 1 + }, + { + "name": "docs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "docs/docsite", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "docs/docsite/rst", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "docs/docsite/rst/guides.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "73c3bbbf92bbdd10cd528a667fa6772fb6720180421721e244bbf0bc266e55f2", + "format": 1 + }, + { + "name": "docs/docsite/extra-docs.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "75be447658e708b1dd0f959ccdf7f9dbf3aeddfce9bb1fc90cf453449e6e29b2", + "format": 1 + }, + { + "name": "docs/docsite/links.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "104f6ea9937b7c9fd67cbc4a4bb2d14dacfb214565f970713912866d72eb8b9e", + "format": 1 + }, + { + "name": "examples", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "examples/inventory.hcloud.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1be881272139cafcb2a9fe4d9bcce8da1b409925176b5dfa90a75daa85a2b4ab", + "format": 1 + }, + { + "name": "examples/server-with-firewall.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "329fb1fe6e1619827414bd5f51676a7100c032892e264cc8a04c3bfc67f26b27", + "format": 1 + }, + { + "name": "examples/server-with-private-ip-only.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ff8f0e5640dd9c91aa6ebab10a6938124426b7ae5f00e0b6f1895cfb25a3d1f8", + "format": 1 + }, + { + "name": "examples/use-module-defaults-group.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5fca0a688f2a345ab924bb3d2c5922141e43f7c41cdbb890b88e45042f4efcda", + "format": 1 + }, + { + "name": "examples/use-refresh-inventory.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1f92f7fd225c70ab8b80a396006c494cd51daeb35108132f852edad3dac7a612", "format": 1 }, { @@ -186,7 +312,7 @@ "name": "meta/runtime.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3758e45dc3de5da271325c52c3f1a95a8bb518de67721dc634a85e67e93ecc69", + "chksum_sha256": "d5bd163e45ad774369d04d500f630d969dd3305dfe292cd49406c5d3912c3bb1", "format": 1 }, { @@ -214,7 +340,7 @@ "name": "plugins/doc_fragments/hcloud.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "40624302c695078a48d9883e47133f0b29809c188584af3a314005eb6c495c2d", + "chksum_sha256": "36dc9a087d06d4eea1f4c953f765d7573390e923f23761fc6877d97e03c93fa3", "format": 1 }, { @@ -235,7 +361,7 @@ "name": "plugins/inventory/hcloud.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2a457686e85db10e3bde0e48a603988fbb64a121ba11372a869f66c988ea5338", + "chksum_sha256": "ab8a8501df0fca5d3947fbebb82a979ec23e3ff03752fedcf8a1a94b427ff76f", "format": 1 }, { @@ -246,2026 +372,3545 @@ "format": 1 }, { - "name": "plugins/module_utils/__init__.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "name": "plugins/module_utils/vendor", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "plugins/module_utils/hcloud.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "8246c7b016b767dbf0d5bffe0e27d5f2992b83cce4393255f664e41663e2e7dd", + "name": "plugins/module_utils/vendor/hcloud", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "plugins/modules", + "name": "plugins/module_utils/vendor/hcloud/actions", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "plugins/modules/hcloud_datacenter_facts.py", + "name": "plugins/module_utils/vendor/hcloud/actions/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e35fd6cad5a6e867260be027a13e985623e1e147ec6b25bce848b2cf1d0d1e5f", + "chksum_sha256": "35998e4cf41531ce81aff4bd67b78b05f336f27762a66a600a5fd6e10965939e", "format": 1 }, { - "name": "plugins/modules/hcloud_floating_ip_facts.py", + "name": "plugins/module_utils/vendor/hcloud/actions/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6f6b9bd26b6f508d71824832ece8b1463e7ff8bc2e9225d1f8694006793b7c16", + "chksum_sha256": "93b11a12849e3b2141933ed1763abcc3b3409e7222f68701581794d50ed9c707", "format": 1 }, { - "name": "plugins/modules/hcloud_image_facts.py", + "name": "plugins/module_utils/vendor/hcloud/actions/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2383873b050200488ca7c90e6506a7c11539bbc60948fe914eafe1c606047c48", + "chksum_sha256": "c560adb2ee230b7f1319ef4fbe41817154ed01273c6a848f052eab3f808b3b32", "format": 1 }, { - "name": "plugins/modules/hcloud_location_facts.py", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "7e4c87b1fac74d9b15dd76c462aab1cdb219ddd14009c392162e0e7ce18aecca", + "name": "plugins/module_utils/vendor/hcloud/certificates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "plugins/modules/hcloud_server_facts.py", + "name": "plugins/module_utils/vendor/hcloud/certificates/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "69f52a3daa13373f32f14074dc1ef14d586fb5d87d653438dae06d78ef12975f", + "chksum_sha256": "fd65d4d36c2c7c0ce095ec376c18fd992488ba6b60f39e56856e4ebc66bdc9b8", "format": 1 }, { - "name": "plugins/modules/hcloud_server_type_facts.py", + "name": "plugins/module_utils/vendor/hcloud/certificates/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8716f5f31f6fc0c868ea9526812b99b0d67e6645f6e993983967a2a04544e5b6", + "chksum_sha256": "4cc351febb45ade25dba56062cdb61968389da7e6c69378229b97c5fcd1f81b9", "format": 1 }, { - "name": "plugins/modules/hcloud_ssh_key_facts.py", + "name": "plugins/module_utils/vendor/hcloud/certificates/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6b33f5766ad194010c3bf1b872d27ae6e8bf2fdb5ed7f00fcd998451b7b86528", + "chksum_sha256": "88420631111bd32e92d231140c9f6ac07818ede0d06552d60776e0c5f5d9a0bd", "format": 1 }, { - "name": "plugins/modules/hcloud_volume_facts.py", + "name": "plugins/module_utils/vendor/hcloud/core", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/core/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c2e4545af69cd1ef6dc10ffbf5901b4ba1cae2cb693f5ab4bbde6603e25e6aec", + "chksum_sha256": "63b51b70b61e142c7878feb354fcbef40bdc985b1d5c32c49cb5cd04d0ca7ed6", "format": 1 }, { - "name": "plugins/modules/__init__.py", + "name": "plugins/module_utils/vendor/hcloud/core/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "chksum_sha256": "5c3f76aa70378bd9523ee5198bcbfb58d281d197d5141ef46de3af6e7b37fe55", "format": 1 }, { - "name": "plugins/modules/hcloud_certificate.py", + "name": "plugins/module_utils/vendor/hcloud/core/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "73e1556df459f2da780aa9aacfd95884cec28e1e14e688aee12df4d521b04825", + "chksum_sha256": "6ccbb73a325759635e5259657164db8f29de49c6b45d7ae1bca3abed12198c0d", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/datacenters", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "plugins/modules/hcloud_certificate_info.py", + "name": "plugins/module_utils/vendor/hcloud/datacenters/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "181d2abb4c961c065a4661173835d48795e04ed569b2ac91788c5db2b0e9faeb", + "chksum_sha256": "bfdba3d6f5da4307851aa5d9ce2d8f6d87a61c39ccfbd9f5248d03658b56b18a", "format": 1 }, { - "name": "plugins/modules/hcloud_datacenter_info.py", + "name": "plugins/module_utils/vendor/hcloud/datacenters/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e35fd6cad5a6e867260be027a13e985623e1e147ec6b25bce848b2cf1d0d1e5f", + "chksum_sha256": "8032ebdd92944ee7cbb1f8a1a954f1fa6599918d30f554d03e82848ec06dd770", "format": 1 }, { - "name": "plugins/modules/hcloud_firewall.py", + "name": "plugins/module_utils/vendor/hcloud/datacenters/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a2541dd2758819ab99904da21c29dcb7f973069d9d228ae16f77ea4bb312b4b7", + "chksum_sha256": "27273dc6c5038695d488a075814362e5f72b15e3bf0f86a50738b7b4eedaad39", "format": 1 }, { - "name": "plugins/modules/hcloud_floating_ip.py", + "name": "plugins/module_utils/vendor/hcloud/deprecation", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/deprecation/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6668354c432db61272b71c8424a9a2ad38fc9179bb7e5b9c88d049012cf79259", + "chksum_sha256": "f8981f1aff8cc4218ee6e9cc95d20e61d75cf27b990c8d56f21b9f498be299f8", "format": 1 }, { - "name": "plugins/modules/hcloud_floating_ip_info.py", + "name": "plugins/module_utils/vendor/hcloud/deprecation/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6f6b9bd26b6f508d71824832ece8b1463e7ff8bc2e9225d1f8694006793b7c16", + "chksum_sha256": "68c2eb0e3cf46b140835d1773d9bbe047626f523103174a797021283f91e19b1", "format": 1 }, { - "name": "plugins/modules/hcloud_image_info.py", + "name": "plugins/module_utils/vendor/hcloud/firewalls", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/firewalls/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2383873b050200488ca7c90e6506a7c11539bbc60948fe914eafe1c606047c48", + "chksum_sha256": "e15a656061d0732a0be2963e4a67cd47661246a9472336a7a353631cc38f61cf", "format": 1 }, { - "name": "plugins/modules/hcloud_load_balancer.py", + "name": "plugins/module_utils/vendor/hcloud/firewalls/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9040e4d20e62211a45f5e87886d0d4274be88fa5642f7ca838bad6e9e4da85a9", + "chksum_sha256": "c32c3715d2ff80af0e03571969428ece0e74ea7141fa0a1f50ec924d2385c14c", "format": 1 }, { - "name": "plugins/modules/hcloud_load_balancer_info.py", + "name": "plugins/module_utils/vendor/hcloud/firewalls/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "19c0f2673a29c02a85280f75f3cad34ac070ee611eb87f6b12a6ec5d15066349", + "chksum_sha256": "5dae67805c04fc8edf2d7d80501fdb52f03e6d2225bc4d196c9fa16893322a21", "format": 1 }, { - "name": "plugins/modules/hcloud_load_balancer_network.py", + "name": "plugins/module_utils/vendor/hcloud/floating_ips", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/floating_ips/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1add8639020a29332c474f5d590b523b16c214cfc312aeb9c2fb476e6d6043ba", + "chksum_sha256": "aff70816a40dc8d1bfcab950c6ee46a6f35361ac841e0ba8040a03831f5325cf", "format": 1 }, { - "name": "plugins/modules/hcloud_load_balancer_service.py", + "name": "plugins/module_utils/vendor/hcloud/floating_ips/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c36342c4f33995aed50a5c31de45a14c6636d4c3188edee4fd85ae1d0a4041b1", + "chksum_sha256": "a474eb1108064ac1a2cdee352ffc4129e6fda656cc270fbf4d8cc3dcfbee4db9", "format": 1 }, { - "name": "plugins/modules/hcloud_load_balancer_target.py", + "name": "plugins/module_utils/vendor/hcloud/floating_ips/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f228d4c8c9e3cbabb11c003db01ba4399195d5881a9e001b24c2e83cec4aed2f", + "chksum_sha256": "5f08d0091db38b48e8984c2cc7d56150dd9fe4cd8f0b606b81f699dc816ff194", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/helpers", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "plugins/modules/hcloud_load_balancer_type_info.py", + "name": "plugins/module_utils/vendor/hcloud/helpers/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "44b9672195eb0ba06bacd81c871595ca31455ca343887b964d3d6e473509c41a", + "chksum_sha256": "821b1278b1c33ce89a4fa7264a245a1c41c5d207aa367f145e01cedd97112471", "format": 1 }, { - "name": "plugins/modules/hcloud_location_info.py", + "name": "plugins/module_utils/vendor/hcloud/helpers/labels.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7e4c87b1fac74d9b15dd76c462aab1cdb219ddd14009c392162e0e7ce18aecca", + "chksum_sha256": "f24902eb5eeb94db2a649611ed579c165b75a0d649f4fc7eb648e54d9c79f8cd", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/images", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "plugins/modules/hcloud_network.py", + "name": "plugins/module_utils/vendor/hcloud/images/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9e18800b2b9d77a3e14d02460263809ca5859288f4a5db9ac3f5487d590edab2", + "chksum_sha256": "1560e51ec1f075fc5e231d7f223530bd8b1230f5996cf8b1d28cc7c81eee7558", "format": 1 }, { - "name": "plugins/modules/hcloud_network_info.py", + "name": "plugins/module_utils/vendor/hcloud/images/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c672dbebee7fce20c3ace097e427e52e1aa240c3ac32b256a79edee38c7d89c2", + "chksum_sha256": "1c2ac5b28d51381625fb7f667173ff0f66ab45f78ced21dbcd1feb26662b2c62", "format": 1 }, { - "name": "plugins/modules/hcloud_placement_group.py", + "name": "plugins/module_utils/vendor/hcloud/images/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ac5dcbe68d6775b17a3731be4eae060ab191d649dc4d93ef5bd608d1795c3855", + "chksum_sha256": "fb80abb568da31a61478f0ac5b545788b5897f8c60aa5b1b26be4d88bf57ec09", "format": 1 }, { - "name": "plugins/modules/hcloud_primary_ip.py", + "name": "plugins/module_utils/vendor/hcloud/isos", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/isos/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b9c4bb5f7c6cb8daae694c649910d2191aeb5577b04e5032e9fd4e1cb54acfde", + "chksum_sha256": "c9ed2c450144903b843adc29195b069dd58b0adc7dc53f75c43a51766fb48167", "format": 1 }, { - "name": "plugins/modules/hcloud_rdns.py", + "name": "plugins/module_utils/vendor/hcloud/isos/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f1ff9bb22dd97a9188cfdfda654bedefb0f261b7ec6b469642e93b0c7ac588db", + "chksum_sha256": "438951acaf9becd34968dbfcd0a652f048980237360a2fe9b78a33b381dee443", "format": 1 }, { - "name": "plugins/modules/hcloud_route.py", + "name": "plugins/module_utils/vendor/hcloud/isos/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d3b94be64a305628502f5798b6125f6f8f6ae7ab2f249d30c3773c03bf4cab2a", + "chksum_sha256": "f43cfe53862e45b9676efa0a0e15b434cac1755d136dccb71a9323525fafe7e6", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/load_balancer_types", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "plugins/modules/hcloud_server.py", + "name": "plugins/module_utils/vendor/hcloud/load_balancer_types/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "60ce8badfaa9266dded57290497619e234904a555b6f61d18cfac7f0d581a780", + "chksum_sha256": "71caf061c4d466851a43798a1f17bdc19eccb090bd67a1c2a129bd788328899d", "format": 1 }, { - "name": "plugins/modules/hcloud_server_info.py", + "name": "plugins/module_utils/vendor/hcloud/load_balancer_types/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "69f52a3daa13373f32f14074dc1ef14d586fb5d87d653438dae06d78ef12975f", + "chksum_sha256": "1a05bb879c0a8f2f1925b1505a99cb02d163e9998285638e2f1e3430ea835f0c", "format": 1 }, { - "name": "plugins/modules/hcloud_server_network.py", + "name": "plugins/module_utils/vendor/hcloud/load_balancer_types/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8e6d0b574c0c7704b61d89e06b7ea7cae10d3f61f71807e4694f6cfdcf8abb7c", + "chksum_sha256": "ded063b76c63624614391fb69f2be72bcfe70ae286c749d2b7c4ea9353567502", "format": 1 }, { - "name": "plugins/modules/hcloud_server_type_info.py", + "name": "plugins/module_utils/vendor/hcloud/load_balancers", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/load_balancers/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8716f5f31f6fc0c868ea9526812b99b0d67e6645f6e993983967a2a04544e5b6", + "chksum_sha256": "161c9000b9234efa249acaadec4ccd9ead7cecee84140f44c4ebd5666c50801d", "format": 1 }, { - "name": "plugins/modules/hcloud_ssh_key.py", + "name": "plugins/module_utils/vendor/hcloud/load_balancers/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "753f50cadb0aff1240c92302ba83bfe36601d80c08b131f2b975781bf2e0f2f6", + "chksum_sha256": "2c06ec62a41f5151fb948f4273e0f9b69cfbd65643583e7743e4a0d5db1215c7", "format": 1 }, { - "name": "plugins/modules/hcloud_ssh_key_info.py", + "name": "plugins/module_utils/vendor/hcloud/load_balancers/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6b33f5766ad194010c3bf1b872d27ae6e8bf2fdb5ed7f00fcd998451b7b86528", + "chksum_sha256": "040f6ed68c8a9a3c17e3844a84cee66e8c8aa65d9d198008b2a159dbf2c248c2", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/locations", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "plugins/modules/hcloud_subnetwork.py", + "name": "plugins/module_utils/vendor/hcloud/locations/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3100c0f9fe4f184386b57ff90462437649eaab8513631e7ed26db3f4c9514220", + "chksum_sha256": "d006c2bebf911826db83f28ea30364c0b1ae5548fb29bf9fcf339f09c69790c8", "format": 1 }, { - "name": "plugins/modules/hcloud_volume.py", + "name": "plugins/module_utils/vendor/hcloud/locations/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7b80037e2b1ab8e38dfeb86dcaef129711436ae8314e82288cec7c3be358a94c", + "chksum_sha256": "e444638821213c7efbe99e1fb0274c49f5f891e3f54bbde57db3a4bd6517cf89", "format": 1 }, { - "name": "plugins/modules/hcloud_volume_info.py", + "name": "plugins/module_utils/vendor/hcloud/locations/domain.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c2e4545af69cd1ef6dc10ffbf5901b4ba1cae2cb693f5ab4bbde6603e25e6aec", + "chksum_sha256": "b8a294758b7833502cacabaf29cb24d49409aabb581653b8ec8666707ff12208", "format": 1 }, { - "name": "tests", + "name": "plugins/module_utils/vendor/hcloud/metrics", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "plugins/module_utils/vendor/hcloud/metrics/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b1ffba070e027a488dad69d9f2c1a1e03773866221659fca418b846368c0ec3a", "format": 1 }, { - "name": "tests/integration/targets", + "name": "plugins/module_utils/vendor/hcloud/metrics/domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "79e3e2dc74284723c515f1b44f78531cf92e0349dd3ae25dfba93236b7dd9d4e", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/networks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate", + "name": "plugins/module_utils/vendor/hcloud/networks/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7b71fdc2ed7722bf7979c1b8b2aab782a8d579aeef322cd4f14c53003e6b1513", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/networks/client.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b5c0ed6f78872df7d0179e8b46b9c6120c27e907db70d98f786edaee11381a0d", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/networks/domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "966de8dfbde97299aa023f3782aed0d0125c6cca3381a793908721fb0581e29e", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/placement_groups", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate/defaults", + "name": "plugins/module_utils/vendor/hcloud/placement_groups/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "79f1e97604e25b3b475677ca3a226392e5be0f7563c604a7735838b5c05635a1", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/placement_groups/client.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f0f51f75f0ad37b31a78febf60c1e6b1d8f221de4c616ff8959b9fae77718e92", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/placement_groups/domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c45a8e918b2ad87c7393180da6a1798a1d31ab69099cef858feff9e05caf28db", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/primary_ips", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate/defaults/main.yml", + "name": "plugins/module_utils/vendor/hcloud/primary_ips/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8c7f6588b06981201aaac29d9812da66019603bb613dd38f415355e6ea3f2f17", + "chksum_sha256": "5e874e0100ae8904b20ae2f6e0c69548cc6c23e9aed3fd0ea1c87d936f6a511e", "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate/meta", + "name": "plugins/module_utils/vendor/hcloud/primary_ips/client.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "39af4f01f5d85a02db97759e4c6d003ab74fd40952ace0e45e1d20e95a830229", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/primary_ips/domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5e19cd121863e11beab0981443834f28f4a75094a0c7e0a5a061401f2e4f63c2", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/server_types", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate/meta/main.yml", + "name": "plugins/module_utils/vendor/hcloud/server_types/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a613e3027e9e2dd78cda228949082fa43284dae03c297b946e974ee3c0e76129", + "chksum_sha256": "e6da3fc784f3da36c212b42215b9e2912328fcd59bb831d3fea471b58acd58a2", "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate/tasks", + "name": "plugins/module_utils/vendor/hcloud/server_types/client.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "90a3f293748bbb55ee730dd030e58dac40e62dd93e1fa25bcc9db0bc10ae21bc", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/server_types/domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4a54d898753315f1a791d41027c5f5f2ce8e520d83f77eb2e822e8077abba7df", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/servers", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate/tasks/main.yml", + "name": "plugins/module_utils/vendor/hcloud/servers/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8fb8130a3dfba9804a0d0cdb26aea93c41ae4fc555c6e0cd1cef303a6556150e", + "chksum_sha256": "ad284d5c360237cc9b1ae6915ea70ba7a2cac155e0a0ab4f99248ba941ef2314", "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate/aliases", + "name": "plugins/module_utils/vendor/hcloud/servers/client.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "0eacb79a9bb8696718a4b8a099c061809ef409bc10418bbeb187f00ca098d4f5", "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate_info", + "name": "plugins/module_utils/vendor/hcloud/servers/domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1d3f2802d9bcfe35a430f1f52986c353da6601deb864d496f2ec9d5a2d0cc4aa", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/ssh_keys", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate_info/defaults", + "name": "plugins/module_utils/vendor/hcloud/ssh_keys/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "aff19b5d16f971f0e48b0c5ef0a33dea3b6d6ecec321c5847111ef51dbe805c2", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/ssh_keys/client.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f9c89c0366b722835fdc6536f9e3af62e1e2209503737eb3c1735049100551a5", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/ssh_keys/domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "76806268d6bf2d7a3a8ffac8a89f5807cd87702370a4ff4c270ed8834bbd1a9d", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/volumes", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate_info/defaults/main.yml", + "name": "plugins/module_utils/vendor/hcloud/volumes/__init__.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "54abe03862bd73dde2caed2d60073ae9a9f43aa16642f877891afde936dbadd1", + "chksum_sha256": "8a5821b3704a961aca3ae5d3eb702879fe10a515dc54d7a8aa8956b858ea1272", "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate_info/meta", + "name": "plugins/module_utils/vendor/hcloud/volumes/client.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "81e3bf0dcf125db04983da876f4632706ec52dc2778e7e712653769b13f921cd", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/volumes/domain.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "651109345eb28455a181de68154a7791f260b68441111c4d2b0df476a2452c59", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ba0400e50866b1dc00567cc8920ca0cf2631465c46030e47535b685273cce6a0", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/_client.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "62318ee61219fc5563fbca19da815d47c2abafb6520606a867c9ce834f9ab7f0", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/_exceptions.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b8207b9aebc834910427cf808480c0bea7c803147eb31cc61d4970c5aa5336d5", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/_version.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3480ae676e9f8016a95ede33f2afba76e90a15747c9a568ea19164c4a4819289", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/hcloud.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2bb04844604c6c109a763d1a0e7eca632219e189fb72bff9ef8d8cbc553eb076", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/hcloud/py.typed", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6d67b0f661e0332f0ba8cbbb46ea905c55cb071876091c747546d2c7edf0138f", + "format": 1 + }, + { + "name": "plugins/module_utils/vendor/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/client.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c0b846cf790f98291097a5d2ebe05235a8e9a65082467e54c76b0e04d97261be", + "format": 1 + }, + { + "name": "plugins/module_utils/hcloud.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "757b549f7087e0a78b69143c190177f46344d1ba510d3b17d34e73534c129b4f", + "format": 1 + }, + { + "name": "plugins/module_utils/version.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ba0c1aca62f6adfabff56202492b5d4941e5ff0a5ef3686821cc046e882baca3", + "format": 1 + }, + { + "name": "plugins/modules", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate_info/meta/main.yml", + "name": "plugins/modules/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/modules/certificate.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "09c842e0a9d0933c9abd6d5fae99e861e3a85dcbbcdd21d3ac91287e3ac8b086", + "format": 1 + }, + { + "name": "plugins/modules/certificate_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8f161a4e958682a52dbfec80232469482a7017d5eb9fc33c62fac1a8a25e06c", + "format": 1 + }, + { + "name": "plugins/modules/datacenter_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "72c739e5039fa088da5705985099c2a089546971d2e312ba836f8e9338d10c58", + "format": 1 + }, + { + "name": "plugins/modules/firewall.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "376cdd1493b06f30375afa678898262f1f60061ddff3c8b9269681cc190d3353", + "format": 1 + }, + { + "name": "plugins/modules/firewall_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "981f7f9230b0046aebd6b1dee180e52c81b88f79e560d38f7398e49cc20210ba", + "format": 1 + }, + { + "name": "plugins/modules/firewall_resource.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "04a07b4ecaa1549c3fa8e6d79e11c98d360a8602bcb36faf1c3c8bf3166f2b2f", + "format": 1 + }, + { + "name": "plugins/modules/floating_ip.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3d716b8875aad3a6aa7ac4dab5070c1959e4829fc283a606dafbaa4ced11ccbb", + "format": 1 + }, + { + "name": "plugins/modules/floating_ip_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "907a9c87aaaabf470f08a49d1407b8f6e2991988f5d9ee5f53562f3d9ffff3b4", + "format": 1 + }, + { + "name": "plugins/modules/image_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e66fedae39facbc1d89b4cd7be1813b980f294990cf3799dc4554833cb43bb81", + "format": 1 + }, + { + "name": "plugins/modules/iso_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9366ae2eb9d6fb18d02bd82c776eb6c92ef0e47087aae86dd3b7c2be87f0ae8c", + "format": 1 + }, + { + "name": "plugins/modules/load_balancer.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b4f3515b0a9f2eff4907025f7a852d017084e2ab4fc60512a5d97b8f32b9f106", + "format": 1 + }, + { + "name": "plugins/modules/load_balancer_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "707101d55ed354171668ec60f1c0769de8976d5fad9c9001a90199e075ed2280", + "format": 1 + }, + { + "name": "plugins/modules/load_balancer_network.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "29bc3cc6725abc235f597ce9ebb074613cd74788c135bf6b896ba5e42a9b056d", + "format": 1 + }, + { + "name": "plugins/modules/load_balancer_service.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c40d3e075bc53481bce19d3e50a0f216075c1da864e00bc0b4c1bea2b347f4f5", + "format": 1 + }, + { + "name": "plugins/modules/load_balancer_target.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "55efb3bbb7018cf5810a5c93d39d3647f245e2e46db1fda2a4a45332e951767b", + "chksum_sha256": "ee8c10b5a44fd09723854cb3dd814d1e4f1dea0a5f62107a75ef81bd2e4f2ca3", "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate_info/tasks", + "name": "plugins/modules/load_balancer_type_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "302a5953254603e46ecac5fb04021228f13ca2e66b67a4dfd57fdbaef4fc3153", + "format": 1 + }, + { + "name": "plugins/modules/location_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "15571d3c16960ff15fad111d9602d688acbdd49febc5314a9fc0d57841b07c40", + "format": 1 + }, + { + "name": "plugins/modules/network.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7a1eb9cfafb175e0ca130179a0f692263b98823897bc036565d2a48ed723c4ff", + "format": 1 + }, + { + "name": "plugins/modules/network_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d89fee964c48a06d0255049ad9981fb4184af29d94a6d4290defb0d54529253f", + "format": 1 + }, + { + "name": "plugins/modules/placement_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8a77ff1cefd870cce48b03d1a344eca233ed7de3a469977c8b9d35bfb4f18ac0", + "format": 1 + }, + { + "name": "plugins/modules/primary_ip.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8642642c6e944824959d671dd536c45f0fcf4c02515515749c56ee0e7778a548", + "format": 1 + }, + { + "name": "plugins/modules/primary_ip_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a5957f795d0abb11f0389d6d9bc6f1aafb4ff09ed52b0baf48cf593dd3a8507f", + "format": 1 + }, + { + "name": "plugins/modules/rdns.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9734b7c3f08137511a5f6c122e95c3ea003e5f9dc69e12076f5bf5c6614a0d8a", + "format": 1 + }, + { + "name": "plugins/modules/route.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1b7f689e4ff302ef28daf7a80b07835b6b6da96f545ec1abdd5213d089a8a4e7", + "format": 1 + }, + { + "name": "plugins/modules/server.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a451eac9d2620aba7956e52729842ab4f3297954c0dfcb074d7d2b71a4bed90d", + "format": 1 + }, + { + "name": "plugins/modules/server_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cb88b4ff41204761144380ed9854388f53df45e1bd6da6e8da9446ba128ce9e3", + "format": 1 + }, + { + "name": "plugins/modules/server_network.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "376f6464ab45438fa5e5f04168c8d666aaab3ee3916daf6bad6bd64c5349cc5f", + "format": 1 + }, + { + "name": "plugins/modules/server_type_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b0f5c0186d54da14115c2a65867e80b7fae4a60e49a7e385faad192ac572d87a", + "format": 1 + }, + { + "name": "plugins/modules/ssh_key.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9560f0f9d6ac83fa89acaa7986308cd04d0e4e7464f5fe115c567173fbcdb9f8", + "format": 1 + }, + { + "name": "plugins/modules/ssh_key_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ab0a6ea5ffdb1f98525be155ff1e999abdf7d10056c45ef5b2a203803b455606", + "format": 1 + }, + { + "name": "plugins/modules/subnetwork.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "908458ed99857ebd8dd05ef3eaf18786beaa8877657bf464ae8481082c9f3699", + "format": 1 + }, + { + "name": "plugins/modules/volume.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0b9363ac9df20b307046c6f8ec1f05c93660e020ff831117a1e74144820aa47f", + "format": 1 + }, + { + "name": "plugins/modules/volume_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5cb42a56726cab37af48ce4f6742efbadcb413ecdfac82bfbf1852f46cfa9a5e", + "format": 1 + }, + { + "name": "plugins/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "scripts", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate_info/tasks/main.yml", + "name": "scripts/integration-test-files.sh", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a63d83d5da4d2fbea8fd4849cadab7dadb4ade729f6b9fcc6fe5c789e766d725", + "chksum_sha256": "d835890c4d8350de8bb621221ce1268cd38f6f3addc58adb86b107ff63306ada", "format": 1 }, { - "name": "tests/integration/targets/hcloud_certificate_info/aliases", + "name": "scripts/vendor.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "d750276690c449d94acffc1c4a65b3d53279f09297a6c296ba99a5b4bc2ed6b9", "format": 1 }, { - "name": "tests/integration/targets/hcloud_datacenter_info", + "name": "tests", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_datacenter_info/defaults", + "name": "tests/integration", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_datacenter_info/defaults/main.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "40e03f6534c2f7203b2b3f2aaa720b36d1ef10bfb6e6ab7f46e34bed8c976ec0", + "name": "tests/integration/common", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_datacenter_info/meta", + "name": "tests/integration/common/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_datacenter_info/meta/main.yml", + "name": "tests/integration/common/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/common/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "d8c30f5355ba8e803396d6ea50c3544369aaff6e21a01bfd8aa580c411a8cfe3", "format": 1 }, { - "name": "tests/integration/targets/hcloud_datacenter_info/tasks", + "name": "tests/integration/common/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_datacenter_info/tasks/main.yml", + "name": "tests/integration/common/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d340f77bcf4217306d8913fed14a3fbfcc1c07960e79c9967e194386778bb2c1", + "chksum_sha256": "e9f6c6e47087728c97db611995d108175c380225331d1147fe2fc1b85161e03e", "format": 1 }, { - "name": "tests/integration/targets/hcloud_datacenter_info/aliases", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "name": "tests/integration/targets", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_firewall", + "name": "tests/integration/targets/certificate", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_firewall/defaults", + "name": "tests/integration/targets/certificate/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_firewall/defaults/main.yml", + "name": "tests/integration/targets/certificate/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/certificate/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/certificate/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "61d34ce86e49d5276fafb8777f7bae73bbfa4a19634bd3b20acb7a0478518c62", + "chksum_sha256": "d4fad8529286707ffa108e1ef456d63196e37ce0275a793b7acf7e95bba436b8", "format": 1 }, { - "name": "tests/integration/targets/hcloud_firewall/meta", + "name": "tests/integration/targets/certificate/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_firewall/meta/main.yml", + "name": "tests/integration/targets/certificate/meta/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "5ca0ad9b9be4ca3dedf78d98ebe3a3c83e0ea1ad47953dc626956fd2fddb6ba8", "format": 1 }, { - "name": "tests/integration/targets/hcloud_firewall/tasks", + "name": "tests/integration/targets/certificate/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_firewall/tasks/main.yml", + "name": "tests/integration/targets/certificate/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "123c9c4f92f86644b19fcb25c3be36c460b4c70354f0fbfef84de7ad580954ce", + "chksum_sha256": "aafe97e26c1fbaa05d9a65cbcf3dedd5341e5105f0f0fa898e1141c8b17b9514", "format": 1 }, { - "name": "tests/integration/targets/hcloud_firewall/aliases", + "name": "tests/integration/targets/certificate/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/certificate/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ea99c83481a65032bdfa140e98a9c264b168923d717702d64028d6b903608ad9", "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip", + "name": "tests/integration/targets/certificate/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/certificate_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip/defaults", + "name": "tests/integration/targets/certificate_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip/defaults/main.yml", + "name": "tests/integration/targets/certificate_info/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/certificate_info/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fbd1af75fee59098d3aad4eb8d3c33da014855310457f3b28309ea0f6d601782", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip/meta", + "name": "tests/integration/targets/certificate_info/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ea2da52d6fb3f83fdb8fb09895aa7bebdbcd3daddf29056bc6f29332438b2b29", + "format": 1 + }, + { + "name": "tests/integration/targets/certificate_info/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip/meta/main.yml", + "name": "tests/integration/targets/certificate_info/meta/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "5ca0ad9b9be4ca3dedf78d98ebe3a3c83e0ea1ad47953dc626956fd2fddb6ba8", "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip/tasks", + "name": "tests/integration/targets/certificate_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip/tasks/main.yml", + "name": "tests/integration/targets/certificate_info/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0276c136a340f6ab58b9d3b70f4c977a65fb71c097afda3085bddfcb388de6b9", + "chksum_sha256": "b3dbc0de71d0b7cfa1748b522d515c688af588e852ce8bbe598d382eed0d688c", "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip/aliases", + "name": "tests/integration/targets/certificate_info/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/certificate_info/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "68d3ec6c207868c15a09077e16e7ae59b3e1f5b36afefd72cdeb4aa8103e2385", + "format": 1 + }, + { + "name": "tests/integration/targets/certificate_info/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8f72dd1ffbf49a8ef7bbafc7b0b109eeee08c55e1d48c5ad85ebae063e0b832e", + "format": 1 + }, + { + "name": "tests/integration/targets/certificate_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/datacenter_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip_info", + "name": "tests/integration/targets/datacenter_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip_info/defaults", + "name": "tests/integration/targets/datacenter_info/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip_info/defaults/main.yml", + "name": "tests/integration/targets/datacenter_info/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/datacenter_info/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8da453a6164fb45ef21bc3c098f838b2fc8d12d218b9a5181033f9d9fe1f10bc", + "chksum_sha256": "d03aee3bf07ce52c4114da7533d37eeafbac75d94bfbf2452d072153f6779e82", "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip_info/meta", + "name": "tests/integration/targets/datacenter_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip_info/meta/main.yml", + "name": "tests/integration/targets/datacenter_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/datacenter_info/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "c8fd4d89bc9a54425fbaf95852e31ce7261452b10f58ceb3610d664e8604d3b3", + "format": 1 + }, + { + "name": "tests/integration/targets/datacenter_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip_info/tasks", + "name": "tests/integration/targets/firewall/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip_info/tasks/main.yml", + "name": "tests/integration/targets/firewall/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/firewall/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6046d0d6217a122060d5475f76a7cf78577850caca478faa95e8483cbb53d753", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_floating_ip_info/aliases", + "name": "tests/integration/targets/firewall/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "f8705fc2213ee576bf8ee5d04a481345dfe2510c79ffde92b6aa175c1125ce8d", "format": 1 }, { - "name": "tests/integration/targets/hcloud_image_info", + "name": "tests/integration/targets/firewall/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_image_info/defaults", + "name": "tests/integration/targets/firewall/tasks/cleanup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8dd870926bba2e051a95750ce3b4eecef5a3833377e161e6bef078928274ad92", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "173da05c6cc7b889b350035f9652014c6c8bd831ab310a8a1b789c1b293dcc07", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ba26d33321fa93dca7304bfb232a2ee64886eb4262db53f561fd3f35eb3905d0", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_image_info/defaults/main.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "8c980497c38e44bc1a09ec895a592835525728c6b43efd9e5ae77242fd0e17d3", + "name": "tests/integration/targets/firewall_info/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_image_info/meta", + "name": "tests/integration/targets/firewall_info/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_image_info/meta/main.yml", + "name": "tests/integration/targets/firewall_info/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_image_info/tasks", + "name": "tests/integration/targets/firewall_info/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1693ac14fe568edec1ab921b3e8e531a4224efd18e8661016d8d2c3fe8a4b02c", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_image_info/tasks/main.yml", + "name": "tests/integration/targets/firewall_info/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "54275a2526a90d1dd179f685f0a80970f34a6b714bd3ff988a6faa0613ff639d", + "chksum_sha256": "8dd870926bba2e051a95750ce3b4eecef5a3833377e161e6bef078928274ad92", "format": 1 }, { - "name": "tests/integration/targets/hcloud_image_info/aliases", + "name": "tests/integration/targets/firewall_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall_info/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "05dfe524b49de9946d238d1a70fa56c406d05d0a88bb0befac879d4e236d35fa", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall_info/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "69d25a91d098086e1e51b175236bc2e407c366b055dde4368e046d81b230ef72", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall_info/aliases", "ftype": "file", "chksum_type": "sha256", "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer", + "name": "tests/integration/targets/firewall_resource", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer/defaults", + "name": "tests/integration/targets/firewall_resource/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer/defaults/main.yml", + "name": "tests/integration/targets/firewall_resource/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/firewall_resource/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ffd88985ada5f3e746640b7767ad9fd1b2de04da44dad808cb4c44005be83136", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer/meta", + "name": "tests/integration/targets/firewall_resource/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1693ac14fe568edec1ab921b3e8e531a4224efd18e8661016d8d2c3fe8a4b02c", + "format": 1 + }, + { + "name": "tests/integration/targets/firewall_resource/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer/meta/main.yml", + "name": "tests/integration/targets/firewall_resource/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "8dd870926bba2e051a95750ce3b4eecef5a3833377e161e6bef078928274ad92", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer/tasks", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "tests/integration/targets/firewall_resource/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer/tasks/main.yml", + "name": "tests/integration/targets/firewall_resource/tasks/prepare.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "37823961c8a8ad27ade27df4d929423373e9ad5d8037240f6f137747e39f3c72", + "chksum_sha256": "ec7d8cfba75d93fe977b25f3bb61bde4088eab4391f506cf12c3299ee8dd3533", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer/aliases", + "name": "tests/integration/targets/firewall_resource/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "899ec496fc016f5e221bdf39f63bcf73374fe136f199b14cac9dd23375c989e4", + "chksum_sha256": "579d339b4b48dafbe2593389a0c93a623fc12b3b8e574dcb0227ea995f314b3f", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_info", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "tests/integration/targets/firewall_resource/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_info/defaults", + "name": "tests/integration/targets/floating_ip", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_info/defaults/main.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "b8e931c775c02f0df653a7f9b077d0baa6ff8cba2651e7e7e37b2770259b1590", + "name": "tests/integration/targets/floating_ip/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_info/meta", + "name": "tests/integration/targets/floating_ip/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_info/meta/main.yml", + "name": "tests/integration/targets/floating_ip/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_info/tasks", + "name": "tests/integration/targets/floating_ip/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ee8ff95cdae5d642d0e2f3e9deec28939b952a333577a0c769d0571c086b0322", + "format": 1 + }, + { + "name": "tests/integration/targets/floating_ip/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_info/tasks/main.yml", + "name": "tests/integration/targets/floating_ip/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/floating_ip/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c595101d0d1838a92198660b95c472514fc903757989378c3905cb1d644e5fcd", + "chksum_sha256": "16ab089febd775cfa9f5a8a3056011c532a93b1d1f950651a915d62e24d7f19a", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_info/aliases", + "name": "tests/integration/targets/floating_ip/aliases", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "899ec496fc016f5e221bdf39f63bcf73374fe136f199b14cac9dd23375c989e4", + "chksum_sha256": "8f69b796700c4a65ac5e4f99e98e9ed67c924a70ac01a47dc24e97e95ba74cd6", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_network", + "name": "tests/integration/targets/floating_ip_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_network/defaults", + "name": "tests/integration/targets/floating_ip_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_network/defaults/main.yml", + "name": "tests/integration/targets/floating_ip_info/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/floating_ip_info/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/floating_ip_info/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5f84c924f92341fe78976aac4e941b2b12b351b19db1e2a1ad111d7c75143844", + "chksum_sha256": "f47b074a854695595e870da5d5691e5ece467efd750956ffbbcd9d9020efc0e4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_network/meta", + "name": "tests/integration/targets/floating_ip_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_network/meta/main.yml", + "name": "tests/integration/targets/floating_ip_info/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "99bbb1c9e8aa96c95043637aa790b282d3322c2f8d229576f8f56c5d7dd25f24", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_network/tasks", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "tests/integration/targets/floating_ip_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/floating_ip_info/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5ee8b6ba8ba3fc445ced424d49d396dbdbe6ee7514fd1ef592d1e9c5b3dab975", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_network/tasks/main.yml", + "name": "tests/integration/targets/floating_ip_info/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5969da2934af118e3359860f30d323073a266f82adc0c1cf85335fc1c387d306", + "chksum_sha256": "aca3d846992ca256bfaa2c0d768a9001eb8bfe3ccdf2562badf720a0b872bf96", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_network/aliases", + "name": "tests/integration/targets/floating_ip_info/aliases", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "899ec496fc016f5e221bdf39f63bcf73374fe136f199b14cac9dd23375c989e4", + "chksum_sha256": "8f69b796700c4a65ac5e4f99e98e9ed67c924a70ac01a47dc24e97e95ba74cd6", + "format": 1 + }, + { + "name": "tests/integration/targets/image_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_service", + "name": "tests/integration/targets/image_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_service/defaults", + "name": "tests/integration/targets/image_info/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_service/defaults/main.yml", + "name": "tests/integration/targets/image_info/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "04105693afa5180466c36776a37b716b03bf6a359f433d9a52f4e91845f8ba8a", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_service/meta", + "name": "tests/integration/targets/image_info/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f26b39d72138efec41c82970a699845ba8386a646351d5601aa688c60b7eff53", + "format": 1 + }, + { + "name": "tests/integration/targets/image_info/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_service/meta/main.yml", + "name": "tests/integration/targets/image_info/meta/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "ad47b88e83ca21b36960d3ae0c769bdd9e675fbb710acd12e359fed6f35f0512", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_service/tasks", + "name": "tests/integration/targets/image_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_service/tasks/main.yml", + "name": "tests/integration/targets/image_info/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3107b8864c2eaeaa97d3568f7a52122b64bea694f9903151e073924b1b45574e", + "chksum_sha256": "7e951d86914dc01657a37661b746259f9e40dcdcb813126b324692f95412b2f0", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_service/aliases", + "name": "tests/integration/targets/image_info/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "899ec496fc016f5e221bdf39f63bcf73374fe136f199b14cac9dd23375c989e4", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_target", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "tests/integration/targets/image_info/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7ea853326f39729110be0b037326a29ec310331efce5034372ccc03203a4e8d7", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_target/defaults", + "name": "tests/integration/targets/image_info/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ffe84d68a629b68e7134c3532fb9d37662be13377d7e0bc56287e24d150cacfa", + "format": 1 + }, + { + "name": "tests/integration/targets/image_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/iso_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_target/defaults/main.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "ecbfca90616286afa88ca58f5bd84522a9adb959fb673893e6e4298c6e07ab37", + "name": "tests/integration/targets/iso_info/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_target/meta", + "name": "tests/integration/targets/iso_info/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_target/meta/main.yml", + "name": "tests/integration/targets/iso_info/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_target/tasks", + "name": "tests/integration/targets/iso_info/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f5cb61f65fecc3450541bd4b4688c084dc2d11816d139c5bfd1acbb8669081ae", + "format": 1 + }, + { + "name": "tests/integration/targets/iso_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_target/tasks/main.yml", + "name": "tests/integration/targets/iso_info/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3338b80159baa36a184a00750e6943d5cf7770febdc2db91d047f803c9410f6c", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_target/aliases", + "name": "tests/integration/targets/iso_info/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "899ec496fc016f5e221bdf39f63bcf73374fe136f199b14cac9dd23375c989e4", + "chksum_sha256": "6cb6af8c77bcb39364e5ad398cc1e42481e7cb27c78c9234940545b6af75bb97", + "format": 1 + }, + { + "name": "tests/integration/targets/iso_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8f69b796700c4a65ac5e4f99e98e9ed67c924a70ac01a47dc24e97e95ba74cd6", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_type_info", + "name": "tests/integration/targets/load_balancer/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_type_info/defaults", + "name": "tests/integration/targets/load_balancer/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_type_info/defaults/main.yml", + "name": "tests/integration/targets/load_balancer/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e5641db8ac5de1ea6954d78949fbf6ac9829f86f9135ae9361859aa8220d1892", + "chksum_sha256": "f6de5f5e4c25e8c96aae26bd8562abc8e1c96943c8e6c79671a4440aab0742db", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_type_info/meta", + "name": "tests/integration/targets/load_balancer/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_type_info/meta/main.yml", + "name": "tests/integration/targets/load_balancer/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "5a99ac3c61265385f9385ea4ec0a418e2dffe34761d314f0e62859519229926a", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_type_info/tasks", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "tests/integration/targets/load_balancer/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_type_info/tasks/main.yml", + "name": "tests/integration/targets/load_balancer/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "56fc91b4ed15be279423b2859d700b70589bb1e10bee32a829fa263253917fa2", + "chksum_sha256": "fce600c1ef1affccfe85e9dcff37aec4b0871d71d56f2c11120dd3a702a0092a", "format": 1 }, { - "name": "tests/integration/targets/hcloud_load_balancer_type_info/aliases", + "name": "tests/integration/targets/load_balancer/aliases", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "7487e3a1fe3da170468c7132137f40e49195c30d9a6657f44b055c9c641e3953", "format": 1 }, { - "name": "tests/integration/targets/hcloud_location_info", + "name": "tests/integration/targets/load_balancer_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_location_info/defaults", + "name": "tests/integration/targets/load_balancer_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_location_info/defaults/main.yml", + "name": "tests/integration/targets/load_balancer_info/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_info/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "49468959c5285a8c2c8bccefa9c1d9dca5f0239158eb292bb8f4b768af0fc387", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_location_info/meta", + "name": "tests/integration/targets/load_balancer_info/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bd45a114383aa5d59c5778ca8c73704099082ecf1bc15570c6d176b727964b5c", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_info/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_location_info/meta/main.yml", + "name": "tests/integration/targets/load_balancer_info/meta/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { - "name": "tests/integration/targets/hcloud_location_info/tasks", + "name": "tests/integration/targets/load_balancer_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_location_info/tasks/main.yml", + "name": "tests/integration/targets/load_balancer_info/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c56ca0af25e0ff72b7f1eb84a71924b3ba7b65a659f47e2c2ee75e797dfd3ad7", + "chksum_sha256": "c225009b2c8cd3de8b711c3fe8d0e1e141d6d183977edb95c91866d0d92738d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_location_info/aliases", + "name": "tests/integration/targets/load_balancer_info/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_info/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b24030c8971a1d5cf054c44707ad97ed13c74083e2662a156f99f1f67bd27809", "format": 1 }, { - "name": "tests/integration/targets/hcloud_network", + "name": "tests/integration/targets/load_balancer_info/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "983f436cf1f93d8eb8bb3422a29d9880e396a4975c80f8f89ae9ca15516a0502", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7487e3a1fe3da170468c7132137f40e49195c30d9a6657f44b055c9c641e3953", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_network", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_network/defaults", + "name": "tests/integration/targets/load_balancer_network/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_network/defaults/main.yml", + "name": "tests/integration/targets/load_balancer_network/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_network/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_network/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c7c1cb312be5bbd4d22ec450029303bd989ec1701b9776f03787f96539fcdd1c", + "chksum_sha256": "f1dc2f50249a137e482d1ca622a8e9f50d4e9d52c9e5955c4b0c6d7e305dcacb", "format": 1 }, { - "name": "tests/integration/targets/hcloud_network/meta", + "name": "tests/integration/targets/load_balancer_network/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_network/meta/main.yml", + "name": "tests/integration/targets/load_balancer_network/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_network/tasks", + "name": "tests/integration/targets/load_balancer_network/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e2b1d11efadf81e27292730061f34398782cbc83a8a2dd3531b3aca5ca1bde1b", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_network/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7487e3a1fe3da170468c7132137f40e49195c30d9a6657f44b055c9c641e3953", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_service", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_network/tasks/main.yml", + "name": "tests/integration/targets/load_balancer_service/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_service/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_service/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "93baf9ebc898fb93f961dad90033a1a50e0aa9aa4c9cc197aa78587738e42616", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_network/aliases", + "name": "tests/integration/targets/load_balancer_service/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "67633416742aa9e9480b796190982c057f5a9b7cff7c57625029c3958b07bfb4", + "chksum_sha256": "02300d4510db4a06cb1547d249a5162bcc5a0d08d0cb0103f9e70597fbf0e6b1", "format": 1 }, { - "name": "tests/integration/targets/hcloud_network_info", + "name": "tests/integration/targets/load_balancer_service/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_network_info/defaults", + "name": "tests/integration/targets/load_balancer_service/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_service/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "32be6d9a04b2a48cdbd01cf0f8af04eb3bf53f82146103978cb974d746091e19", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_service/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7487e3a1fe3da170468c7132137f40e49195c30d9a6657f44b055c9c641e3953", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_target", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_network_info/defaults/main.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "c35ec449e5b6556c7c7205e598fdc98b7dff12a9b4a6be290b3fffd936422dc4", + "name": "tests/integration/targets/load_balancer_target/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_network_info/meta", + "name": "tests/integration/targets/load_balancer_target/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_network_info/meta/main.yml", + "name": "tests/integration/targets/load_balancer_target/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_target/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "177697b0f735081cbd3ab5edece930f4648659671cc4de4679528b5bee37a749", "format": 1 }, { - "name": "tests/integration/targets/hcloud_network_info/tasks", + "name": "tests/integration/targets/load_balancer_target/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_network_info/tasks/main.yml", + "name": "tests/integration/targets/load_balancer_target/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_target/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cd53b64efc25ba17d0b8dd5db034e63ddd2bc30bd912766fd8e6f4bed585510a", + "chksum_sha256": "ba13cee3ba0ed0d0ba97e4b518f78db4a4c452006aac77b79328da174dc13bd2", "format": 1 }, { - "name": "tests/integration/targets/hcloud_network_info/aliases", + "name": "tests/integration/targets/load_balancer_target/aliases", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "899ec496fc016f5e221bdf39f63bcf73374fe136f199b14cac9dd23375c989e4", + "chksum_sha256": "7487e3a1fe3da170468c7132137f40e49195c30d9a6657f44b055c9c641e3953", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_type_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_placement_group", + "name": "tests/integration/targets/load_balancer_type_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_placement_group/defaults", + "name": "tests/integration/targets/load_balancer_type_info/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_placement_group/defaults/main.yml", + "name": "tests/integration/targets/load_balancer_type_info/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_type_info/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "17ff18cf59a1d19ba682a9ec20ea00af243721108fb8ae635d26a4f7c8159bdb", + "chksum_sha256": "859cd8bca45204be87a549a1029cd610673872a207aeee448d2a7b8c16791fef", "format": 1 }, { - "name": "tests/integration/targets/hcloud_placement_group/meta", + "name": "tests/integration/targets/load_balancer_type_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_placement_group/meta/main.yml", + "name": "tests/integration/targets/load_balancer_type_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_type_info/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "91f416ba4aeca7549f77cd552073305770d13e2e612129f8d507164946a8107c", + "format": 1 + }, + { + "name": "tests/integration/targets/load_balancer_type_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/location_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_placement_group/tasks", + "name": "tests/integration/targets/location_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_placement_group/tasks/main.yml", + "name": "tests/integration/targets/location_info/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/location_info/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "aeb396dd105fc91adda4785d275eef4a421b2a4d8512ae0ad74532452253162f", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_placement_group/aliases", + "name": "tests/integration/targets/location_info/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "cec2c8560ce24757623da13bf8b4b96e00d10dc6b27219343de84c302b8a23c7", "format": 1 }, { - "name": "tests/integration/targets/hcloud_primary_ip", + "name": "tests/integration/targets/location_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_primary_ip/defaults", + "name": "tests/integration/targets/location_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/location_info/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5820b7ee81974045b5946bbe66668b4f4e9069a47873ef413f5990a0f6704117", + "format": 1 + }, + { + "name": "tests/integration/targets/location_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8f69b796700c4a65ac5e4f99e98e9ed67c924a70ac01a47dc24e97e95ba74cd6", + "format": 1 + }, + { + "name": "tests/integration/targets/network", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_primary_ip/defaults/main.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e6bd2109171a870ae7429ace6bf18a6c0280f188c0030623a1eb7ec5b8c2f72e", + "name": "tests/integration/targets/network/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_primary_ip/meta", + "name": "tests/integration/targets/network/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_primary_ip/meta/main.yml", + "name": "tests/integration/targets/network/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_primary_ip/tasks", + "name": "tests/integration/targets/network/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6ebb966b0125a1f2d47085ec0ec1b7bf5fe77546b0c20e7aa611e5030d6d9858", + "format": 1 + }, + { + "name": "tests/integration/targets/network/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_primary_ip/tasks/main.yml", + "name": "tests/integration/targets/network/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f757913b19fad8b1256fc52046dc8f7bc286e135832837452d54e1744bf6236b", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_primary_ip/aliases", + "name": "tests/integration/targets/network/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "0431a784bc8a2466094fd6c62232127726e53f8edaec2a49427005ecd2dcba89", + "format": 1 + }, + { + "name": "tests/integration/targets/network/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9581657d21f8bc0c17584836cc436825d28f40fb04208162424c3e8f5cbd1d17", "format": 1 }, { - "name": "tests/integration/targets/hcloud_rdns", + "name": "tests/integration/targets/network_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_rdns/defaults", + "name": "tests/integration/targets/network_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_rdns/defaults/main.yml", + "name": "tests/integration/targets/network_info/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/network_info/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/network_info/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8eb41c71669a809fdc595161f96c374cd0514a0fe232db2af105c5b97c86f7d3", + "chksum_sha256": "198cc6e4f9cfdacce2d589208c673f7f14bf8275c47b8bced31a3bbd6f773576", "format": 1 }, { - "name": "tests/integration/targets/hcloud_rdns/meta", + "name": "tests/integration/targets/network_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_rdns/meta/main.yml", + "name": "tests/integration/targets/network_info/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cf9085335e8c310c57255c35c9a7f9ed61437277b0eb23af810565ed3a1b63d9", + "chksum_sha256": "a5269525807e9843a939b1e5953ee9ce827f6be8afa561e38281487e8e7ef4b5", "format": 1 }, { - "name": "tests/integration/targets/hcloud_rdns/tasks", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "tests/integration/targets/network_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_rdns/tasks/main.yml", + "name": "tests/integration/targets/network_info/tasks/prepare.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "90b0be18a534d3700c7fefb669b463786e8c3321377b14b909135ba59629f46a", + "chksum_sha256": "45693cf1a774261838acc82fc21f2577598271ef46c1e7fb32a292670c1747ca", "format": 1 }, { - "name": "tests/integration/targets/hcloud_rdns/aliases", + "name": "tests/integration/targets/network_info/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "899ec496fc016f5e221bdf39f63bcf73374fe136f199b14cac9dd23375c989e4", + "chksum_sha256": "eab97cc6891bb3b071b69d017b703629339189b2d0716bbbd7990bfbc6556e66", "format": 1 }, { - "name": "tests/integration/targets/hcloud_route", + "name": "tests/integration/targets/network_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7487e3a1fe3da170468c7132137f40e49195c30d9a6657f44b055c9c641e3953", + "format": 1 + }, + { + "name": "tests/integration/targets/placement_group", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_route/defaults", + "name": "tests/integration/targets/placement_group/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_route/defaults/main.yml", + "name": "tests/integration/targets/placement_group/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/placement_group/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "73f973bef8b6a33b117aaac954c339df4a6f8c5d482db92f4cc1e95e30153001", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_route/meta", + "name": "tests/integration/targets/placement_group/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0f0ae84d1e77da69c3c1301cb427ab012cbca399c2b2eac4e271ecd525fefd06", + "format": 1 + }, + { + "name": "tests/integration/targets/placement_group/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_route/meta/main.yml", + "name": "tests/integration/targets/placement_group/meta/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cf9085335e8c310c57255c35c9a7f9ed61437277b0eb23af810565ed3a1b63d9", + "chksum_sha256": "f073c7028f9bc45238179f3d0d3bab0daaa1869b0b99a51689b7cfd848079e9e", "format": 1 }, { - "name": "tests/integration/targets/hcloud_route/tasks", + "name": "tests/integration/targets/placement_group/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_route/tasks/main.yml", + "name": "tests/integration/targets/placement_group/tasks/cleanup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b26e28bc9c79f4ba4df5aaba914a41937602230829a42d5d1deed53a33f93dec", + "format": 1 + }, + { + "name": "tests/integration/targets/placement_group/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/placement_group/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "255e31ee3625bd4d451ac04b084f973ac34913a7e16d019b6c81b36a558bef59", + "format": 1 + }, + { + "name": "tests/integration/targets/placement_group/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "754a7fee0573b3a5c27cba4c53e7f66bb36ca485e153305efbd3b332d52c36d7", + "chksum_sha256": "912665632b18f00f5a4e9d5f1d38d6c5ae1500682bd43486cf84e3e2f1fb68d9", "format": 1 }, { - "name": "tests/integration/targets/hcloud_route/aliases", + "name": "tests/integration/targets/placement_group/aliases", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "899ec496fc016f5e221bdf39f63bcf73374fe136f199b14cac9dd23375c989e4", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server", + "name": "tests/integration/targets/primary_ip", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/defaults", + "name": "tests/integration/targets/primary_ip/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/defaults/main.yml", + "name": "tests/integration/targets/primary_ip/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/primary_ip/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/primary_ip/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "96ffda2413e62b64c60f03e5861ebeb6bf0f2837775a412cd2f6c166329ce40b", + "chksum_sha256": "1a08b93d149ae35d7b4971c436478a6c07d214aa85aa415b163344edd24679ae", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/meta", + "name": "tests/integration/targets/primary_ip/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/meta/main.yml", + "name": "tests/integration/targets/primary_ip/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/tasks", + "name": "tests/integration/targets/primary_ip/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "41dad71607635d485207ada2c6b36b2241bbe8dc625ece233666fa6663dbdced", + "format": 1 + }, + { + "name": "tests/integration/targets/primary_ip/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8f69b796700c4a65ac5e4f99e98e9ed67c924a70ac01a47dc24e97e95ba74cd6", + "format": 1 + }, + { + "name": "tests/integration/targets/primary_ip_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/tasks/basic.yml", + "name": "tests/integration/targets/primary_ip_info/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/primary_ip_info/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/primary_ip_info/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "19feb42f8f21128cb8cf3fc0efe562d4d8b2a5d0126bde222ff1fa674204be67", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/tasks/firewalls.yml", + "name": "tests/integration/targets/primary_ip_info/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b43203d049ab787df703e53808f6bcaed4917283ee0a6c5108fab3bc7a0533e9", + "chksum_sha256": "b08efb022cd220cb621bd279a957fe5077a6f969c39fb0aad6332d5d4e4540e3", + "format": 1 + }, + { + "name": "tests/integration/targets/primary_ip_info/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/tasks/main.yml", + "name": "tests/integration/targets/primary_ip_info/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6be3a83aea87ad5a7d89ebf84718d1881d9106e95b55d0895fc58e8aaad4bb6e", + "chksum_sha256": "f485ed9ac7ed72c2e682fcb57a8ea304b1e238b02ed9d4dcbb5340d7af39c7ee", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/tasks/primary_ips.yml", + "name": "tests/integration/targets/primary_ip_info/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "67a5ed8f27f4c55daa46c128212ccffda58b15dd9e1adea5fd75d027820d45ee", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/tasks/private_network_only.yml", + "name": "tests/integration/targets/primary_ip_info/tasks/prepare.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6fe7897ff35cd82e4bef8d5a1fa44a6901a16e75b84f605984cd8881120e41fc", + "chksum_sha256": "e04ceb120ba9d72ac80a937aa669708e4982d0b7cc15d608a8d94f0325ed4643", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/tasks/validation.yml", + "name": "tests/integration/targets/primary_ip_info/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4ca7c4b3b4460686b05dc9878c6365c99dd2f0e290f6b79563312ac9e48a45d7", + "chksum_sha256": "c1a39778efc4b23857542de2a4ac78c34b4e308567b1b20105542cf8d7b5ccc6", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server/aliases", + "name": "tests/integration/targets/primary_ip_info/aliases", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "899ec496fc016f5e221bdf39f63bcf73374fe136f199b14cac9dd23375c989e4", + "chksum_sha256": "8f69b796700c4a65ac5e4f99e98e9ed67c924a70ac01a47dc24e97e95ba74cd6", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_info", + "name": "tests/integration/targets/rdns", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_info/defaults", + "name": "tests/integration/targets/rdns/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_info/defaults/main.yml", + "name": "tests/integration/targets/rdns/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/rdns/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/rdns/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "86d40e0b83f231ac4c5d3d94657d4fb529311261efbba7c0db0093959d82e3a8", + "chksum_sha256": "d5c8e1c0a3056d89a3f6faaef42a1d8eb4527c6bc7a4602e39d59ce9ca1e593f", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_info/meta", + "name": "tests/integration/targets/rdns/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_info/meta/main.yml", + "name": "tests/integration/targets/rdns/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "9b84e2434430aabf5824220a30514d47022f22642441714f3d57f1dd593e2962", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_info/tasks", + "name": "tests/integration/targets/rdns/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/rdns/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "954557c50987b4e0c2623decc35154dfb44dfab4bba1712a5a75d174e2152141", + "format": 1 + }, + { + "name": "tests/integration/targets/rdns/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a6339b5c76d064603cf1d80cd67f67e624bcb8fd90a56c8e5965381cc3336bb3", + "format": 1 + }, + { + "name": "tests/integration/targets/rdns/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7487e3a1fe3da170468c7132137f40e49195c30d9a6657f44b055c9c641e3953", + "format": 1 + }, + { + "name": "tests/integration/targets/route", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/route/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_info/tasks/main.yml", + "name": "tests/integration/targets/route/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/route/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9b22f51084b84013a65066696e8e389643dc7bbf35a64545072bf5c6767b71c1", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_info/aliases", + "name": "tests/integration/targets/route/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "198cc6e4f9cfdacce2d589208c673f7f14bf8275c47b8bced31a3bbd6f773576", + "format": 1 + }, + { + "name": "tests/integration/targets/route/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/route/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_network", + "name": "tests/integration/targets/route/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0031b5a7b0fe8f5d354360f3c0527445614539487ed6d5e99939100daed4fa67", + "format": 1 + }, + { + "name": "tests/integration/targets/route/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7487e3a1fe3da170468c7132137f40e49195c30d9a6657f44b055c9c641e3953", + "format": 1 + }, + { + "name": "tests/integration/targets/server", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_network/defaults", + "name": "tests/integration/targets/server/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_network/defaults/main.yml", + "name": "tests/integration/targets/server/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/server/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0d2464557ed4ade951ffc241834816068b4869198c79cb12d58c3d297f3f9d62", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_network/meta", + "name": "tests/integration/targets/server/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f4d3d7d8b59263668e9c41f19f78e5041686be54e72080e3ac7b4660791b66cb", + "format": 1 + }, + { + "name": "tests/integration/targets/server/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_network/meta/main.yml", + "name": "tests/integration/targets/server/meta/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "f073c7028f9bc45238179f3d0d3bab0daaa1869b0b99a51689b7cfd848079e9e", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_network/tasks", + "name": "tests/integration/targets/server/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_network/tasks/main.yml", + "name": "tests/integration/targets/server/tasks/cleanup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b26e28bc9c79f4ba4df5aaba914a41937602230829a42d5d1deed53a33f93dec", + "format": 1 + }, + { + "name": "tests/integration/targets/server/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/server/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "255e31ee3625bd4d451ac04b084f973ac34913a7e16d019b6c81b36a558bef59", + "format": 1 + }, + { + "name": "tests/integration/targets/server/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d86fb4be961c47637ce1aac842c3841a820d4cc71499f60aaf398ebda962ba70", + "format": 1 + }, + { + "name": "tests/integration/targets/server/tasks/test_basic.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bbaa1a6325538510af5f484a375db05fa78e0c01d5eba621c47bb4d1334c6165", + "format": 1 + }, + { + "name": "tests/integration/targets/server/tasks/test_firewalls.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1ddcad3dccc8d45a0a35579b57ffad87a4837a296e3503dbb98e18af06c8b618", + "format": 1 + }, + { + "name": "tests/integration/targets/server/tasks/test_primary_ips.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4aaf9b7f5c70031287488817d492fb19e9b23cb8e2f886bcf9c7f2b6bb647bf0", + "chksum_sha256": "8caa0b1b0773ce3eaad2afb17670351cf63e477b3c9f49aee4308eb43f22bf20", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_network/aliases", + "name": "tests/integration/targets/server/tasks/test_private_network_only.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "864269fd363e663625df637c1cca2ad3a47ca364bbbad59da58cb75c6002a827", + "chksum_sha256": "83b5f595d0e1184e26087029a3790b52b4a9382879afc4b74919ba85de3e2fcf", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_type_info", + "name": "tests/integration/targets/server/tasks/test_validation.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "36a7a338d06d69fa47aac6fcbf3ff5204d59836548d41c255cfd38726aad6fa5", + "format": 1 + }, + { + "name": "tests/integration/targets/server/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8f69b796700c4a65ac5e4f99e98e9ed67c924a70ac01a47dc24e97e95ba74cd6", + "format": 1 + }, + { + "name": "tests/integration/targets/server_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_type_info/defaults", + "name": "tests/integration/targets/server_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_type_info/defaults/main.yml", + "name": "tests/integration/targets/server_info/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/server_info/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1be0206ddc23c1059392c89a7d0e321ceccf2f3f04a0a33f2d72ff209fb51b62", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_type_info/meta", + "name": "tests/integration/targets/server_info/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "92668ff038a7b9962185484ba6656786f259ef4c9bf03bbc93af8e1da63c8966", + "format": 1 + }, + { + "name": "tests/integration/targets/server_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_type_info/meta/main.yml", + "name": "tests/integration/targets/server_info/tasks/cleanup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "565e1f3618e5dbd550c64cf235b172b8e0b00e89ac20c6b26fc50bfeeb16b312", + "format": 1 + }, + { + "name": "tests/integration/targets/server_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/server_info/tasks/prepare.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "09186e955def1c3db23eeeef46aa27cdc43a43c12b5242bf886fbd78504f76a1", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_type_info/tasks", + "name": "tests/integration/targets/server_info/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "80d5f9bace3abe6b098985a10f256cfafe206c286c796e4510e02dae92d9be61", + "format": 1 + }, + { + "name": "tests/integration/targets/server_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/server_network", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/server_network/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_type_info/tasks/main.yml", + "name": "tests/integration/targets/server_network/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/server_network/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cbc4ccf4a22f6d7cf98a9c1c5710a93611f4992817dd6ffd8260e7c503274852", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_server_type_info/aliases", + "name": "tests/integration/targets/server_network/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "237c63c224a0b4cf03d2ed9695571cb3415373629d6db9a15b4d1f7bfbf9cd50", "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key", + "name": "tests/integration/targets/server_network/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key/defaults", + "name": "tests/integration/targets/server_network/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/server_network/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c8720a7da054c73c059f61d0ed72ddaa434e2cc77bb9593d5bf931c5dccdb7f6", + "format": 1 + }, + { + "name": "tests/integration/targets/server_network/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0258aae2182ba7af39814062303251139ba3f539c4bd6abcc33c735021c1936f", + "format": 1 + }, + { + "name": "tests/integration/targets/server_type_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key/defaults/main.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e9edea10b3c410594ee50a56d4ee65e460170c493ac0af8b0cd64e515af2bcde", + "name": "tests/integration/targets/server_type_info/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key/meta", + "name": "tests/integration/targets/server_type_info/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key/meta/main.yml", + "name": "tests/integration/targets/server_type_info/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/server_type_info/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2267e95d9e1412bfa94ca2ecb76ecf5ea027753e4b50f87cf59c9450f9090b00", + "chksum_sha256": "1627b4770407ecf6d1817b248762a9c95ad73946cd567bd3d201d5d8feb9f039", "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key/tasks", + "name": "tests/integration/targets/server_type_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key/tasks/main.yml", + "name": "tests/integration/targets/server_type_info/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f70d83fc7b47354a328da3c929ef9c7c0eda4d9b98707e5c591399963c15c262", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key/aliases", + "name": "tests/integration/targets/server_type_info/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "69cb31ea4e2610e364b7c1e1808771b2d7083ecd000477114601eb1f6554903f", "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key_info", + "name": "tests/integration/targets/server_type_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_hcloud_cli", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key_info/defaults", + "name": "tests/integration/targets/setup_hcloud_cli/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key_info/defaults/main.yml", + "name": "tests/integration/targets/setup_hcloud_cli/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "622b6f801a80c1b2c9e0f1743cee078a93689245b8b05744a6797e52cd152272", + "chksum_sha256": "ea9ffb6e72f13dfcd04c82c2c97d5fe0f781d8ffcc7ce6644fd102f22ade9c8a", "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key_info/meta", + "name": "tests/integration/targets/setup_selfsigned_certificate", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key_info/meta/main.yml", + "name": "tests/integration/targets/setup_selfsigned_certificate/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_selfsigned_certificate/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2267e95d9e1412bfa94ca2ecb76ecf5ea027753e4b50f87cf59c9450f9090b00", + "chksum_sha256": "a82e09972ee6f55c8ab1f1223693658d2ae6484cf079ee1b7c1df86e434db22b", "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key_info/tasks", + "name": "tests/integration/targets/setup_ssh_keypair", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key_info/tasks/main.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "f2ac15fb16f8e251a10741c938b839bbe4786accfa7ac34a52624f1b03c4d1a9", + "name": "tests/integration/targets/setup_ssh_keypair/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_ssh_key_info/aliases", + "name": "tests/integration/targets/setup_ssh_keypair/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "bcf41c2ee52b037558de2be251057f281ba5a179953577a427ff1692820623f8", + "format": 1 + }, + { + "name": "tests/integration/targets/ssh_key", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_subnetwork", + "name": "tests/integration/targets/ssh_key/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_subnetwork/defaults", + "name": "tests/integration/targets/ssh_key/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_subnetwork/defaults/main.yml", + "name": "tests/integration/targets/ssh_key/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "48ec8ba489041f8a7f69a1bdca4f5e2a4fea038317fb8cafd870b9b2dc2aaea9", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_subnetwork/meta", + "name": "tests/integration/targets/ssh_key/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "aef3bffd48aac9423474a2460c6dd72ff7ebe21c416fe5511754255af7c0e986", + "format": 1 + }, + { + "name": "tests/integration/targets/ssh_key/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_subnetwork/meta/main.yml", + "name": "tests/integration/targets/ssh_key/meta/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "f073c7028f9bc45238179f3d0d3bab0daaa1869b0b99a51689b7cfd848079e9e", "format": 1 }, { - "name": "tests/integration/targets/hcloud_subnetwork/tasks", + "name": "tests/integration/targets/ssh_key/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_subnetwork/tasks/main.yml", + "name": "tests/integration/targets/ssh_key/tasks/cleanup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f4711831c1afed00bb7a91743cd73c0dd48219a7481d46203652614d6196bddf", + "format": 1 + }, + { + "name": "tests/integration/targets/ssh_key/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/ssh_key/tasks/test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a1b71650fde2c710274a8e017cca68e43832ed6360f5aa83eb5909f8f8dc00fd", + "chksum_sha256": "a989382c4723e0cc5fa7132c0bcbdf1ed98c20f714a70ff1f70b99c3927d3c24", "format": 1 }, { - "name": "tests/integration/targets/hcloud_subnetwork/aliases", + "name": "tests/integration/targets/ssh_key/aliases", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a48fdbe183742154c38aa6c1658c907b2fac5a90170aca3f813c65234a33845e", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume", + "name": "tests/integration/targets/ssh_key_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume/defaults", + "name": "tests/integration/targets/ssh_key_info/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume/defaults/main.yml", + "name": "tests/integration/targets/ssh_key_info/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ssh_key_info/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/ssh_key_info/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4be0ed3631b304c66f46a7443dcc0f05b5d7eb99c8d55dc98cf3a4ec7a513a44", + "chksum_sha256": "f2f46edfa24807685a0d8fc8b61c55edcbb8be13b9b0fb8ac94b04778f90e122", "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume/meta", + "name": "tests/integration/targets/ssh_key_info/meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume/meta/main.yml", + "name": "tests/integration/targets/ssh_key_info/meta/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "f073c7028f9bc45238179f3d0d3bab0daaa1869b0b99a51689b7cfd848079e9e", "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume/tasks", + "name": "tests/integration/targets/ssh_key_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume/tasks/main.yml", + "name": "tests/integration/targets/ssh_key_info/tasks/cleanup.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b41df455c2d9c29dad9308de40a386808471e6c386ca104bb9f3fcd0f7b86555", + "chksum_sha256": "b26e28bc9c79f4ba4df5aaba914a41937602230829a42d5d1deed53a33f93dec", "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume/aliases", + "name": "tests/integration/targets/ssh_key_info/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/ssh_key_info/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d178f1df74e003613f8994ed427fd67d2f8882f9d745f13dd9c6d62c77ca2b9e", "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume_info", + "name": "tests/integration/targets/ssh_key_info/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "371a6ad6db6b445c6a0a5b8ab7001eb22fe10bbef016faaa93b48ce7f1b6027a", + "format": 1 + }, + { + "name": "tests/integration/targets/ssh_key_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/subnetwork", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume_info/defaults", + "name": "tests/integration/targets/subnetwork/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume_info/defaults/main.yml", + "name": "tests/integration/targets/subnetwork/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/subnetwork/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "59d614a7e95cd4313b26fb93a5bc68fc1fd8591c08a103af883e885858232d0f", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume_info/meta", + "name": "tests/integration/targets/subnetwork/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "32f6a598c604eec6f0099b84f097aa20a6196d59640c83df05294cf895e02a95", + "format": 1 + }, + { + "name": "tests/integration/targets/subnetwork/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume_info/meta/main.yml", + "name": "tests/integration/targets/subnetwork/tasks/cleanup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a5269525807e9843a939b1e5953ee9ce827f6be8afa561e38281487e8e7ef4b5", + "format": 1 + }, + { + "name": "tests/integration/targets/subnetwork/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/subnetwork/tasks/prepare.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "56045f6ec8ff13b5d0aff5bbc475800a6419ab316cbbed8e8bda4b2613a2549f", + "format": 1 + }, + { + "name": "tests/integration/targets/subnetwork/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ce4392dd35f0c625c3882ae3ead18bf959fd0bffae7250f3105d71853110a6fc", + "format": 1 + }, + { + "name": "tests/integration/targets/subnetwork/aliases", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb96249de09a0a3fdd399aca62aa600ee7bb7b66670ffb65e000fdbe3419d986", + "chksum_sha256": "8f69b796700c4a65ac5e4f99e98e9ed67c924a70ac01a47dc24e97e95ba74cd6", + "format": 1 + }, + { + "name": "tests/integration/targets/volume", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume_info/tasks", + "name": "tests/integration/targets/volume/defaults", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume_info/tasks/main.yml", + "name": "tests/integration/targets/volume/defaults/main", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/volume/defaults/main/common.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "85ad9de1a55e08bfa43a2fc065a515eda84b9c4cc411fee92084c7f47ad7485b", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", "format": 1 }, { - "name": "tests/integration/targets/hcloud_volume_info/aliases", + "name": "tests/integration/targets/volume/defaults/main/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b02a426443d5b1f10b9edc49bad4cdaf57c66f422bdaf60f1b4b904529431fb5", + "chksum_sha256": "ddb11611392004f5c051344d1796fe0c612d6a6871e4d7a7f5cf08cfadd01dd9", "format": 1 }, { - "name": "tests/integration/targets/setup_selfsigned_certificate", + "name": "tests/integration/targets/volume/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/setup_selfsigned_certificate/tasks", + "name": "tests/integration/targets/volume/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", + "format": 1 + }, + { + "name": "tests/integration/targets/volume/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d647af7b292a38d1c68d348b99fbaea7e40244a3588cb63f6658e44f1dd806c2", + "format": 1 + }, + { + "name": "tests/integration/targets/volume/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/targets/volume_info", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/setup_selfsigned_certificate/tasks/main.yml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e4d4d2cd1069f55d65c9e6c8017b934023575ecb0540445ac65714d85d422323", + "name": "tests/integration/targets/volume_info/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/setup_sshkey", + "name": "tests/integration/targets/volume_info/defaults/main", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/setup_sshkey/tasks", + "name": "tests/integration/targets/volume_info/defaults/main/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be14638f15126802a9e86e2cf7080d6fa592a357eb6bc7330d243a8546ea6218", + "format": 1 + }, + { + "name": "tests/integration/targets/volume_info/defaults/main/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7d6f2491e4f90d881e3523fdc14381b1afc8a96a4bc201165df04ea92ad1faac", + "format": 1 + }, + { + "name": "tests/integration/targets/volume_info/tasks", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/integration/targets/setup_sshkey/tasks/main.yml", + "name": "tests/integration/targets/volume_info/tasks/cleanup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "23cc28c94d303945afa4d902e2ebff652f7e55260dcc38344f187e25f13fc11c", + "format": 1 + }, + { + "name": "tests/integration/targets/volume_info/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "016d4c789eb18f7d6cd03c5cd32300a5e1ccec5bb6695aa35b60105c0a5d5d2d", + "chksum_sha256": "f8fa7936e810d502f5960448c95b70be0219cb6de06034f8c84aefede2b661d4", "format": 1 }, { - "name": "tests/integration/constraints.txt", + "name": "tests/integration/targets/volume_info/tasks/prepare.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "67495540cfc7f8c60daeb40ff4607f95f44163cd1ee5d29726042ae7e6c7f45b", + "chksum_sha256": "b995c3cda7ee53f795d5f46a823be7c63f3773f8e5844de4463b8430393f6a35", + "format": 1 + }, + { + "name": "tests/integration/targets/volume_info/tasks/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3f63786237de282418be7b5f82f1c9c2fdaa979f7c66b2dd064074de36ec3b62", + "format": 1 + }, + { + "name": "tests/integration/targets/volume_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d924d2bc6bd615c6717c4d8b837fb0c9f5e4bd8df1c4ae94e3f649265d9ed19f", + "format": 1 + }, + { + "name": "tests/integration/README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cd09c93479d97689a033c01fb5ea3644b37fa9821739f770f75ca196858edff1", "format": 1 }, { "name": "tests/integration/requirements.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f8c3336e9a6315d376c59719374e5aced8a1be5d81934e0b39bc9483fdb49dc6", + "chksum_sha256": "ecdcaa388cde1f4c3487a06fb5b09ada6870c193a3b0d948132fd96d51bc11f8", "format": 1 }, { @@ -2276,164 +3921,213 @@ "format": 1 }, { - "name": "tests/sanity/ignore-2.12.txt", + "name": "tests/sanity/ignore-2.13.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8b8815dcc93330716312b23095ad47d984bd7a039d20a5baa1127ce6d73ca6e8", + "chksum_sha256": "6cd38282c4ae22a82047d6491cf2c203b54a59dad3f30b73f760404aaab212b6", "format": 1 }, { - "name": "tests/sanity/ignore-2.13.txt", + "name": "tests/sanity/ignore-2.14.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8b8815dcc93330716312b23095ad47d984bd7a039d20a5baa1127ce6d73ca6e8", + "chksum_sha256": "a81ea2400b9a7784f1ddd256bce51cae5a74dd3e06695e5a452e2754ae0407fc", "format": 1 }, { - "name": "tests/sanity/ignore-2.14.txt", + "name": "tests/sanity/ignore-2.15.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8b8815dcc93330716312b23095ad47d984bd7a039d20a5baa1127ce6d73ca6e8", + "chksum_sha256": "c895e7338fb3f7aa695054166a509d750c337963f7cdd8fbca0e3d458c8eeb6a", "format": 1 }, { - "name": "tests/sanity/ignore-2.15.txt", + "name": "tests/sanity/ignore-2.16.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8b8815dcc93330716312b23095ad47d984bd7a039d20a5baa1127ce6d73ca6e8", + "chksum_sha256": "c895e7338fb3f7aa695054166a509d750c337963f7cdd8fbca0e3d458c8eeb6a", "format": 1 }, { - "name": "tests/utils", + "name": "tests/unit", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/utils/gitlab", + "name": "tests/unit/module_utils", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/utils/gitlab/gitlab.sh", + "name": "tests/unit/module_utils/test_hcloud.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4649d4c2fc11e85bf786a6a5894aebdb114f32866006f18d3878058b04318501", + "chksum_sha256": "548d821beaa35a1804b6f3dc2b3dbd08a1a678ec0ba93a4f6a474d68fc3ed90f", "format": 1 }, { - "name": "tests/utils/gitlab/integration.sh", + "name": "tests/unit/requirements.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "944d27147a31dfcee01b9a94003b06d1f0ba0c7a396b8fe53f606045ad80d186", + "chksum_sha256": "5ccac8097140973987120cb958c4aaf19a44c6026c678b11cb2be4c784ad133a", "format": 1 }, { - "name": "tests/utils/gitlab/sanity.sh", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "d3de6463986d6023f096a2cd43900cecbe697e7b46b4014a6d1c22a7403a7017", + "name": "tests/utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "tests/utils/shippable", + "name": "tests/utils/gitlab", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/utils/shippable/check_matrix.py", + "name": "tests/utils/gitlab/gitlab.sh", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eca2864515b1854d55dd78b481962969ac030fa8d10a4fd4edb9833797ee85fb", + "chksum_sha256": "9251f81ffb8ee5d83b22743c52a1bc4da60ba0b487c06847be39a9887664a280", "format": 1 }, { - "name": "tests/utils/shippable/hcloud.sh", + "name": "tests/utils/gitlab/integration.sh", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "99db6946e47cf9e49ab2fccbe0aca8ffc9aaa0918fdc9e3ef543601c55a98713", + "chksum_sha256": "944d27147a31dfcee01b9a94003b06d1f0ba0c7a396b8fe53f606045ad80d186", "format": 1 }, { - "name": "tests/utils/shippable/sanity.sh", + "name": "tests/utils/gitlab/sanity.sh", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5815f3e7f2275a6a218e94c6529b0bff8dd6c162b2fece0dacca65cf734c0b22", + "chksum_sha256": "9b2d61be8801d4975930f40eff00561beccfd9b7a1444ec4fe0b500085f8bab5", "format": 1 }, { - "name": "tests/utils/shippable/shippable.sh", + "name": "tests/utils/ci.sh", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "76af8f1e1f106ddd5128405aed79a4cfc15f26ee090b1f20c0b82281efecaf51", + "chksum_sha256": "37eb9786585891fd9c37037770b91c7bb5551c5def84c877e6420e247d59de6e", "format": 1 }, { - "name": "tests/utils/shippable/timing.py", + "name": "tests/.gitignore", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ebb7d3553349747ad41d80899ed353e13cf32fcbecbb6566cf36e9d2bc33703e", + "chksum_sha256": "b5726d3ec9335a09c124469eca039523847a6b0f08a083efaefd002b83326600", "format": 1 }, { - "name": "tests/utils/shippable/timing.sh", + "name": "tests/config.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f3f3cc03a997cdba719b0542fe668fc612451841cbe840ab36865f30aa54a1bd", + "chksum_sha256": "673096e872f1747b59841ac7bc000c35bc81af7581de76f7e8137a4608c9b909", "format": 1 }, { - "name": "tests/.gitignore", + "name": "tests/constraints.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b5726d3ec9335a09c124469eca039523847a6b0f08a083efaefd002b83326600", + "chksum_sha256": "478864a698082b5ccea9854ad41052b6be43e5417503e31f280fdb4f014b936a", "format": 1 }, { "name": "tests/requirements.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "bdf5cf6c8dad0da0a4e726e341c0ba44124759ce387da8c1ab158a6662cb2bb6", + "chksum_sha256": "ebad45710389fc9c2f5cc910cab9014355faf15995ea9eb5f620c7de39874777", + "format": 1 + }, + { + "name": ".ansible-lint", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cf689ed57f2490cabb563d4e59b6abc0fd8d236bf635cf3da4c434cf55f892a7", + "format": 1 + }, + { + "name": ".flake8", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "28c9956d5f02f520015d5b67be3a7c18efc83b97ea9ef54c50e792ed88776ee9", "format": 1 }, { "name": ".gitignore", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c30262d952e58b561e2f59039f3dee6b5b16ff266c5e948fa7ea36049a19acfb", + "chksum_sha256": "d2486cacee7a77366eb4c121fb5aca1112383898975408da427ea7f2c3ee0f8a", "format": 1 }, { "name": ".gitlab-ci.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1fcd5b6555665ac7b3c1a8ec7c0183fc3c9e35f40d8e549faf02a86b948338bc", + "chksum_sha256": "eb567b2e12c80f6541f0afa43e50e8c8b5345ac802ccf7c6b37459ad7b240769", + "format": 1 + }, + { + "name": ".pre-commit-config.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "da31f2118bc2255609a17b9d3cc0e1890355b0b5fbc3d03918afaba12913a1cb", "format": 1 }, { "name": "CHANGELOG.rst", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5c0249d66834ac8b3f72c326bf8666fc3c46ca8d8f023b9363745a0b7c51e358", + "chksum_sha256": "367305440cc3eb10145002d97843578480820303a33ba94efad87790c113090c", "format": 1 }, { "name": "COPYING", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0ae0485a5bd37a63e63603596417e4eb0e653334fa6c7f932ca3a0e85d4af227", + "chksum_sha256": "8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903", + "format": 1 + }, + { + "name": "Makefile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "aafc23f45f3a932b4cb743f04ba476e20334967e6f694b1c60e8e45e95ea7b67", "format": 1 }, { "name": "README.md", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "036ac8c2ec4dcbbdea4b3b5e8bb5ad1e0576ffa4795ad7dc6245da27f8f7998e", + "chksum_sha256": "60ad30b06e6a30077c22cc3c6993a0f254b5003d1965a8654ecf24a12b5e5ea2", + "format": 1 + }, + { + "name": "pyproject.toml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b3d7daeee52b89e89e6d9ae221c798c93e534aab36d40ce2a5779fc407ee4a2b", + "format": 1 + }, + { + "name": "renovate.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e661c54a7b50c698a98791849805cc4e8f80df0814fdc935c5cf2b87bcb6bdec", + "format": 1 + }, + { + "name": "requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5bd140232f60ddf13de976b1fb62d039e9e095c3d3e61ea0a2f85b2e2ff56f19", "format": 1 } ], diff --git a/ansible_collections/hetzner/hcloud/MANIFEST.json b/ansible_collections/hetzner/hcloud/MANIFEST.json index 3a02e99d8..9a5a2871e 100644 --- a/ansible_collections/hetzner/hcloud/MANIFEST.json +++ b/ansible_collections/hetzner/hcloud/MANIFEST.json @@ -2,7 +2,7 @@ "collection_info": { "namespace": "hetzner", "name": "hcloud", - "version": "1.11.0", + "version": "2.5.0", "authors": [ "Hetzner Cloud (github.com/hetznercloud)" ], @@ -17,9 +17,7 @@ "GPL-3.0-or-later" ], "license_file": null, - "dependencies": { - "ansible.netcommon": ">=0.0.1" - }, + "dependencies": {}, "repository": "https://github.com/ansible-collections/hetzner.hcloud", "documentation": "https://docs.ansible.com/ansible/latest/collections/hetzner/hcloud", "homepage": "https://github.com/ansible-collections/hetzner.hcloud", @@ -29,7 +27,7 @@ "name": "FILES.json", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c08aa88abd3883a1ce05b7b35a56516e099b1d2b8acbc37dc495d39e2874c31a", + "chksum_sha256": "afff741537d1ee1b0ef966debfc999feb8bcab30ee5b5780613c7de284cfbb58", "format": 1 }, "format": 1 diff --git a/ansible_collections/hetzner/hcloud/Makefile b/ansible_collections/hetzner/hcloud/Makefile new file mode 100644 index 000000000..48c411d64 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/Makefile @@ -0,0 +1,42 @@ +SHELL := bash +.PHONY: vendor clean + +vendor: + python3 scripts/vendor.py + +vendor-check: + python3 scripts/vendor.py --check + +venv: + python3 -m venv venv + venv/bin/pip install -r requirements.txt + +lint: venv + venv/bin/pylint plugins + +lint-docs: venv + venv/bin/antsibull-docs lint-collection-docs \ + --plugin-docs \ + --validate-collection-refs self \ + . + +clean: + git clean -xdf \ + -e tests/integration/cloud-config-hcloud.ini + +sanity: + ansible-test sanity --color --truncate 0 -v \ + --exclude plugins/module_utils/vendor/ \ + --exclude scripts/ \ + --exclude tests/utils/ \ + --docker default \ + --allow-disabled + +units: + ansible-test units --color --truncate 0 -v \ + --docker default + +integration: + ansible-test integration --color --truncate 0 -v \ + --docker default \ + --allow-disabled diff --git a/ansible_collections/hetzner/hcloud/README.md b/ansible_collections/hetzner/hcloud/README.md index f57ae7be3..274c90884 100644 --- a/ansible_collections/hetzner/hcloud/README.md +++ b/ansible_collections/hetzner/hcloud/README.md @@ -1,20 +1,29 @@ -[![Build Status](https://dev.azure.com/ansible/hetzner.hcloud/_apis/build/status/CI?branchName=master)](https://dev.azure.com/ansible/hetzner.hcloud/_build?definitionId=35) +[![Galaxy version](https://img.shields.io/badge/dynamic/json?label=galaxy&prefix=v&url=https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/index/hetzner/hcloud/&query=highest_version.version)](https://galaxy.ansible.com/ui/repo/published/hetzner/hcloud) +[![GitHub version](https://img.shields.io/github/v/release/ansible-collections/hetzner.hcloud)](https://github.com/ansible-collections/hetzner.hcloud/releases) +[![Build Status](https://dev.azure.com/ansible/hetzner.hcloud/_apis/build/status/ci?branchName=main)](https://dev.azure.com/ansible/hetzner.hcloud/_build?definitionId=35) [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/hetzner.hcloud)](https://codecov.io/gh/ansible-collections/hetzner.hcloud) -Ansible Collection: hetzner.hcloud -================================================= +# Ansible Collection: hetzner.hcloud Ansible Hetzner Cloud Collection for controlling your Hetzner Cloud Resources. +### Python version compatibility + +This collection depends on the [hcloud](https://github.com/hetznercloud/hcloud-python) library. Due to the [hcloud](https://github.com/hetznercloud/hcloud-python) Python Support Policy this collection requires Python 3.8 or greater. + ## Release notes See [here](https://github.com/ansible-collections/hetzner.hcloud/tree/master/CHANGELOG.rst). +### Release policy + +The `main` branch is used for the development of the latest versions of the collections, and may contain breaking changes. The `stable-*` branches (e.g. `stable-1` for the `1.x.y` releases) are used to cut additional minor or patch releases if needed, but we do not provide official support for multiple versions of the collection. + ## Documentation The documentation for all modules are available through `ansible-doc`. -Sample: `ansible-doc hetzner.hcloud.hcloud_server` shows the documentation for the `hcloud_server` module. +Sample: `ansible-doc hetzner.hcloud.server` shows the documentation for the `server` module. For all modules that were part of Ansible directly (before Ansible 2.11) we also have the documentation published in the Ansible documentation: https://docs.ansible.com/ansible/latest/collections/hetzner/hcloud/ @@ -47,16 +56,9 @@ After this you should be able to use `ansible-test integration` to perform the i Sample: ``` -ansible-test integration --color --local -vvv hcloud_server // Executed all integration tests for hcloud_server module +ansible-test integration --color --local -vvv hetzner.hcloud.server // Executed all integration tests for server module ``` ## Releasing a new version -### Generating changelog from fragments - -1. Check if the changelog fragments are available (there should be files in `changelogs/fragments`) -2. Run `antsibull-changelog release --version <version>`, it should remove all fragments and change - the `changelogs/changlog.yaml` and `CHANGELOG.rst` -3. Push the changes to the main branch -4. Tag the release through the Github UI, after this the Github Actions will run and publish the collection to Ansible - Galaxy +If there are releasable changes, `release-please` will open a PR on GitHub with the proposed version. When this PR is merged, `release-please` will tag the release. diff --git a/ansible_collections/hetzner/hcloud/changelogs/changelog.yaml b/ansible_collections/hetzner/hcloud/changelogs/changelog.yaml index 0a4f123d0..cba509728 100644 --- a/ansible_collections/hetzner/hcloud/changelogs/changelog.yaml +++ b/ansible_collections/hetzner/hcloud/changelogs/changelog.yaml @@ -97,6 +97,64 @@ releases: fragments: - arm-features.yaml release_date: '2023-04-11' + 1.12.0: + changes: + breaking_changes: + - hcloud-python 1.20.0 is now required for full compatibility + minor_changes: + - hcloud_server_type_info - Add field included_traffic to returned server types + fragments: + - server-type-included-traffic.yml + release_date: '2023-05-16' + 1.13.0: + changes: + bugfixes: + - hcloud_server - TypeError when trying to use deprecated image with allow_deprecated_image + minor_changes: + - hcloud_primary_ip_info Create hcloud_primary_ip_info module + - hcloud_server Show warning if used server_type is deprecated. + - hcloud_server_type_info Return deprecation info for server types. + fragments: + - create_primary_ip_info.yml + - server-type-deprecation.yml + - server_error_deprecated_image.yml + release_date: '2023-06-20' + 1.14.0: + changes: + minor_changes: + - hcloud_network Add expose_routes_to_vswitch field. + - hcloud_network_info Return expose_routes_to_vswitch for network. + fragments: + - add-field-expose_routes_to_vswitch.yml + release_date: '2023-06-22' + 1.15.0: + changes: + bugfixes: + - hcloud_image_info Fix facts modules deprecated result key + - hcloud_location_info Fix facts modules deprecation warnings + - hcloud_server_type_info Fix facts modules deprecated result dict + - hcloud_server_type_info Fix facts modules deprecation warnings + minor_changes: + - hcloud_iso_info Create hcloud_iso_info module + fragments: + - add-hcloud_iso_info-module.yml + - fix-facts-modules-deprecation.yml + release_date: '2023-07-04' + 1.16.0: + changes: + minor_changes: + - Bundle hcloud python dependency inside the collection. + - python-dateutil >= 2.7.5 is now required by the collection. If you already + have the hcloud package installed, this dependency should also be installed. + - requests >= 2.20 is now required by the collection. If you already have the + hcloud package installed, this dependency should also be installed. + release_summary: This release bundles the hcloud dependency in the collection, + this allows us to ship new features or bug fixes without having to release + new major versions and require the users to upgrade their version of the hcloud + dependency. + fragments: + - vendor-hcloud-python-dependency.yml + release_date: '2023-07-13' 1.2.0: changes: minor_changes: @@ -273,3 +331,160 @@ releases: fragments: - hcloud_server-removed-networks.yml release_date: '2022-12-20' + 2.0.0: + changes: + breaking_changes: + - Drop support for ansible-core 2.12 + - Drop support for python 3.7 + - inventory plugin - Don't set the server image variables (`image_id`, `image_os_flavor` + and `image_name`) when the server image is not defined. + minor_changes: + - Bundle hcloud python dependency inside the collection. + - python-dateutil >= 2.7.5 is now required by the collection. If you already + have the hcloud package installed, this dependency should also be installed. + - requests >= 2.20 is now required by the collection. If you already have the + hcloud package installed, this dependency should also be installed. + release_summary: This release bundles the hcloud dependency in the collection, + this allows us to ship new features or bug fixes without having to release + new major versions and require the users to upgrade their version of the hcloud + dependency. + removed_features: + - hcloud_datacenter_facts Removed deprecated facts module + - hcloud_floating_ip_facts Removed deprecated facts module + - hcloud_image_facts Removed deprecated facts module + - hcloud_location_facts Removed deprecated facts module + - hcloud_server_facts Removed deprecated facts module + - hcloud_server_type_facts Removed deprecated facts module + - hcloud_ssh_key_facts Removed deprecated facts module + - hcloud_volume_facts Removed deprecated facts module + fragments: + - do-not-set-inventory-server-image-variables-when-undefined.yml + - drop-support-for-ansible-core-2.12.yml + - drop-support-for-python-3.7.yml + - remove-deprecated-facts-modules.yml + - vendor-hcloud-python-dependency.yml + release_date: '2023-07-24' + 2.1.0: + changes: + bugfixes: + - '`*_info` - Consistently fail on invalid ID in `*_info` modules.' + minor_changes: + - Use the collection version in the hcloud user-agent instead of the ansible-core + version. + - hcloud_floating_ip_info - Allow querying floating ip by name. + - hcloud_load_balancer_info - Add targets health status field. + - inventory - Allow caching the hcloud inventory. + fragments: + - add-target-health-status-in-hcloud_load_balancer_info.yml + - allow-caching-hcloud-inventory.yml + - consistently-fail-on-invalid-id.yml + - query-hcloud_floating_ip_info-by-name.yml + - use-collection-version-in-user-agent.yml + release_date: '2023-08-17' + 2.1.1: + changes: + bugfixes: + - hcloud_server - Fix string formatting error on deprecated server type warning + fragments: + - fix-string-formatting-error-on-deprecated-server-type-warning.yml + release_date: '2023-08-23' + 2.1.2: + changes: + bugfixes: + - hcloud_firewall - The port argument is required when the firewall rule protocol + is `udp` or `tcp`. + - hcloud_load_balancer_service - In the returned data, the invalid `health_check.http.certificates` + field was renamed to `health_check.http.status_codes`. + fragments: + - rename-load-balancer-service-http-health-check-dict.yaml + - require-firewall-port-argument-on-tcp-or-udp-protocol.yaml + release_date: '2023-10-05' + 2.2.0: + changes: + minor_changes: + - hcloud_iso_info - Add deprecation field + - hcloud_load_balancer_network - Allow selecting a `load_balancer` or `network` + using its ID. + - hcloud_load_balancer_service - Allow selecting a `load_balancer` using its + ID. + - hcloud_load_balancer_target - Allow selecting a `load_balancer` or `server` + using its ID. + - hcloud_rdns - Allow selecting a `server`, `floating_ip`, `primary_ip` or `load_balancer` + using its ID. + - hcloud_route - Allow selecting a `network` using its ID. + - hcloud_server_network - Allow selecting a `network` or `server` using its + ID. + - hcloud_subnetwork - Allow selecting to a `network` using its ID. + fragments: + - add-deprecation-field-to-iso-info.yaml + - allow-selecting-a-resource-using-its-id.yaml + release_date: '2023-10-23' + 2.3.0: + changes: + minor_changes: + - hcloud_datacenter_info - Add `server_types` field + - hcloud_server - Add `created` field + - hcloud_server_info - Add `created` field + fragments: + - add-server_types-to-datacenter-info.yaml + - add_created_to_server_info.yaml + release_date: '2023-11-07' + 2.4.0: + changes: + minor_changes: + - Add the `hetzner.hcloud.all` group to configure all the modules using `module_defaults`. + - Allow to set the `api_endpoint` module argument using the `HCLOUD_ENDPOINT` + environment variable. + - Removed the `hcloud_` prefix from all modules names, e.g. `hetzner.hcloud.hcloud_firewall` + was renamed to `hetzner.hcloud.firewall`. Old module names will continue working. + - Renamed the `endpoint` module argument to `api_endpoint`, backward compatibility + is maintained using an alias. + - hcloud inventory - Add the `api_endpoint` option. + - hcloud inventory - Deprecate the `api_token_env` option, suggest using a lookup + plugin (`{{ lookup('ansible.builtin.env', 'YOUR_ENV_VAR') }}`) or use the + well-known `HCLOUD_TOKEN` environment variable name. + - hcloud inventory - Rename the `token_env` option to `api_token_env`, use aliases + for backward compatibility. + - hcloud inventory - Rename the `token` option to `api_token`, use aliases for + backward compatibility. + fragments: + - add-module-defaults-group-for-all-modules.yml + - improve-inventory-api-options.yml + - improve-modules-api-arguments.yml + - remove-hcloud-prefix-from-modules-names.yml + release_date: '2023-11-24' + 2.4.1: + changes: + bugfixes: + - hcloud inventory - Ensure the API client use a new cache for every *cached + session*. + fragments: + - fix-inventory-fresh-cache.yaml + release_date: '2023-11-27' + 2.5.0: + changes: + minor_changes: + - 'Replace deprecated `ansible.netcommon` ip utils with python `ipaddress` module. + The `ansible.netcommon` collection is no longer required by the collections. + + ' + - firewall - Allow forcing the deletion of firewalls that are still in use. + - firewall - Do not silence 'firewall still in use' delete failures. + - firewall - Return resources the firewall is `applied_to`. + - firewall_info - Add new `firewall_info` module to gather firewalls info. + - firewall_resource - Add new `firewall_resource` module to manage firewalls + resources. + - 'inventory - Add `hostvars_prefix` and hostvars_suffix` options to customize + the inventory host variables keys. + + ' + fragments: + - add-inventory-hostvars-prefix-and-suffix-option.yml + - fix-firewall-deletion.yml + - improve-firewall-resources-management.yml + - replace-deprecated-ansible.netcommon-with-python-ipaddress.yml + modules: + - description: Manage Resources a Hetzner Cloud Firewall is applied to. + name: firewall_resource + namespace: '' + release_date: '2024-02-02' diff --git a/ansible_collections/hetzner/hcloud/changelogs/config.yaml b/ansible_collections/hetzner/hcloud/changelogs/config.yaml index 739603a44..688ddbb0b 100644 --- a/ansible_collections/hetzner/hcloud/changelogs/config.yaml +++ b/ansible_collections/hetzner/hcloud/changelogs/config.yaml @@ -10,19 +10,19 @@ notesdir: fragments prelude_section_name: release_summary prelude_section_title: Release Summary sections: -- - major_changes - - Major Changes -- - minor_changes - - Minor Changes -- - breaking_changes - - Breaking Changes / Porting Guide -- - deprecated_features - - Deprecated Features -- - removed_features - - Removed Features (previously deprecated) -- - security_fixes - - Security Fixes -- - bugfixes - - Bugfixes -- - known_issues - - Known Issues + - - major_changes + - Major Changes + - - minor_changes + - Minor Changes + - - breaking_changes + - Breaking Changes / Porting Guide + - - deprecated_features + - Deprecated Features + - - removed_features + - Removed Features (previously deprecated) + - - security_fixes + - Security Fixes + - - bugfixes + - Bugfixes + - - known_issues + - Known Issues diff --git a/ansible_collections/hetzner/hcloud/changelogs/dev-changelog.md b/ansible_collections/hetzner/hcloud/changelogs/dev-changelog.md new file mode 100644 index 000000000..9fbbf81a3 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/changelogs/dev-changelog.md @@ -0,0 +1,64 @@ +# Changelog + +## [2.5.0](https://github.com/ansible-collections/hetzner.hcloud/compare/2.4.1...2.5.0) (2024-02-02) + + +### Features + +* add `hostvars_prefix` and `hostvars_suffix` options to inventory hostvars ([#423](https://github.com/ansible-collections/hetzner.hcloud/issues/423)) ([4e3f89a](https://github.com/ansible-collections/hetzner.hcloud/commit/4e3f89aed3be6f040e304521d69329c313616df5)) +* allow forcing the deletion of firewalls that are still in use ([#447](https://github.com/ansible-collections/hetzner.hcloud/issues/447)) ([559d315](https://github.com/ansible-collections/hetzner.hcloud/commit/559d31561ad1e0fcf8dd14523bd3eb4262a8a3c1)) +* improve firewall resources management ([#324](https://github.com/ansible-collections/hetzner.hcloud/issues/324)) ([2757fe7](https://github.com/ansible-collections/hetzner.hcloud/commit/2757fe745fcd80409290a453db72e9e6e4016f8f)) +* replace `ansible.netcommon` utils with python3 `ipaddress` module ([#416](https://github.com/ansible-collections/hetzner.hcloud/issues/416)) ([4cfdf50](https://github.com/ansible-collections/hetzner.hcloud/commit/4cfdf50b26536c468705c729cdb48d4b2d421571)) + +## [2.4.1](https://github.com/ansible-collections/hetzner.hcloud/compare/2.4.0...2.4.1) (2023-11-27) + + +### Bug Fixes + +* **inventory:** always use fresh cache on new cached session ([#404](https://github.com/ansible-collections/hetzner.hcloud/issues/404)) ([df7fa04](https://github.com/ansible-collections/hetzner.hcloud/commit/df7fa041494eb3609fcdbe65517a58a6396e0a84)) + +## [2.4.0](https://github.com/ansible-collections/hetzner.hcloud/compare/2.3.0...2.4.0) (2023-11-24) + + +### Features + +* add `hetzner.hcloud.all` action group ([#396](https://github.com/ansible-collections/hetzner.hcloud/issues/396)) ([6581ed5](https://github.com/ansible-collections/hetzner.hcloud/commit/6581ed50db8fd7a3e7525cb364acd63fec256c3a)) +* **inventory:** improve api options ([#397](https://github.com/ansible-collections/hetzner.hcloud/issues/397)) ([9905bd0](https://github.com/ansible-collections/hetzner.hcloud/commit/9905bd0e01ca5a21bb2db94f29a4c5276ffc638b)) +* remove `hcloud_` prefix from all modules names ([#390](https://github.com/ansible-collections/hetzner.hcloud/issues/390)) ([933a162](https://github.com/ansible-collections/hetzner.hcloud/commit/933a16249bc224ee135fcf28a2ebb9ad34978d85)) +* rename api_endpoint module argument ([#395](https://github.com/ansible-collections/hetzner.hcloud/issues/395)) ([7c9fbf8](https://github.com/ansible-collections/hetzner.hcloud/commit/7c9fbf85a734bc7884ff967680beb1fe422dc0ff)) + + +### Bug Fixes + +* **inventory:** improve performance ([#402](https://github.com/ansible-collections/hetzner.hcloud/issues/402)) ([f85d8f4](https://github.com/ansible-collections/hetzner.hcloud/commit/f85d8f4492f5c400dfcc4601f8212b6310f5c691)) + +## [2.3.0](https://github.com/ansible-collections/hetzner.hcloud/compare/2.2.0...2.3.0) (2023-11-07) + +### Features + +- add `created` field to server and server_info modules ([#381](https://github.com/ansible-collections/hetzner.hcloud/issues/381)) ([c3e4c0e](https://github.com/ansible-collections/hetzner.hcloud/commit/c3e4c0ea0a77bec26b83476af99d35078ed9cf6d)) +- add server_types to datacenter info module ([#379](https://github.com/ansible-collections/hetzner.hcloud/issues/379)) ([084e04d](https://github.com/ansible-collections/hetzner.hcloud/commit/084e04d576798e7b49c5c3101803e7b8d2e80181)) + +## [2.2.0](https://github.com/ansible-collections/hetzner.hcloud/compare/2.1.2...2.2.0) (2023-10-23) + +### Features + +- add deprecation field to hcloud_iso_info ([#357](https://github.com/ansible-collections/hetzner.hcloud/issues/357)) ([76ef636](https://github.com/ansible-collections/hetzner.hcloud/commit/76ef636f07feb91daa91ecaa17619d10fea7d6e4)) +- add load_balancer algorithm option ([#368](https://github.com/ansible-collections/hetzner.hcloud/issues/368)) ([a93dbaa](https://github.com/ansible-collections/hetzner.hcloud/commit/a93dbaa428a128555d71a9ef36a1a6c211e09952)) +- allow selecting a resource using its ID ([#361](https://github.com/ansible-collections/hetzner.hcloud/issues/361)) ([5e425c5](https://github.com/ansible-collections/hetzner.hcloud/commit/5e425c56c2643f7c0c68b7c6feb8d3e098d4bcdb)) + +## [2.1.2](https://github.com/ansible-collections/hetzner.hcloud/compare/2.1.1...v2.1.2) (2023-10-05) + +### Bug Fixes + +- firewall port argument is required with udp or tcp ([#345](https://github.com/ansible-collections/hetzner.hcloud/issues/345)) ([76c1abf](https://github.com/ansible-collections/hetzner.hcloud/commit/76c1abf44764778aa6e11bae57df5ee5f69a947b)) +- invalid field in load_balancer_service health_check.http return data ([#333](https://github.com/ansible-collections/hetzner.hcloud/issues/333)) ([fb35516](https://github.com/ansible-collections/hetzner.hcloud/commit/fb35516e7609fad4dd3fa75138dbc603f83d9aa0)) + +## Dev Changelog + +> [!WARNING] +> For the user changelog, please check out [CHANGELOG.rst](../CHANGELOG.rst) instead. + +This file contains a list of changes intended towards developers. It is auto-generated by release-please. + +We would prefer to not generate this file, but disabling this is not supported currently: https://github.com/googleapis/release-please/issues/2007 diff --git a/ansible_collections/hetzner/hcloud/docs/docsite/extra-docs.yml b/ansible_collections/hetzner/hcloud/docs/docsite/extra-docs.yml new file mode 100644 index 000000000..fa9b5bd68 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/docs/docsite/extra-docs.yml @@ -0,0 +1,5 @@ +--- +sections: + - title: Guides + toctree: + - guides diff --git a/ansible_collections/hetzner/hcloud/docs/docsite/links.yml b/ansible_collections/hetzner/hcloud/docs/docsite/links.yml new file mode 100644 index 000000000..c7ae323c2 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/docs/docsite/links.yml @@ -0,0 +1,10 @@ +--- +edit_on_github: + repository: ansible-collections/hetzner.hcloud + branch: main + +extra_links: + - description: Hetzner Cloud API Reference + url: https://docs.hetzner.cloud + - description: Hetzner Cloud API Changelog + url: https://docs.hetzner.cloud/changelog diff --git a/ansible_collections/hetzner/hcloud/docs/docsite/rst/guides.rst b/ansible_collections/hetzner/hcloud/docs/docsite/rst/guides.rst new file mode 100644 index 000000000..14e6b2f05 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/docs/docsite/rst/guides.rst @@ -0,0 +1,50 @@ +.. _ansible_collections.hetzner.hcloud.docsite.authentication: + +Authentication +============== + +To `authenticate the API call against the Hetzner Cloud API <https://docs.hetzner.cloud/#authentication>`_ when +using the ``hetzner.hcloud`` collection, you can provide the API token by different means: + +You can pass the API token using an environment variable (recommended): + +.. code-block:: bash + + export HCLOUD_TOKEN='LRK9DAWQ1ZAEFSrCNEEzLCUwhYX1U3g7wMg4dTlkkDC96fyDuyJ39nVbVjCKSDfj' + + # Verify that your token is working + ansible -m hetzner.hcloud.location_info localhost + +Alternatively, you may provide the API token directly as module argument: + +.. code-block:: yaml + + - name: Create server + hetzner.hcloud.server: + api_token: LRK9DAWQ1ZAEFSrCNEEzLCUwhYX1U3g7wMg4dTlkkDC96fyDuyJ39nVbVjCKSDfj + name: my-server + server_type: cx11 + image: debian-12 + state: present + +To reduce the duplication of the above solution, you may configure the +``hetzner.hcloud.*`` modules using the ``hetzner.hcloud.all`` action group, for +example if you want to store your API token in a vault: + +.. code-block:: yaml + + - name: Demonstrate the usage of the 'hetzner.hcloud.all' module_defaults group + hosts: localhost + connection: local + + module_defaults: + group/hetzner.hcloud.all: + api_token: "{{ _vault_hcloud_api_token }}" + + tasks: + - name: Create server + hetzner.hcloud.server: + name: my-server + server_type: cx11 + image: debian-12 + state: present diff --git a/ansible_collections/hetzner/hcloud/examples/inventory.hcloud.yml b/ansible_collections/hetzner/hcloud/examples/inventory.hcloud.yml new file mode 100644 index 000000000..6a67e2836 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/examples/inventory.hcloud.yml @@ -0,0 +1,7 @@ +# You can list the hosts using: +# ansible-inventory --list -i examples/inventory.hcloud.yml --extra-vars=network_name=my-network + +plugin: hetzner.hcloud.hcloud + +network: "{{ network_name }}" +status: [running] diff --git a/ansible_collections/hetzner/hcloud/examples/server-with-firewall.yml b/ansible_collections/hetzner/hcloud/examples/server-with-firewall.yml new file mode 100644 index 000000000..0e5709676 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/examples/server-with-firewall.yml @@ -0,0 +1,62 @@ +--- +- name: Demonstrate creating servers with a firewall + hosts: localhost + connection: local + + vars: + servers: + - name: my-server1 + - name: my-server2 + + tasks: + - name: Create firewall + hetzner.hcloud.firewall: + name: my-firewall + rules: + - description: allow icmp from everywhere + direction: in + protocol: icmp + source_ips: + - 0.0.0.0/0 + - ::/0 + - description: allow ssh from everywhere + direction: in + protocol: tcp + port: 22 + source_ips: + - 0.0.0.0/0 + - ::/0 + state: present + + - name: Create servers + hetzner.hcloud.server: + name: "{{ item.name }}" + server_type: cx11 + image: debian-12 + labels: + kind: runners + state: started + loop: "{{ servers }}" + + - name: Apply firewall to resources using label selectors + hetzner.hcloud.firewall_resource: + firewall: my-firewall + label_selectors: [kind=runners] + state: present + + - name: Apply firewall to individual servers + hetzner.hcloud.firewall_resource: + firewall: my-firewall + servers: "{{ servers | map(attribute='name') }}" + state: present + + - name: Delete firewall + hetzner.hcloud.firewall: + name: my-firewall + state: absent + + - name: Delete servers + hetzner.hcloud.server: + name: "{{ item.name }}" + state: absent + loop: "{{ servers }}" diff --git a/ansible_collections/hetzner/hcloud/examples/server-with-private-ip-only.yml b/ansible_collections/hetzner/hcloud/examples/server-with-private-ip-only.yml new file mode 100644 index 000000000..67cf47e51 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/examples/server-with-private-ip-only.yml @@ -0,0 +1,50 @@ +--- +- name: Demonstrate creating a server that only has a private ip + hosts: localhost + connection: local + + vars: + servers: + - name: my-server1 + private_ip: 10.0.0.2 + - name: my-server2 + private_ip: 10.0.0.3 + + tasks: + - name: Create a network + hetzner.hcloud.network: + name: my-network + ip_range: 10.0.0.0/8 + state: present + + - name: Create a subnetwork + hetzner.hcloud.subnetwork: + network: my-network + ip_range: 10.0.0.0/16 + network_zone: eu-central + type: cloud + state: present + + - name: Create servers without public IPs + hetzner.hcloud.server: + name: "{{ item.name }}" + server_type: cx11 + image: debian-12 + enable_ipv4: false + enable_ipv6: false + state: stopped # A server without networking cannot be started! + loop: "{{ servers }}" + + - name: Attach private IP to servers + hetzner.hcloud.server_network: + network: my-network + server: "{{ item.name }}" + ip: "{{ item.private_ip }}" + state: present + loop: "{{ servers }}" + + - name: Start servers + hetzner.hcloud.server: + name: "{{ item.name }}" + state: started + loop: "{{ servers }}" diff --git a/ansible_collections/hetzner/hcloud/examples/use-module-defaults-group.yml b/ansible_collections/hetzner/hcloud/examples/use-module-defaults-group.yml new file mode 100644 index 000000000..f3de9adac --- /dev/null +++ b/ansible_collections/hetzner/hcloud/examples/use-module-defaults-group.yml @@ -0,0 +1,27 @@ +--- +- name: Demonstrate the usage of the 'hetzner.hcloud.all' module_defaults group + hosts: localhost + connection: local + + module_defaults: + group/hetzner.hcloud.all: + api_token: LRK9DAWQ1ZAEFSrCNEEzLCUwhYX1U3g7wMg4dTlkkDC96fyDuyJ39nVbVjCKSDfj + + tasks: + - name: Create a volume + hetzner.hcloud.volume: + name: my-volume + location: fsn1 + size: 100 + state: present + register: volume + + - name: Create a server + hetzner.hcloud.server: + name: my-server + server_type: cx11 + image: debian-12 + location: fsn1 + volumes: + - "{{ volume.hcloud_volume.id }}" + state: present diff --git a/ansible_collections/hetzner/hcloud/examples/use-refresh-inventory.yml b/ansible_collections/hetzner/hcloud/examples/use-refresh-inventory.yml new file mode 100644 index 000000000..fd4c351b6 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/examples/use-refresh-inventory.yml @@ -0,0 +1,35 @@ +--- +- name: Demonstrate the usage of 'refresh_inventory' + hosts: localhost + connection: local + + tasks: + - name: Print hostvars + ansible.builtin.debug: + var: hostvars + + - name: Create new server + hetzner.hcloud.server: + name: my-server + server_type: cx11 + image: debian-12 + + - name: Refresh inventory + ansible.builtin.meta: refresh_inventory + + - name: Run tests + block: + - name: Print updated inventory + ansible.builtin.debug: + var: hostvars + + - name: Verify hostvars is not empty + ansible.builtin.assert: + that: + - hostvars != {} + + always: + - name: Cleanup server + hetzner.hcloud.server: + name: my-server + state: absent diff --git a/ansible_collections/hetzner/hcloud/meta/runtime.yml b/ansible_collections/hetzner/hcloud/meta/runtime.yml index d37a1864a..9891ce73a 100644 --- a/ansible_collections/hetzner/hcloud/meta/runtime.yml +++ b/ansible_collections/hetzner/hcloud/meta/runtime.yml @@ -1,35 +1,106 @@ -requires_ansible: '>=2.12.0' +requires_ansible: ">=2.13.0" + +action_groups: + all: + - certificate + - certificate_info + - datacenter_info + - firewall + - firewall_info + - firewall_resource + - floating_ip + - floating_ip_info + - image_info + - iso_info + - load_balancer + - load_balancer_info + - load_balancer_network + - load_balancer_service + - load_balancer_target + - load_balancer_type_info + - location_info + - network + - network_info + - placement_group + - primary_ip + - primary_ip_info + - rdns + - route + - server + - server_info + - server_network + - server_type_info + - ssh_key + - ssh_key_info + - subnetwork + - volume + - volume_info + plugin_routing: modules: - hcloud_location_facts: - deprecation: - removal_version: 2.0.0 - warning_text: see plugin documentation for details - hcloud_server_type_facts: - deprecation: - removal_version: 2.0.0 - warning_text: see plugin documentation for details - hcloud_image_facts: - deprecation: - removal_version: 2.0.0 - warning_text: see plugin documentation for details - hcloud_volume_facts: - deprecation: - removal_version: 2.0.0 - warning_text: see plugin documentation for details - hcloud_floating_ip_facts: - deprecation: - removal_version: 2.0.0 - warning_text: see plugin documentation for details - hcloud_ssh_key_facts: - deprecation: - removal_version: 2.0.0 - warning_text: see plugin documentation for details - hcloud_datacenter_facts: - deprecation: - removal_version: 2.0.0 - warning_text: see plugin documentation for details - hcloud_server_facts: - deprecation: - removal_version: 2.0.0 - warning_text: see plugin documentation for details + hcloud_certificate_info: + redirect: hetzner.hcloud.certificate_info + hcloud_certificate: + redirect: hetzner.hcloud.certificate + hcloud_datacenter_info: + redirect: hetzner.hcloud.datacenter_info + hcloud_firewall: + redirect: hetzner.hcloud.firewall + hcloud_firewall_info: + redirect: hetzner.hcloud.firewall_info + hcloud_firewall_resource: + redirect: hetzner.hcloud.firewall_resource + hcloud_floating_ip_info: + redirect: hetzner.hcloud.floating_ip_info + hcloud_floating_ip: + redirect: hetzner.hcloud.floating_ip + hcloud_image_info: + redirect: hetzner.hcloud.image_info + hcloud_iso_info: + redirect: hetzner.hcloud.iso_info + hcloud_load_balancer_info: + redirect: hetzner.hcloud.load_balancer_info + hcloud_load_balancer_network: + redirect: hetzner.hcloud.load_balancer_network + hcloud_load_balancer_service: + redirect: hetzner.hcloud.load_balancer_service + hcloud_load_balancer_target: + redirect: hetzner.hcloud.load_balancer_target + hcloud_load_balancer_type_info: + redirect: hetzner.hcloud.load_balancer_type_info + hcloud_load_balancer: + redirect: hetzner.hcloud.load_balancer + hcloud_location_info: + redirect: hetzner.hcloud.location_info + hcloud_network_info: + redirect: hetzner.hcloud.network_info + hcloud_network: + redirect: hetzner.hcloud.network + hcloud_placement_group: + redirect: hetzner.hcloud.placement_group + hcloud_primary_ip_info: + redirect: hetzner.hcloud.primary_ip_info + hcloud_primary_ip: + redirect: hetzner.hcloud.primary_ip + hcloud_rdns: + redirect: hetzner.hcloud.rdns + hcloud_route: + redirect: hetzner.hcloud.route + hcloud_server_info: + redirect: hetzner.hcloud.server_info + hcloud_server_network: + redirect: hetzner.hcloud.server_network + hcloud_server_type_info: + redirect: hetzner.hcloud.server_type_info + hcloud_server: + redirect: hetzner.hcloud.server + hcloud_ssh_key_info: + redirect: hetzner.hcloud.ssh_key_info + hcloud_ssh_key: + redirect: hetzner.hcloud.ssh_key + hcloud_subnetwork: + redirect: hetzner.hcloud.subnetwork + hcloud_volume_info: + redirect: hetzner.hcloud.volume_info + hcloud_volume: + redirect: hetzner.hcloud.volume diff --git a/ansible_collections/hetzner/hcloud/plugins/__init__.py b/ansible_collections/hetzner/hcloud/plugins/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/__init__.py diff --git a/ansible_collections/hetzner/hcloud/plugins/doc_fragments/hcloud.py b/ansible_collections/hetzner/hcloud/plugins/doc_fragments/hcloud.py index d19e64616..eee4a9c83 100644 --- a/ansible_collections/hetzner/hcloud/plugins/doc_fragments/hcloud.py +++ b/ansible_collections/hetzner/hcloud/plugins/doc_fragments/hcloud.py @@ -1,29 +1,33 @@ -# -*- coding: utf-8 -*- # Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import absolute_import, division, print_function -__metaclass__ = type +from __future__ import annotations -class ModuleDocFragment(object): - DOCUMENTATION = ''' + +class ModuleDocFragment: + DOCUMENTATION = """ options: - api_token: - description: - - This is the API Token for the Hetzner Cloud. - - You can also set this option by using the environment variable HCLOUD_TOKEN - required: True - type: str - endpoint: - description: - - This is the API Endpoint for the Hetzner Cloud. - default: https://api.hetzner.cloud/v1 - type: str + api_token: + description: + - The API Token for the Hetzner Cloud. + - You can also set this option by using the C(HCLOUD_TOKEN) environment variable. + required: True + type: str + api_endpoint: + description: + - The API Endpoint for the Hetzner Cloud. + - You can also set this option by using the C(HCLOUD_ENDPOINT) environment variable. + default: https://api.hetzner.cloud/v1 + type: str + aliases: [endpoint] + requirements: - - hcloud-python >= 1.0.0 + - python-dateutil >= 2.7.5 + - requests >=2.20 + seealso: -- name: Documentation for Hetzner Cloud API - description: Complete reference for the Hetzner Cloud API. - link: https://docs.hetzner.cloud/ -''' + - name: Documentation for Hetzner Cloud API + description: Complete reference for the Hetzner Cloud API. + link: https://docs.hetzner.cloud +""" diff --git a/ansible_collections/hetzner/hcloud/plugins/inventory/hcloud.py b/ansible_collections/hetzner/hcloud/plugins/inventory/hcloud.py index 390dedc4c..449342271 100644 --- a/ansible_collections/hetzner/hcloud/plugins/inventory/hcloud.py +++ b/ansible_collections/hetzner/hcloud/plugins/inventory/hcloud.py @@ -1,102 +1,139 @@ # Copyright (c) 2019 Hetzner Cloud GmbH <info@hetzner-cloud.de> # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import (absolute_import, division, print_function) - -__metaclass__ = type - -DOCUMENTATION = r''' - name: hcloud - author: - - Lukas Kaemmerling (@lkaemmerling) - short_description: Ansible dynamic inventory plugin for the Hetzner Cloud. - requirements: - - python >= 3.5 - - hcloud-python >= 1.0.0 +from __future__ import annotations + +DOCUMENTATION = """ +name: hcloud +short_description: Ansible dynamic inventory plugin for the Hetzner Cloud. + +description: + - Reads inventories from the Hetzner Cloud API. + - Uses a YAML configuration file that ends with C(hcloud.yml) or C(hcloud.yaml). + +author: + - Lukas Kaemmerling (@lkaemmerling) + +requirements: + - python-dateutil >= 2.7.5 + - requests >=2.20 + +extends_documentation_fragment: + - constructed + - inventory_cache + +options: + plugin: + description: Mark this as an P(hetzner.hcloud.hcloud#inventory) inventory instance. + required: true + choices: [hcloud, hetzner.hcloud.hcloud] + + api_token: + description: + - The API Token for the Hetzner Cloud. + type: str + required: false # TODO: Mark as required once 'api_token_env' is removed. + aliases: [token] + env: + - name: HCLOUD_TOKEN + api_token_env: + description: + - Environment variable name to load the Hetzner Cloud API Token from. + type: str + default: HCLOUD_TOKEN + aliases: [token_env] + deprecated: + why: The option is adding too much complexity, while the alternatives are preferred. + collection_name: hetzner.hcloud + version: 3.0.0 + alternatives: Use the P(ansible.builtin.env#lookup) lookup plugin instead. + api_endpoint: description: - - Reads inventories from the Hetzner Cloud API. - - Uses a YAML configuration file that ends with hcloud.(yml|yaml). - extends_documentation_fragment: - - constructed - options: - plugin: - description: marks this as an instance of the "hcloud" plugin - required: true - choices: ["hcloud", "hetzner.hcloud.hcloud"] - token: - description: The Hetzner Cloud API Token. - required: false - group: - description: The group all servers are automatically added to. - default: hcloud - type: str - required: false - token_env: - description: Environment variable to load the Hetzner Cloud API Token from. - default: HCLOUD_TOKEN - type: str - required: false - connect_with: - description: | - Connect to the server using the value from this field. This sets the `ansible_host` - variable to the value indicated, if that value is available. If you need further - customization, like falling back to private ipv4 if the server has no public ipv4, - you can use `compose` top-level key. - default: public_ipv4 - type: str - choices: - - public_ipv4 - - public_ipv6 - - hostname - - ipv4_dns_ptr - - private_ipv4 - locations: - description: Populate inventory with instances in this location. - default: [] - type: list - elements: str - required: false - types: - description: Populate inventory with instances with this type. - default: [] - type: list - elements: str - required: false - images: - description: Populate inventory with instances with this image name, only available for system images. - default: [] - type: list - elements: str - required: false - label_selector: - description: Populate inventory with instances with this label. - default: "" - type: str - required: false - network: - description: Populate inventory with instances which are attached to this network name or ID. - default: "" - type: str - required: false - status: - description: Populate inventory with instances with this status. - default: [] - type: list - elements: str - required: false -''' - -EXAMPLES = r""" -# Minimal example. `HCLOUD_TOKEN` is exposed in environment. -plugin: hcloud + - The API Endpoint for the Hetzner Cloud. + type: str + default: https://api.hetzner.cloud/v1 + env: + - name: HCLOUD_ENDPOINT + + group: + description: The group all servers are automatically added to. + default: hcloud + type: str + required: false + connect_with: + description: | + Connect to the server using the value from this field. This sets the C(ansible_host) + variable to the value indicated, if that value is available. If you need further + customization, like falling back to private ipv4 if the server has no public ipv4, + you can use O(compose) top-level key. + default: public_ipv4 + type: str + choices: + - public_ipv4 + - public_ipv6 + - hostname + - ipv4_dns_ptr + - private_ipv4 + + locations: + description: Populate inventory with instances in this location. + default: [] + type: list + elements: str + required: false + types: + description: Populate inventory with instances with this type. + default: [] + type: list + elements: str + required: false + images: + description: Populate inventory with instances with this image name, only available for system images. + default: [] + type: list + elements: str + required: false + label_selector: + description: Populate inventory with instances with this label. + default: "" + type: str + required: false + network: + description: Populate inventory with instances which are attached to this network name or ID. + default: "" + type: str + required: false + status: + description: Populate inventory with instances with this status. + default: [] + type: list + elements: str + required: false + + hostvars_prefix: + description: + - The prefix for host variables names coming from Hetzner Cloud. + type: str + version_added: 2.5.0 + hostvars_suffix: + description: + - The suffix for host variables names coming from Hetzner Cloud. + type: str + version_added: 2.5.0 +""" +EXAMPLES = """ +# Minimal example. 'HCLOUD_TOKEN' is exposed in environment. +plugin: hetzner.hcloud.hcloud + +--- # Example with templated token, e.g. provided through extra vars. -plugin: hcloud -token: "{{ hetzner_apitoken }}" +plugin: hetzner.hcloud.hcloud +api_token: "{{ _vault_hetzner_cloud_token }}" -# Example with locations, types, status and token -plugin: hcloud -token: foobar +--- +# Example with locations, types, status +plugin: hetzner.hcloud.hcloud locations: - nbg1 types: @@ -104,10 +141,11 @@ types: status: - running +--- # Group by a location with prefix e.g. "hcloud_location_nbg1" # and image_os_flavor without prefix and separator e.g. "ubuntu" # and status with prefix e.g. "server_status_running" -plugin: hcloud +plugin: hetzner.hcloud.hcloud keyed_groups: - key: location prefix: hcloud_location @@ -118,234 +156,355 @@ keyed_groups: """ import os -from ansible.errors import AnsibleError -from ansible.module_utils._text import to_native -from ansible.plugins.inventory import BaseInventoryPlugin, Constructable -from ansible.release import __version__ +import sys from ipaddress import IPv6Network -try: - from hcloud import hcloud - from hcloud import APIException - HAS_HCLOUD = True -except ImportError: - HAS_HCLOUD = False +from ansible.errors import AnsibleError +from ansible.inventory.manager import InventoryData +from ansible.module_utils.common.text.converters import to_native +from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable +from ansible.utils.display import Display + +from ..module_utils.client import ( + Client, + ClientException, + client_check_required_lib, + client_get_by_name_or_id, +) +from ..module_utils.vendor.hcloud import APIException +from ..module_utils.vendor.hcloud.networks import Network +from ..module_utils.vendor.hcloud.servers import Server +from ..module_utils.version import version + +if sys.version_info >= (3, 11): + # The typed dicts are only used to help development and we prefer not requiring + # the additional typing-extensions dependency + from typing import NotRequired, TypedDict + + class InventoryPrivateNetwork(TypedDict): + id: int + name: str + ip: str + + class InventoryServer(TypedDict): + id: int + name: str + status: str + + # Server Type + type: str + server_type: str + architecture: str + # Datacenter + datacenter: str + location: str -class InventoryModule(BaseInventoryPlugin, Constructable): - NAME = 'hetzner.hcloud.hcloud' + # Labels + labels: dict[str, str] + + # Network + ipv4: NotRequired[str] + ipv6: NotRequired[str] + ipv6_network: NotRequired[str] + ipv6_network_mask: NotRequired[str] + private_ipv4: NotRequired[str] + private_networks: list[InventoryPrivateNetwork] + + # Image + image_id: int + image_name: str + image_os_flavor: str + + # Ansible + ansible_host: str + +else: + InventoryServer = dict + + +def first_ipv6_address(network: str) -> str: + """ + Return the first address for a ipv6 network. + + :param network: IPv6 Network. + """ + return next(IPv6Network(network).hosts()) + + +class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): + NAME = "hetzner.hcloud.hcloud" + + inventory: InventoryData + display: Display + + client: Client + + network: Network | None def _configure_hcloud_client(self): - self.token_env = self.get_option("token_env") - self.templar.available_variables = self._vars - self.api_token = self.templar.template(self.get_option("token"), fail_on_undefined=False) or os.getenv(self.token_env) - if self.api_token is None: + # If api_token_env is not the default, print a deprecation warning and load the + # environment variable. + api_token_env = self.get_option("api_token_env") + if api_token_env != "HCLOUD_TOKEN": + self.display.deprecated( + "The 'api_token_env' option is deprecated, please use the 'HCLOUD_TOKEN' " + "environment variable or use the 'ansible.builtin.env' lookup instead.", + version="3.0.0", + collection_name="hetzner.hcloud", + ) + if api_token_env in os.environ: + self.set_option("api_token", os.environ.get(api_token_env)) + + api_token = self.get_option("api_token") + api_endpoint = self.get_option("api_endpoint") + + if api_token is None: # TODO: Remove once I(api_token_env) is removed. raise AnsibleError( - "Please specify a token, via the option token, via environment variable HCLOUD_TOKEN " - "or via custom environment variable set by token_env option." + "No setting was provided for required configuration setting: " + "plugin_type: inventory " + "plugin: hetzner.hcloud.hcloud " + "setting: api_token" ) - self.endpoint = os.getenv("HCLOUD_ENDPOINT") or "https://api.hetzner.cloud/v1" + # Resolve template string + api_token = self.templar.template(api_token) - self.client = hcloud.Client(token=self.api_token, - api_endpoint=self.endpoint, - application_name="ansible-inventory", - application_version=__version__) + self.client = Client( + token=api_token, + api_endpoint=api_endpoint, + application_name="ansible-inventory", + application_version=version, + ) - def _test_hcloud_token(self): try: - # We test the API Token against the location API, because this is the API with the smallest result - # and not controllable from the customer. - self.client.locations.get_all() - except APIException: - raise AnsibleError("Invalid Hetzner Cloud API Token.") - - def _get_servers(self): - if len(self.get_option("label_selector")) > 0: - self.servers = self.client.servers.get_all(label_selector=self.get_option("label_selector")) - else: - self.servers = self.client.servers.get_all() - - def _filter_servers(self): + # Ensure the api token is valid + self.client.locations.get_list() + except APIException as exception: + raise AnsibleError("Invalid Hetzner Cloud API Token.") from exception + + def _validate_options(self) -> None: if self.get_option("network"): - network = self.templar.template(self.get_option("network"), fail_on_undefined=False) or self.get_option("network") + network_param: str = self.get_option("network") + network_param = self.templar.template(network_param) + try: - self.network = self.client.networks.get_by_name(network) - if self.network is None: - self.network = self.client.networks.get_by_id(network) - except APIException: - raise AnsibleError( - "The given network is not found.") - - tmp = [] - for server in self.servers: - for server_private_network in server.private_net: - if server_private_network.network.id == self.network.id: - tmp.append(server) - self.servers = tmp + self.network = client_get_by_name_or_id(self.client, "networks", network_param) + except (ClientException, APIException) as exception: + raise AnsibleError(to_native(exception)) from exception + + def _fetch_servers(self) -> list[Server]: + self._validate_options() + + get_servers_params = {} + if self.get_option("label_selector"): + get_servers_params["label_selector"] = self.get_option("label_selector") + + if self.get_option("status"): + get_servers_params["status"] = self.get_option("status") + + servers = self.client.servers.get_all(**get_servers_params) + + if self.get_option("network"): + servers = [s for s in servers if self.network.id in [p.network.id for p in s.private_net]] if self.get_option("locations"): - tmp = [] - for server in self.servers: - if server.datacenter.location.name in self.get_option("locations"): - tmp.append(server) - self.servers = tmp + locations: list[str] = self.get_option("locations") + servers = [s for s in servers if s.datacenter.location.name in locations] if self.get_option("types"): - tmp = [] - for server in self.servers: - if server.server_type.name in self.get_option("types"): - tmp.append(server) - self.servers = tmp + server_types: list[str] = self.get_option("types") + servers = [s for s in servers if s.server_type.name in server_types] if self.get_option("images"): - tmp = [] - for server in self.servers: - if server.image is not None and server.image.os_flavor in self.get_option("images"): - tmp.append(server) - self.servers = tmp + images: list[str] = self.get_option("images") + servers = [s for s in servers if s.image is not None and s.image.os_flavor in images] - if self.get_option("status"): - tmp = [] - for server in self.servers: - if server.status in self.get_option("status"): - tmp.append(server) - self.servers = tmp - - def _set_server_attributes(self, server): - self.inventory.set_variable(server.name, "id", to_native(server.id)) - self.inventory.set_variable(server.name, "name", to_native(server.name)) - self.inventory.set_variable(server.name, "status", to_native(server.status)) - self.inventory.set_variable(server.name, "type", to_native(server.server_type.name)) - self.inventory.set_variable(server.name, "architecture", to_native(server.server_type.architecture)) + return servers + + def _build_inventory_server(self, server: Server) -> InventoryServer: + server_dict: InventoryServer = {} + server_dict["id"] = server.id + server_dict["name"] = to_native(server.name) + server_dict["status"] = to_native(server.status) + + # Server Type + server_dict["type"] = to_native(server.server_type.name) + server_dict["server_type"] = to_native(server.server_type.name) + server_dict["architecture"] = to_native(server.server_type.architecture) # Network if server.public_net.ipv4: - self.inventory.set_variable(server.name, "ipv4", to_native(server.public_net.ipv4.ip)) + server_dict["ipv4"] = to_native(server.public_net.ipv4.ip) if server.public_net.ipv6: - self.inventory.set_variable(server.name, "ipv6_network", to_native(server.public_net.ipv6.network)) - self.inventory.set_variable(server.name, "ipv6_network_mask", to_native(server.public_net.ipv6.network_mask)) - self.inventory.set_variable(server.name, "ipv6", to_native(self._first_ipv6_address(server.public_net.ipv6.ip))) - - self.inventory.set_variable( - server.name, - "private_networks", - [ - {"name": n.network.name, "id": n.network.id, "ip": n.ip} - for n in server.private_net - ], - ) + server_dict["ipv6"] = to_native(first_ipv6_address(server.public_net.ipv6.ip)) + server_dict["ipv6_network"] = to_native(server.public_net.ipv6.network) + server_dict["ipv6_network_mask"] = to_native(server.public_net.ipv6.network_mask) + + server_dict["private_networks"] = [ + {"id": v.network.id, "name": to_native(v.network.name), "ip": to_native(v.ip)} for v in server.private_net + ] if self.get_option("network"): - for server_private_network in server.private_net: + for private_net in server.private_net: # Set private_ipv4 if user filtered for one network - if server_private_network.network.id == self.network.id: - self.inventory.set_variable(server.name, "private_ipv4", to_native(server_private_network.ip)) - - try: - self.inventory.set_variable(server.name, "ansible_host", self._get_server_ansible_host(server)) - except AnsibleError as e: - # Log warning that for this host can not be connected to, using the - # method specified in `connect_with`. Users might use `compose` to - # override the connection method, or implement custom logic, so we - # do not need to abort if nothing matched. - self.display.v("[hcloud] %s" % e, server.name) - - # Server Type - if server.server_type is not None: - self.inventory.set_variable(server.name, "server_type", to_native(server.server_type.name)) + if private_net.network.id == self.network.id: + server_dict["private_ipv4"] = to_native(private_net.ip) + break # Datacenter - self.inventory.set_variable(server.name, "datacenter", to_native(server.datacenter.name)) - self.inventory.set_variable(server.name, "location", to_native(server.datacenter.location.name)) + server_dict["datacenter"] = to_native(server.datacenter.name) + server_dict["location"] = to_native(server.datacenter.location.name) # Image if server.image is not None: - self.inventory.set_variable(server.name, "image_id", to_native(server.image.id)) - self.inventory.set_variable(server.name, "image_os_flavor", to_native(server.image.os_flavor)) - if server.image.name is not None: - self.inventory.set_variable(server.name, "image_name", to_native(server.image.name)) - else: - self.inventory.set_variable(server.name, "image_name", to_native(server.image.description)) - else: - self.inventory.set_variable(server.name, "image_id", to_native("No Image ID found")) - self.inventory.set_variable(server.name, "image_name", to_native("No Image Name found")) - self.inventory.set_variable(server.name, "image_os_flavor", to_native("No Image OS Flavor found")) + server_dict["image_id"] = server.image.id + server_dict["image_os_flavor"] = to_native(server.image.os_flavor) + server_dict["image_name"] = to_native(server.image.name or server.image.description) # Labels - self.inventory.set_variable(server.name, "labels", dict(server.labels)) + server_dict["labels"] = dict(server.labels) - def _get_server_ansible_host(self, server): + try: + server_dict["ansible_host"] = self._get_server_ansible_host(server) + except AnsibleError as exception: + # Log warning that for this host can not be connected to, using the + # method specified in 'connect_with'. Users might use 'compose' to + # override the connection method, or implement custom logic, so we + # do not need to abort if nothing matched. + self.display.v(f"[hcloud] {exception}", server.name) + + return server_dict + + def _get_server_ansible_host(self, server: Server): if self.get_option("connect_with") == "public_ipv4": if server.public_net.ipv4: return to_native(server.public_net.ipv4.ip) - else: - raise AnsibleError("Server has no public ipv4, but connect_with=public_ipv4 was specified") + raise AnsibleError("Server has no public ipv4, but connect_with=public_ipv4 was specified") if self.get_option("connect_with") == "public_ipv6": if server.public_net.ipv6: - return to_native(self._first_ipv6_address(server.public_net.ipv6.ip)) - else: - raise AnsibleError("Server has no public ipv6, but connect_with=public_ipv6 was specified") + return to_native(first_ipv6_address(server.public_net.ipv6.ip)) + raise AnsibleError("Server has no public ipv6, but connect_with=public_ipv6 was specified") - elif self.get_option("connect_with") == "hostname": + if self.get_option("connect_with") == "hostname": # every server has a name, no need to guard this return to_native(server.name) - elif self.get_option("connect_with") == "ipv4_dns_ptr": + if self.get_option("connect_with") == "ipv4_dns_ptr": if server.public_net.ipv4: return to_native(server.public_net.ipv4.dns_ptr) - else: - raise AnsibleError("Server has no public ipv4, but connect_with=ipv4_dns_ptr was specified") + raise AnsibleError("Server has no public ipv4, but connect_with=ipv4_dns_ptr was specified") - elif self.get_option("connect_with") == "private_ipv4": + if self.get_option("connect_with") == "private_ipv4": if self.get_option("network"): - for server_private_network in server.private_net: - if server_private_network.network.id == self.network.id: - return to_native(server_private_network.ip) + for private_net in server.private_net: + if private_net.network.id == self.network.id: + return to_native(private_net.ip) else: - raise AnsibleError( - "You can only connect via private IPv4 if you specify a network") - - def _first_ipv6_address(self, network): - return next(IPv6Network(network).hosts()) + raise AnsibleError("You can only connect via private IPv4 if you specify a network") def verify_file(self, path): """Return the possibly of a file being consumable by this plugin.""" - return ( - super(InventoryModule, self).verify_file(path) and - path.endswith(("hcloud.yaml", "hcloud.yml")) - ) + return super().verify_file(path) and path.endswith(("hcloud.yaml", "hcloud.yml")) + + def _get_cached_result(self, path, cache) -> tuple[list[InventoryServer], bool]: + # false when refresh_cache or --flush-cache is used + if not cache: + return [], False + + # get the user-specified directive + if not self.get_option("cache"): + return [], False + + cache_key = self.get_cache_key(path) + try: + cached_result = self._cache[cache_key] + except KeyError: + # if cache expires or cache file doesn"t exist + return [], False + + return cached_result, True + + def _update_cached_result(self, path, cache, result: list[InventoryServer]): + if not self.get_option("cache"): + return + + cache_key = self.get_cache_key(path) + # We weren't explicitly told to flush the cache, and there's already a cache entry, + # this means that the result we're being passed came from the cache. As such we don't + # want to "update" the cache as that could reset a TTL on the cache entry. + if cache and cache_key in self._cache: + return + + self._cache[cache_key] = result def parse(self, inventory, loader, path, cache=True): - super(InventoryModule, self).parse(inventory, loader, path, cache) + super().parse(inventory, loader, path, cache) + + try: + client_check_required_lib() + except ClientException as exception: + raise AnsibleError(to_native(exception)) from exception - if not HAS_HCLOUD: - raise AnsibleError("The Hetzner Cloud dynamic inventory plugin requires hcloud-python.") + # Allow using extra variables arguments as template variables (e.g. + # '--extra-vars my_var=my_value') + self.templar.available_variables = self._vars self._read_config_data(path) self._configure_hcloud_client() - self._test_hcloud_token() - self._get_servers() - self._filter_servers() + + servers, cached = self._get_cached_result(path, cache) + if not cached: + with self.client.cached_session(): + servers = [self._build_inventory_server(s) for s in self._fetch_servers()] # Add a top group self.inventory.add_group(group=self.get_option("group")) - for server in self.servers: - self.inventory.add_host(server.name, group=self.get_option("group")) - self._set_server_attributes(server) + hostvars_prefix = self.get_option("hostvars_prefix") + hostvars_suffix = self.get_option("hostvars_suffix") + + for server in servers: + self.inventory.add_host(server["name"], group=self.get_option("group")) + for key, value in server.items(): + # Add hostvars prefix and suffix for variables coming from the Hetzner Cloud. + if hostvars_prefix or hostvars_suffix: + if key not in ("ansible_host",): + if hostvars_prefix: + key = hostvars_prefix + key + if hostvars_suffix: + key = key + hostvars_suffix + + self.inventory.set_variable(server["name"], key, value) # Use constructed if applicable - strict = self.get_option('strict') + strict = self.get_option("strict") # Composed variables - self._set_composite_vars(self.get_option('compose'), self.inventory.get_host(server.name).get_vars(), server.name, strict=strict) + self._set_composite_vars( + self.get_option("compose"), + self.inventory.get_host(server["name"]).get_vars(), + server["name"], + strict=strict, + ) # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group - self._add_host_to_composed_groups(self.get_option('groups'), {}, server.name, strict=strict) + self._add_host_to_composed_groups( + self.get_option("groups"), + {}, + server["name"], + strict=strict, + ) # Create groups based on variable values and add the corresponding hosts to it - self._add_host_to_keyed_groups(self.get_option('keyed_groups'), {}, server.name, strict=strict) + self._add_host_to_keyed_groups( + self.get_option("keyed_groups"), + {}, + server["name"], + strict=strict, + ) + + self._update_cached_result(path, cache, servers) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/client.py new file mode 100644 index 000000000..d82a63007 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/client.py @@ -0,0 +1,108 @@ +# Copyright: (c) 2023, Hetzner Cloud GmbH <info@hetzner-cloud.de> + +from __future__ import annotations + +from contextlib import contextmanager + +from ansible.module_utils.basic import missing_required_lib + +from .vendor.hcloud import APIException, Client as ClientBase + +HAS_REQUESTS = True +HAS_DATEUTIL = True + +try: + import requests # pylint: disable=unused-import +except ImportError: + HAS_REQUESTS = False + +try: + import dateutil # pylint: disable=unused-import +except ImportError: + HAS_DATEUTIL = False + + +class ClientException(Exception): + """An error related to the client occurred.""" + + +def client_check_required_lib(): + if not HAS_REQUESTS: + raise ClientException(missing_required_lib("requests")) + if not HAS_DATEUTIL: + raise ClientException(missing_required_lib("python-dateutil")) + + +def _client_resource_not_found(resource: str, param: str | int): + return ClientException(f"resource ({resource.rstrip('s')}) does not exist: {param}") + + +def client_get_by_name_or_id(client: Client, resource: str, param: str | int): + """ + Get a resource by name, and if not found by its ID. + + :param client: Client to use to make the call + :param resource: Name of the resource client that implements both `get_by_name` and `get_by_id` methods + :param param: Name or ID of the resource to query + """ + resource_client = getattr(client, resource) + + result = resource_client.get_by_name(param) + if result is not None: + return result + + # If the param is not a valid ID, prevent an unnecessary call to the API. + try: + int(param) + except ValueError as exception: + raise _client_resource_not_found(resource, param) from exception + + try: + return resource_client.get_by_id(param) + except APIException as exception: + if exception.code == "not_found": + raise _client_resource_not_found(resource, param) from exception + raise exception + + +if HAS_REQUESTS: + + class CachedSession(requests.Session): + cache: dict[str, requests.Response] + + def __init__(self) -> None: + super().__init__() + self.cache = {} + + def send(self, request: requests.PreparedRequest, **kwargs) -> requests.Response: # type: ignore[no-untyped-def] + """ + Send a given PreparedRequest. + """ + if request.method != "GET" or request.url is None: + return super().send(request, **kwargs) + + if request.url in self.cache: + return self.cache[request.url] + + response = super().send(request, **kwargs) + if response.ok: + self.cache[request.url] = response + + return response + + +class Client(ClientBase): + @contextmanager + def cached_session(self): + """ + Swap the client session during the scope of the context. The session will cache + all GET requests. + + Cached response will not expire, therefore the cached client must not be used + for long living scopes. + """ + self._requests_session = CachedSession() + try: + yield + finally: + self._requests_session = requests.Session() diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/hcloud.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/hcloud.py index 932b0c529..eab0aef59 100644 --- a/ansible_collections/hetzner/hcloud/plugins/module_utils/hcloud.py +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/hcloud.py @@ -1,45 +1,101 @@ -# -*- coding: utf-8 -*- # Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) -from __future__ import absolute_import, division, print_function -__metaclass__ = type +from __future__ import annotations -from ansible.module_utils.ansible_release import __version__ -from ansible.module_utils.basic import env_fallback, missing_required_lib +import traceback +from typing import Any, NoReturn -try: - import hcloud +from ansible.module_utils.basic import AnsibleModule as AnsibleModuleBase, env_fallback +from ansible.module_utils.common.text.converters import to_native - HAS_HCLOUD = True -except ImportError: - HAS_HCLOUD = False +from .client import ClientException, client_check_required_lib, client_get_by_name_or_id +from .vendor.hcloud import APIException, Client, HCloudException +from .vendor.hcloud.actions import ActionException +from .version import version -class Hcloud(object): - def __init__(self, module, represent): +# Provide typing definitions to the AnsibleModule class +class AnsibleModule(AnsibleModuleBase): + params: dict + + +class AnsibleHCloud: + represent: str + + module: AnsibleModule + + def __init__(self, module: AnsibleModule): + if not self.represent: + raise NotImplementedError(f"represent property is not defined for {self.__class__.__name__}") + self.module = module - self.represent = represent self.result = {"changed": False, self.represent: None} - if not HAS_HCLOUD: - module.fail_json(msg=missing_required_lib("hcloud-python")) + + try: + client_check_required_lib() + except ClientException as exception: + module.fail_json(msg=to_native(exception)) + self._build_client() - def _build_client(self): - self.client = hcloud.Client( + def fail_json_hcloud( + self, + exception: HCloudException, + msg: str | None = None, + params: Any = None, + **kwargs, + ) -> NoReturn: + last_traceback = traceback.format_exc() + + failure = {} + + if params is not None: + failure["params"] = params + + if isinstance(exception, APIException): + failure["message"] = exception.message + failure["code"] = exception.code + failure["details"] = exception.details + + elif isinstance(exception, ActionException): + failure["action"] = {k: getattr(exception.action, k) for k in exception.action.__slots__} + + exception_message = to_native(exception) + if msg is not None: + msg = f"{exception_message}: {msg}" + else: + msg = exception_message + + self.module.fail_json(msg=msg, exception=last_traceback, failure=failure, **kwargs) + + def _build_client(self) -> None: + self.client = Client( token=self.module.params["api_token"], - api_endpoint=self.module.params["endpoint"], + api_endpoint=self.module.params["api_endpoint"], application_name="ansible-module", - application_version=__version__, + application_version=version, ) - def _mark_as_changed(self): + def _client_get_by_name_or_id(self, resource: str, param: str | int): + """ + Get a resource by name, and if not found by its ID. + + :param resource: Name of the resource client that implements both `get_by_name` and `get_by_id` methods + :param param: Name or ID of the resource to query + """ + try: + return client_get_by_name_or_id(self.client, resource, param) + except ClientException as exception: + self.module.fail_json(msg=to_native(exception)) + + def _mark_as_changed(self) -> None: self.result["changed"] = True - @staticmethod - def base_module_arguments(): + @classmethod + def base_module_arguments(cls): return { "api_token": { "type": "str", @@ -47,17 +103,19 @@ class Hcloud(object): "fallback": (env_fallback, ["HCLOUD_TOKEN"]), "no_log": True, }, - "endpoint": {"type": "str", "default": "https://api.hetzner.cloud/v1"}, + "api_endpoint": { + "type": "str", + "fallback": (env_fallback, ["HCLOUD_ENDPOINT"]), + "default": "https://api.hetzner.cloud/v1", + "aliases": ["endpoint"], + }, } - def _prepare_result(self): - """Prepare the result for every module - - :return: dict - """ + def _prepare_result(self) -> dict[str, Any]: + """Prepare the result for every module""" return {} - def get_result(self): + def get_result(self) -> dict[str, Any]: if getattr(self, self.represent) is not None: self.result[self.represent] = self._prepare_result() return self.result diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/__init__.py diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/__init__.py new file mode 100644 index 000000000..6b69b1699 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/__init__.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from ._client import Client as Client # noqa pylint: disable=C0414 +from ._exceptions import ( # noqa pylint: disable=C0414 + APIException as APIException, + HCloudException as HCloudException, +) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_client.py new file mode 100644 index 000000000..257d361c7 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_client.py @@ -0,0 +1,236 @@ +from __future__ import annotations + +import time +from typing import NoReturn + +try: + import requests +except ImportError: + requests = None + +from ._version import VERSION +from ._exceptions import APIException +from .actions import ActionsClient +from .certificates import CertificatesClient +from .datacenters import DatacentersClient +from .firewalls import FirewallsClient +from .floating_ips import FloatingIPsClient +from .images import ImagesClient +from .isos import IsosClient +from .load_balancer_types import LoadBalancerTypesClient +from .load_balancers import LoadBalancersClient +from .locations import LocationsClient +from .networks import NetworksClient +from .placement_groups import PlacementGroupsClient +from .primary_ips import PrimaryIPsClient +from .server_types import ServerTypesClient +from .servers import ServersClient +from .ssh_keys import SSHKeysClient +from .volumes import VolumesClient + + +class Client: + """Base Client for accessing the Hetzner Cloud API""" + + _version = VERSION + _retry_wait_time = 0.5 + __user_agent_prefix = "hcloud-python" + + def __init__( + self, + token: str, + api_endpoint: str = "https://api.hetzner.cloud/v1", + application_name: str | None = None, + application_version: str | None = None, + poll_interval: int = 1, + timeout: float | tuple[float, float] | None = None, + ): + """Create a new Client instance + + :param token: Hetzner Cloud API token + :param api_endpoint: Hetzner Cloud API endpoint + :param application_name: Your application name + :param application_version: Your application _version + :param poll_interval: Interval for polling information from Hetzner Cloud API in seconds + :param timeout: Requests timeout in seconds + """ + self.token = token + self._api_endpoint = api_endpoint + self._application_name = application_name + self._application_version = application_version + self._requests_session = requests.Session() + self._requests_timeout = timeout + self.poll_interval = poll_interval + + self.datacenters = DatacentersClient(self) + """DatacentersClient Instance + + :type: :class:`DatacentersClient <hcloud.datacenters.client.DatacentersClient>` + """ + self.locations = LocationsClient(self) + """LocationsClient Instance + + :type: :class:`LocationsClient <hcloud.locations.client.LocationsClient>` + """ + self.servers = ServersClient(self) + """ServersClient Instance + + :type: :class:`ServersClient <hcloud.servers.client.ServersClient>` + """ + self.server_types = ServerTypesClient(self) + """ServerTypesClient Instance + + :type: :class:`ServerTypesClient <hcloud.server_types.client.ServerTypesClient>` + """ + self.volumes = VolumesClient(self) + """VolumesClient Instance + + :type: :class:`VolumesClient <hcloud.volumes.client.VolumesClient>` + """ + self.actions = ActionsClient(self) + """ActionsClient Instance + + :type: :class:`ActionsClient <hcloud.actions.client.ActionsClient>` + """ + self.images = ImagesClient(self) + """ImagesClient Instance + + :type: :class:`ImagesClient <hcloud.images.client.ImagesClient>` + """ + self.isos = IsosClient(self) + """ImagesClient Instance + + :type: :class:`IsosClient <hcloud.isos.client.IsosClient>` + """ + self.ssh_keys = SSHKeysClient(self) + """SSHKeysClient Instance + + :type: :class:`SSHKeysClient <hcloud.ssh_keys.client.SSHKeysClient>` + """ + self.floating_ips = FloatingIPsClient(self) + """FloatingIPsClient Instance + + :type: :class:`FloatingIPsClient <hcloud.floating_ips.client.FloatingIPsClient>` + """ + self.primary_ips = PrimaryIPsClient(self) + """PrimaryIPsClient Instance + + :type: :class:`PrimaryIPsClient <hcloud.primary_ips.client.PrimaryIPsClient>` + """ + self.networks = NetworksClient(self) + """NetworksClient Instance + + :type: :class:`NetworksClient <hcloud.networks.client.NetworksClient>` + """ + self.certificates = CertificatesClient(self) + """CertificatesClient Instance + + :type: :class:`CertificatesClient <hcloud.certificates.client.CertificatesClient>` + """ + + self.load_balancers = LoadBalancersClient(self) + """LoadBalancersClient Instance + + :type: :class:`LoadBalancersClient <hcloud.load_balancers.client.LoadBalancersClient>` + """ + + self.load_balancer_types = LoadBalancerTypesClient(self) + """LoadBalancerTypesClient Instance + + :type: :class:`LoadBalancerTypesClient <hcloud.load_balancer_types.client.LoadBalancerTypesClient>` + """ + + self.firewalls = FirewallsClient(self) + """FirewallsClient Instance + + :type: :class:`FirewallsClient <hcloud.firewalls.client.FirewallsClient>` + """ + + self.placement_groups = PlacementGroupsClient(self) + """PlacementGroupsClient Instance + + :type: :class:`PlacementGroupsClient <hcloud.placement_groups.client.PlacementGroupsClient>` + """ + + def _get_user_agent(self) -> str: + """Get the user agent of the hcloud-python instance with the user application name (if specified) + + :return: The user agent of this hcloud-python instance + """ + user_agents = [] + for name, version in [ + (self._application_name, self._application_version), + (self.__user_agent_prefix, self._version), + ]: + if name is not None: + user_agents.append(name if version is None else f"{name}/{version}") + + return " ".join(user_agents) + + def _get_headers(self) -> dict: + headers = { + "User-Agent": self._get_user_agent(), + "Authorization": f"Bearer {self.token}", + } + return headers + + def _raise_exception_from_response(self, response) -> NoReturn: + raise APIException( + code=response.status_code, + message=response.reason, + details={"content": response.content}, + ) + + def _raise_exception_from_content(self, content: dict) -> NoReturn: + raise APIException( + code=content["error"]["code"], + message=content["error"]["message"], + details=content["error"]["details"], + ) + + def request( # type: ignore[no-untyped-def] + self, + method: str, + url: str, + tries: int = 1, + **kwargs, + ) -> dict: + """Perform a request to the Hetzner Cloud API, wrapper around requests.request + + :param method: HTTP Method to perform the Request + :param url: URL of the Endpoint + :param tries: Tries of the request (used internally, should not be set by the user) + :param timeout: Requests timeout in seconds + :return: Response + """ + timeout = kwargs.pop("timeout", self._requests_timeout) + + response = self._requests_session.request( + method=method, + url=self._api_endpoint + url, + headers=self._get_headers(), + timeout=timeout, + **kwargs, + ) + + content = response.content + try: + if len(content) > 0: + content = response.json() + except (TypeError, ValueError): + self._raise_exception_from_response(response) + + if not response.ok: + if content: + assert isinstance(content, dict) + if content["error"]["code"] == "rate_limit_exceeded" and tries < 5: + time.sleep(tries * self._retry_wait_time) + tries = tries + 1 + return self.request(method, url, tries, **kwargs) + + self._raise_exception_from_content(content) + else: + self._raise_exception_from_response(response) + + # TODO: return an empty dict instead of an empty string when content == "". + return content # type: ignore[return-value] diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_exceptions.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_exceptions.py new file mode 100644 index 000000000..877083f87 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_exceptions.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from typing import Any + + +class HCloudException(Exception): + """There was an error while using the hcloud library""" + + +class APIException(HCloudException): + """There was an error while performing an API Request""" + + def __init__(self, code: int | str, message: str | None, details: Any): + super().__init__(code if message is None and isinstance(code, str) else message) + self.code = code + self.message = message + self.details = details diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_version.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_version.py new file mode 100644 index 000000000..e03c1b434 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/_version.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +VERSION = "1.33.2" # x-release-please-version diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/actions/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/actions/__init__.py new file mode 100644 index 000000000..ca93c89f5 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/actions/__init__.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + ActionsClient, + ActionsPageResult, + BoundAction, + ResourceActionsClient, +) +from .domain import ( # noqa: F401 + Action, + ActionException, + ActionFailedException, + ActionTimeoutException, +) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/actions/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/actions/client.py new file mode 100644 index 000000000..a188f6247 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/actions/client.py @@ -0,0 +1,166 @@ +from __future__ import annotations + +import time +import warnings +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import Action, ActionFailedException, ActionTimeoutException + +if TYPE_CHECKING: + from .._client import Client + + +class BoundAction(BoundModelBase, Action): + _client: ActionsClient + + model = Action + + def wait_until_finished(self, max_retries: int = 100) -> None: + """Wait until the specific action has status="finished" (set Client.poll_interval to specify a delay between checks) + + :param max_retries: int + Specify how many retries will be performed before an ActionTimeoutException will be raised + :raises: ActionFailedException when action is finished with status=="error" + :raises: ActionTimeoutException when Action is still in "running" state after max_retries reloads. + """ + while self.status == Action.STATUS_RUNNING: + if max_retries > 0: + self.reload() + # pylint: disable=protected-access + time.sleep(self._client._client.poll_interval) + max_retries = max_retries - 1 + else: + raise ActionTimeoutException(action=self) + + if self.status == Action.STATUS_ERROR: + raise ActionFailedException(action=self) + + +class ActionsPageResult(NamedTuple): + actions: list[BoundAction] + meta: Meta | None + + +class ResourceActionsClient(ClientEntityBase): + _resource: str + + def __init__(self, client: Client, resource: str | None): + super().__init__(client) + self._resource = resource or "" + + def get_by_id(self, id: int) -> BoundAction: + """Get a specific action by its ID. + + :param id: int + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"{self._resource}/actions/{id}", + method="GET", + ) + return BoundAction(self._client.actions, response["action"]) + + def get_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Get a list of actions. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `command` `status` `progress` `started` `finished` . You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default) + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if status is not None: + params["status"] = status + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url=f"{self._resource}/actions", + method="GET", + params=params, + ) + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + return ActionsPageResult(actions, Meta.parse_meta(response)) + + def get_all( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Get all actions. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `command` `status` `progress` `started` `finished` . You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default) + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._iter_pages(self.get_list, status=status, sort=sort) + + +class ActionsClient(ResourceActionsClient): + def __init__(self, client: Client): + super().__init__(client, None) + + def get_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """ + .. deprecated:: 1.28 + Use :func:`client.<resource>.actions.get_list` instead, + e.g. using :attr:`hcloud.certificates.client.CertificatesClient.actions`. + + `Starting 1 October 2023, it will no longer be available. <https://docs.hetzner.cloud/changelog#2023-07-20-actions-list-endpoint-is-deprecated>`_ + """ + warnings.warn( + "The 'client.actions.get_list' method is deprecated, please use the " + "'client.<resource>.actions.get_list' method instead (e.g. " + "'client.certificates.actions.get_list').", + DeprecationWarning, + stacklevel=2, + ) + return super().get_list(status=status, sort=sort, page=page, per_page=per_page) + + def get_all( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """ + .. deprecated:: 1.28 + Use :func:`client.<resource>.actions.get_all` instead, + e.g. using :attr:`hcloud.certificates.client.CertificatesClient.actions`. + + `Starting 1 October 2023, it will no longer be available. <https://docs.hetzner.cloud/changelog#2023-07-20-actions-list-endpoint-is-deprecated>`_ + """ + warnings.warn( + "The 'client.actions.get_all' method is deprecated, please use the " + "'client.<resource>.actions.get_all' method instead (e.g. " + "'client.certificates.actions.get_all').", + DeprecationWarning, + stacklevel=2, + ) + return super().get_all(status=status, sort=sort) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/actions/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/actions/domain.py new file mode 100644 index 000000000..16b74ac68 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/actions/domain.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from .._exceptions import HCloudException +from ..core import BaseDomain + +if TYPE_CHECKING: + from .client import BoundAction + + +class Action(BaseDomain): + """Action Domain + + :param id: int ID of an action + :param command: Command executed in the action + :param status: Status of the action + :param progress: Progress of action in percent + :param started: Point in time when the action was started + :param datetime,None finished: Point in time when the action was finished. Only set if the action is finished otherwise None + :param resources: Resources the action relates to + :param error: Error message for the action if error occurred, otherwise None. + """ + + STATUS_RUNNING = "running" + """Action Status running""" + STATUS_SUCCESS = "success" + """Action Status success""" + STATUS_ERROR = "error" + """Action Status error""" + + __slots__ = ( + "id", + "command", + "status", + "progress", + "resources", + "error", + "started", + "finished", + ) + + def __init__( + self, + id: int, + command: str | None = None, + status: str | None = None, + progress: int | None = None, + started: str | None = None, + finished: str | None = None, + resources: list[dict] | None = None, + error: dict | None = None, + ): + self.id = id + self.command = command + + self.status = status + self.progress = progress + self.started = isoparse(started) if started else None + self.finished = isoparse(finished) if finished else None + self.resources = resources + self.error = error + + +class ActionException(HCloudException): + """A generic action exception""" + + def __init__(self, action: Action | BoundAction): + assert self.__doc__ is not None + message = self.__doc__ + if action.error is not None and "message" in action.error: + message += f": {action.error['message']}" + + super().__init__(message) + self.message = message + self.action = action + + +class ActionFailedException(ActionException): + """The pending action failed""" + + +class ActionTimeoutException(ActionException): + """The pending action timed out""" diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/certificates/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/certificates/__init__.py new file mode 100644 index 000000000..4e63df576 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/certificates/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundCertificate, + CertificatesClient, + CertificatesPageResult, +) +from .domain import ( # noqa: F401 + Certificate, + CreateManagedCertificateResponse, + ManagedCertificateError, + ManagedCertificateStatus, +) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/certificates/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/certificates/client.py new file mode 100644 index 000000000..a5fe1d77f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/certificates/client.py @@ -0,0 +1,371 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import ( + Certificate, + CreateManagedCertificateResponse, + ManagedCertificateError, + ManagedCertificateStatus, +) + +if TYPE_CHECKING: + from .._client import Client + + +class BoundCertificate(BoundModelBase, Certificate): + _client: CertificatesClient + + model = Certificate + + def __init__(self, client: CertificatesClient, data: dict, complete: bool = True): + status = data.get("status") + if status is not None: + error_data = status.get("error") + error = None + if error_data: + error = ManagedCertificateError( + code=error_data["code"], message=error_data["message"] + ) + data["status"] = ManagedCertificateStatus( + issuance=status["issuance"], renewal=status["renewal"], error=error + ) + super().__init__(client, data, complete) + + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a Certificate. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + return self._client.get_actions_list(self, status, sort, page, per_page) + + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a Certificate. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.get_actions(self, status, sort) + + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundCertificate: + """Updates an certificate. You can update an certificate name and the certificate labels. + + :param name: str (optional) + New name to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` + """ + return self._client.update(self, name, labels) + + def delete(self) -> bool: + """Deletes a certificate. + :return: boolean + """ + return self._client.delete(self) + + def retry_issuance(self) -> BoundAction: + """Retry a failed Certificate issuance or renewal. + :return: BoundAction + """ + return self._client.retry_issuance(self) + + +class CertificatesPageResult(NamedTuple): + certificates: list[BoundCertificate] + meta: Meta | None + + +class CertificatesClient(ClientEntityBase): + _client: Client + + actions: ResourceActionsClient + """Certificates scoped actions client + + :type: :class:`ResourceActionsClient <hcloud.actions.client.ResourceActionsClient>` + """ + + def __init__(self, client: Client): + super().__init__(client) + self.actions = ResourceActionsClient(client, "/certificates") + + def get_by_id(self, id: int) -> BoundCertificate: + """Get a specific certificate by its ID. + + :param id: int + :return: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` + """ + response = self._client.request(url=f"/certificates/{id}", method="GET") + return BoundCertificate(self, response["certificate"]) + + def get_list( + self, + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> CertificatesPageResult: + """Get a list of certificates + + :param name: str (optional) + Can be used to filter certificates by their name. + :param label_selector: str (optional) + Can be used to filter certificates by labels. The response will only contain certificates matching the label selector. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + + if label_selector is not None: + params["label_selector"] = label_selector + + if page is not None: + params["page"] = page + + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url="/certificates", method="GET", params=params + ) + + certificates = [ + BoundCertificate(self, certificate_data) + for certificate_data in response["certificates"] + ] + + return CertificatesPageResult(certificates, Meta.parse_meta(response)) + + def get_all( + self, + name: str | None = None, + label_selector: str | None = None, + ) -> list[BoundCertificate]: + """Get all certificates + + :param name: str (optional) + Can be used to filter certificates by their name. + :param label_selector: str (optional) + Can be used to filter certificates by labels. The response will only contain certificates matching the label selector. + :return: List[:class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>`] + """ + return self._iter_pages(self.get_list, name=name, label_selector=label_selector) + + def get_by_name(self, name: str) -> BoundCertificate | None: + """Get certificate by name + + :param name: str + Used to get certificate by name. + :return: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` + """ + return self._get_first_by(name=name) + + def create( + self, + name: str, + certificate: str, + private_key: str, + labels: dict[str, str] | None = None, + ) -> BoundCertificate: + """Creates a new Certificate with the given name, certificate and private_key. This methods allows only creating + custom uploaded certificates. If you want to create a managed certificate use :func:`~hcloud.certificates.client.CertificatesClient.create_managed` + + :param name: str + :param certificate: str + Certificate and chain in PEM format, in order so that each record directly certifies the one preceding + :param private_key: str + Certificate key in PEM format + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` + """ + data: dict[str, Any] = { + "name": name, + "certificate": certificate, + "private_key": private_key, + "type": Certificate.TYPE_UPLOADED, + } + if labels is not None: + data["labels"] = labels + response = self._client.request(url="/certificates", method="POST", json=data) + return BoundCertificate(self, response["certificate"]) + + def create_managed( + self, + name: str, + domain_names: list[str], + labels: dict[str, str] | None = None, + ) -> CreateManagedCertificateResponse: + """Creates a new managed Certificate with the given name and domain names. This methods allows only creating + managed certificates for domains that are using the Hetzner DNS service. If you want to create a custom uploaded certificate use :func:`~hcloud.certificates.client.CertificatesClient.create` + + :param name: str + :param domain_names: List[str] + Domains and subdomains that should be contained in the Certificate + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` + """ + data: dict[str, Any] = { + "name": name, + "type": Certificate.TYPE_MANAGED, + "domain_names": domain_names, + } + if labels is not None: + data["labels"] = labels + response = self._client.request(url="/certificates", method="POST", json=data) + return CreateManagedCertificateResponse( + certificate=BoundCertificate(self, response["certificate"]), + action=BoundAction(self._client.actions, response["action"]), + ) + + def update( + self, + certificate: Certificate | BoundCertificate, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundCertificate: + """Updates a Certificate. You can update a certificate name and labels. + + :param certificate: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` or :class:`Certificate <hcloud.certificates.domain.Certificate>` + :param name: str (optional) + New name to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` + """ + data: dict[str, Any] = {} + if name is not None: + data["name"] = name + if labels is not None: + data["labels"] = labels + response = self._client.request( + url=f"/certificates/{certificate.id}", + method="PUT", + json=data, + ) + return BoundCertificate(self, response["certificate"]) + + def delete(self, certificate: Certificate | BoundCertificate) -> bool: + """Deletes a certificate. + + :param certificate: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` or :class:`Certificate <hcloud.certificates.domain.Certificate>` + :return: True + """ + self._client.request( + url=f"/certificates/{certificate.id}", + method="DELETE", + ) + # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised + return True + + def get_actions_list( + self, + certificate: Certificate | BoundCertificate, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a Certificate. + + :param certificate: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` or :class:`Certificate <hcloud.certificates.domain.Certificate>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if status is not None: + params["status"] = status + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url=f"/certificates/{certificate.id}/actions", + method="GET", + params=params, + ) + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + return ActionsPageResult(actions, Meta.parse_meta(response)) + + def get_actions( + self, + certificate: Certificate | BoundCertificate, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a Certificate. + + :param certificate: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` or :class:`Certificate <hcloud.certificates.domain.Certificate>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._iter_pages( + self.get_actions_list, + certificate, + status=status, + sort=sort, + ) + + def retry_issuance( + self, + certificate: Certificate | BoundCertificate, + ) -> BoundAction: + """Returns all action objects for a Certificate. + + :param certificate: :class:`BoundCertificate <hcloud.certificates.client.BoundCertificate>` or :class:`Certificate <hcloud.certificates.domain.Certificate>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/certificates/{certificate.id}/actions/retry", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/certificates/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/certificates/domain.py new file mode 100644 index 000000000..c09c288d3 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/certificates/domain.py @@ -0,0 +1,133 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain, DomainIdentityMixin + +if TYPE_CHECKING: + from ..actions import BoundAction + from .client import BoundCertificate + + +class Certificate(BaseDomain, DomainIdentityMixin): + """Certificate Domain + + :param id: int ID of Certificate + :param name: str Name of Certificate + :param certificate: str Certificate and chain in PEM format, in order so that each record directly certifies the one preceding + :param not_valid_before: datetime + Point in time when the Certificate becomes valid + :param not_valid_after: datetime + Point in time when the Certificate becomes invalid + :param domain_names: List[str] List of domains and subdomains covered by this certificate + :param fingerprint: str Fingerprint of the Certificate + :param labels: dict + User-defined labels (key-value pairs) + :param created: datetime + Point in time when the certificate was created + :param type: str Type of Certificate + :param status: ManagedCertificateStatus Current status of a type managed Certificate, always none for type uploaded Certificates + """ + + __slots__ = ( + "id", + "name", + "certificate", + "not_valid_before", + "not_valid_after", + "domain_names", + "fingerprint", + "created", + "labels", + "type", + "status", + ) + TYPE_UPLOADED = "uploaded" + TYPE_MANAGED = "managed" + + def __init__( + self, + id: int | None = None, + name: str | None = None, + certificate: str | None = None, + not_valid_before: str | None = None, + not_valid_after: str | None = None, + domain_names: list[str] | None = None, + fingerprint: str | None = None, + created: str | None = None, + labels: dict[str, str] | None = None, + type: str | None = None, + status: ManagedCertificateStatus | None = None, + ): + self.id = id + self.name = name + self.type = type + self.certificate = certificate + self.domain_names = domain_names + self.fingerprint = fingerprint + self.not_valid_before = isoparse(not_valid_before) if not_valid_before else None + self.not_valid_after = isoparse(not_valid_after) if not_valid_after else None + self.created = isoparse(created) if created else None + self.labels = labels + self.status = status + + +class ManagedCertificateStatus(BaseDomain): + """ManagedCertificateStatus Domain + + :param issuance: str + Status of the issuance process of the Certificate + :param renewal: str + Status of the renewal process of the Certificate + :param error: ManagedCertificateError + If issuance or renewal reports failure, this property contains information about what happened + """ + + def __init__( + self, + issuance: str | None = None, + renewal: str | None = None, + error: ManagedCertificateError | None = None, + ): + self.issuance = issuance + self.renewal = renewal + self.error = error + + +class ManagedCertificateError(BaseDomain): + """ManagedCertificateError Domain + + :param code: str + Error code identifying the error + :param message: + Message detailing the error + """ + + def __init__(self, code: str | None = None, message: str | None = None): + self.code = code + self.message = message + + +class CreateManagedCertificateResponse(BaseDomain): + """Create Managed Certificate Response Domain + + :param certificate: :class:`BoundCertificate <hcloud.certificate.client.BoundCertificate>` + The created server + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + Shows the progress of the certificate creation + """ + + __slots__ = ("certificate", "action") + + def __init__( + self, + certificate: BoundCertificate, + action: BoundAction, + ): + self.certificate = certificate + self.action = action diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/__init__.py new file mode 100644 index 000000000..4e17dac97 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundModelBase, ClientEntityBase # noqa: F401 +from .domain import BaseDomain, DomainIdentityMixin, Meta, Pagination # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/client.py new file mode 100644 index 000000000..d213daf00 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/client.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Callable + +if TYPE_CHECKING: + from .._client import Client + + +class ClientEntityBase: + _client: Client + + max_per_page: int = 50 + + def __init__(self, client: Client): + """ + :param client: Client + :return self + """ + self._client = client + + def _iter_pages( # type: ignore[no-untyped-def] + self, + list_function: Callable, + *args, + **kwargs, + ) -> list: + results = [] + + page = 1 + while page: + # The *PageResult tuples MUST have the following structure + # `(result: List[Bound*], meta: Meta)` + result, meta = list_function( + *args, page=page, per_page=self.max_per_page, **kwargs + ) + if result: + results.extend(result) + + if meta and meta.pagination and meta.pagination.next_page: + page = meta.pagination.next_page + else: + page = 0 + + return results + + def _get_first_by(self, **kwargs): # type: ignore[no-untyped-def] + assert hasattr(self, "get_list") + # pylint: disable=no-member + entities, _ = self.get_list(**kwargs) + return entities[0] if entities else None + + +class BoundModelBase: + """Bound Model Base""" + + model: Any + + def __init__( + self, + client: ClientEntityBase, + data: dict, + complete: bool = True, + ): + """ + :param client: + The client for the specific model to use + :param data: + The data of the model + :param complete: bool + False if not all attributes of the model fetched + """ + self._client = client + self.complete = complete + self.data_model = self.model.from_dict(data) + + def __getattr__(self, name: str): # type: ignore[no-untyped-def] + """Allow magical access to the properties of the model + :param name: str + :return: + """ + value = getattr(self.data_model, name) + if not value and not self.complete: + self.reload() + value = getattr(self.data_model, name) + return value + + def reload(self) -> None: + """Reloads the model and tries to get all data from the APIx""" + assert hasattr(self._client, "get_by_id") + bound_model = self._client.get_by_id(self.data_model.id) + self.data_model = bound_model.data_model + self.complete = True + + def __repr__(self) -> str: + # Override and reset hcloud.core.domain.BaseDomain.__repr__ method for bound + # models, as they will generate a lot of API call trying to print all the fields + # of the model. + return object.__repr__(self) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/domain.py new file mode 100644 index 000000000..692f7488b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/core/domain.py @@ -0,0 +1,84 @@ +from __future__ import annotations + + +class BaseDomain: + __slots__ = () + + @classmethod + def from_dict(cls, data: dict): # type: ignore[no-untyped-def] + """ + Build the domain object from the data dict. + """ + supported_data = {k: v for k, v in data.items() if k in cls.__slots__} + return cls(**supported_data) + + def __repr__(self) -> str: + kwargs = [f"{key}={getattr(self, key)!r}" for key in self.__slots__] # type: ignore[var-annotated] + return f"{self.__class__.__qualname__}({', '.join(kwargs)})" + + +class DomainIdentityMixin: + __slots__ = () + + id: int | None + name: str | None + + @property + def id_or_name(self) -> int | str: + """ + Return the first defined value, and fails if none is defined. + """ + if self.id is not None: + return self.id + if self.name is not None: + return self.name + raise ValueError("id or name must be set") + + +class Pagination(BaseDomain): + __slots__ = ( + "page", + "per_page", + "previous_page", + "next_page", + "last_page", + "total_entries", + ) + + def __init__( + self, + page: int, + per_page: int, + previous_page: int | None = None, + next_page: int | None = None, + last_page: int | None = None, + total_entries: int | None = None, + ): + self.page = page + self.per_page = per_page + self.previous_page = previous_page + self.next_page = next_page + self.last_page = last_page + self.total_entries = total_entries + + +class Meta(BaseDomain): + __slots__ = ("pagination",) + + def __init__(self, pagination: Pagination | None = None): + self.pagination = pagination + + @classmethod + def parse_meta(cls, response: dict) -> Meta | None: + """ + If present, extract the meta details from the response and return a meta object. + """ + meta = None + if response and "meta" in response: + meta = cls() + try: + meta.pagination = Pagination(**response["meta"]["pagination"]) + except KeyError: + pass + + return meta diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/datacenters/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/datacenters/__init__.py new file mode 100644 index 000000000..559694c06 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/datacenters/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundDatacenter, + DatacentersClient, + DatacentersPageResult, +) +from .domain import Datacenter, DatacenterServerTypes # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/datacenters/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/datacenters/client.py new file mode 100644 index 000000000..1be1e126e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/datacenters/client.py @@ -0,0 +1,121 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..locations import BoundLocation +from ..server_types import BoundServerType +from .domain import Datacenter, DatacenterServerTypes + +if TYPE_CHECKING: + from .._client import Client + + +class BoundDatacenter(BoundModelBase, Datacenter): + _client: DatacentersClient + + model = Datacenter + + def __init__(self, client: DatacentersClient, data: dict): + location = data.get("location") + if location is not None: + data["location"] = BoundLocation(client._client.locations, location) + + server_types = data.get("server_types") + if server_types is not None: + available = [ + BoundServerType( + client._client.server_types, {"id": server_type}, complete=False + ) + for server_type in server_types["available"] + ] + supported = [ + BoundServerType( + client._client.server_types, {"id": server_type}, complete=False + ) + for server_type in server_types["supported"] + ] + available_for_migration = [ + BoundServerType( + client._client.server_types, {"id": server_type}, complete=False + ) + for server_type in server_types["available_for_migration"] + ] + data["server_types"] = DatacenterServerTypes( + available=available, + supported=supported, + available_for_migration=available_for_migration, + ) + + super().__init__(client, data) + + +class DatacentersPageResult(NamedTuple): + datacenters: list[BoundDatacenter] + meta: Meta | None + + +class DatacentersClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundDatacenter: + """Get a specific datacenter by its ID. + + :param id: int + :return: :class:`BoundDatacenter <hcloud.datacenters.client.BoundDatacenter>` + """ + response = self._client.request(url=f"/datacenters/{id}", method="GET") + return BoundDatacenter(self, response["datacenter"]) + + def get_list( + self, + name: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> DatacentersPageResult: + """Get a list of datacenters + + :param name: str (optional) + Can be used to filter datacenters by their name. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundDatacenter <hcloud.datacenters.client.BoundDatacenter>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + + if page is not None: + params["page"] = page + + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request(url="/datacenters", method="GET", params=params) + + datacenters = [ + BoundDatacenter(self, datacenter_data) + for datacenter_data in response["datacenters"] + ] + + return DatacentersPageResult(datacenters, Meta.parse_meta(response)) + + def get_all(self, name: str | None = None) -> list[BoundDatacenter]: + """Get all datacenters + + :param name: str (optional) + Can be used to filter datacenters by their name. + :return: List[:class:`BoundDatacenter <hcloud.datacenters.client.BoundDatacenter>`] + """ + return self._iter_pages(self.get_list, name=name) + + def get_by_name(self, name: str) -> BoundDatacenter | None: + """Get datacenter by name + + :param name: str + Used to get datacenter by name. + :return: :class:`BoundDatacenter <hcloud.datacenters.client.BoundDatacenter>` + """ + return self._get_first_by(name=name) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/datacenters/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/datacenters/domain.py new file mode 100644 index 000000000..05d5f793f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/datacenters/domain.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ..core import BaseDomain, DomainIdentityMixin + +if TYPE_CHECKING: + from ..locations import Location + from ..server_types import BoundServerType + + +class Datacenter(BaseDomain, DomainIdentityMixin): + """Datacenter Domain + + :param id: int ID of Datacenter + :param name: str Name of Datacenter + :param description: str Description of Datacenter + :param location: :class:`BoundLocation <hcloud.locations.client.BoundLocation>` + :param server_types: :class:`DatacenterServerTypes <hcloud.datacenters.domain.DatacenterServerTypes>` + """ + + __slots__ = ("id", "name", "description", "location", "server_types") + + def __init__( + self, + id: int | None = None, + name: str | None = None, + description: str | None = None, + location: Location | None = None, + server_types: DatacenterServerTypes | None = None, + ): + self.id = id + self.name = name + self.description = description + self.location = location + self.server_types = server_types + + +class DatacenterServerTypes(BaseDomain): + """DatacenterServerTypes Domain + + :param available: List[:class:`BoundServerTypes <hcloud.server_types.client.BoundServerTypes>`] + All available server types for this datacenter + :param supported: List[:class:`BoundServerTypes <hcloud.server_types.client.BoundServerTypes>`] + All supported server types for this datacenter + :param available_for_migration: List[:class:`BoundServerTypes <hcloud.server_types.client.BoundServerTypes>`] + All available for migration (change type) server types for this datacenter + """ + + __slots__ = ("available", "supported", "available_for_migration") + + def __init__( + self, + available: list[BoundServerType], + supported: list[BoundServerType], + available_for_migration: list[BoundServerType], + ): + self.available = available + self.supported = supported + self.available_for_migration = available_for_migration diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/deprecation/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/deprecation/__init__.py new file mode 100644 index 000000000..315576b11 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/deprecation/__init__.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +from .domain import DeprecationInfo # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/deprecation/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/deprecation/domain.py new file mode 100644 index 000000000..b79e70943 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/deprecation/domain.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + + +class DeprecationInfo(BaseDomain): + """Describes if, when & how the resources was deprecated. If this field is set to ``None`` the resource is not + deprecated. If it has a value, it is considered deprecated. + + :param announced: datetime + Date of when the deprecation was announced. + :param unavailable_after: datetime + After the time in this field, the resource will not be available from the general listing endpoint of the + resource type, and it can not be used in new resources. For example, if this is an image, you can not create + new servers with this image after the mentioned date. + """ + + __slots__ = ( + "announced", + "unavailable_after", + ) + + def __init__( + self, + announced: str | None = None, + unavailable_after: str | None = None, + ): + self.announced = isoparse(announced) if announced else None + self.unavailable_after = ( + isoparse(unavailable_after) if unavailable_after else None + ) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/__init__.py new file mode 100644 index 000000000..5205d7664 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/__init__.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from .client import BoundFirewall, FirewallsClient, FirewallsPageResult # noqa: F401 +from .domain import ( # noqa: F401 + CreateFirewallResponse, + Firewall, + FirewallResource, + FirewallResourceAppliedToResources, + FirewallResourceLabelSelector, + FirewallRule, +) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/client.py new file mode 100644 index 000000000..fbcd10080 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/client.py @@ -0,0 +1,501 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import ( + CreateFirewallResponse, + Firewall, + FirewallResource, + FirewallResourceAppliedToResources, + FirewallResourceLabelSelector, + FirewallRule, +) + +if TYPE_CHECKING: + from .._client import Client + + +class BoundFirewall(BoundModelBase, Firewall): + _client: FirewallsClient + + model = Firewall + + def __init__(self, client: FirewallsClient, data: dict, complete: bool = True): + rules = data.get("rules", []) + if rules: + rules = [ + FirewallRule( + direction=rule["direction"], + source_ips=rule["source_ips"], + destination_ips=rule["destination_ips"], + protocol=rule["protocol"], + port=rule["port"], + description=rule["description"], + ) + for rule in rules + ] + data["rules"] = rules + + applied_to = data.get("applied_to", []) + if applied_to: + # pylint: disable=import-outside-toplevel + from ..servers import BoundServer + + data_applied_to = [] + for firewall_resource in applied_to: + applied_to_resources = None + if firewall_resource.get("applied_to_resources"): + applied_to_resources = [ + FirewallResourceAppliedToResources( + type=resource["type"], + server=( + BoundServer( + client._client.servers, + resource.get("server"), + complete=False, + ) + if resource.get("server") is not None + else None + ), + ) + for resource in firewall_resource.get("applied_to_resources") + ] + + if firewall_resource["type"] == FirewallResource.TYPE_SERVER: + data_applied_to.append( + FirewallResource( + type=firewall_resource["type"], + server=BoundServer( + client._client.servers, + firewall_resource["server"], + complete=False, + ), + applied_to_resources=applied_to_resources, + ) + ) + elif firewall_resource["type"] == FirewallResource.TYPE_LABEL_SELECTOR: + data_applied_to.append( + FirewallResource( + type=firewall_resource["type"], + label_selector=FirewallResourceLabelSelector( + selector=firewall_resource["label_selector"]["selector"] + ), + applied_to_resources=applied_to_resources, + ) + ) + + data["applied_to"] = data_applied_to + + super().__init__(client, data, complete) + + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a Firewall. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + return self._client.get_actions_list(self, status, sort, page, per_page) + + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a Firewall. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.get_actions(self, status, sort) + + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundFirewall: + """Updates the name or labels of a Firewall. + + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param name: str (optional) + New Name to set + :return: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` + """ + return self._client.update(self, labels, name) + + def delete(self) -> bool: + """Deletes a Firewall. + + :return: boolean + """ + return self._client.delete(self) + + def set_rules(self, rules: list[FirewallRule]) -> list[BoundAction]: + """Sets the rules of a Firewall. All existing rules will be overwritten. Pass an empty rules array to remove all rules. + :param rules: List[:class:`FirewallRule <hcloud.firewalls.domain.FirewallRule>`] + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + + return self._client.set_rules(self, rules) + + def apply_to_resources( + self, + resources: list[FirewallResource], + ) -> list[BoundAction]: + """Applies one Firewall to multiple resources. + :param resources: List[:class:`FirewallResource <hcloud.firewalls.domain.FirewallResource>`] + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.apply_to_resources(self, resources) + + def remove_from_resources( + self, + resources: list[FirewallResource], + ) -> list[BoundAction]: + """Removes one Firewall from multiple resources. + :param resources: List[:class:`FirewallResource <hcloud.firewalls.domain.FirewallResource>`] + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.remove_from_resources(self, resources) + + +class FirewallsPageResult(NamedTuple): + firewalls: list[BoundFirewall] + meta: Meta | None + + +class FirewallsClient(ClientEntityBase): + _client: Client + + actions: ResourceActionsClient + """Firewalls scoped actions client + + :type: :class:`ResourceActionsClient <hcloud.actions.client.ResourceActionsClient>` + """ + + def __init__(self, client: Client): + super().__init__(client) + self.actions = ResourceActionsClient(client, "/firewalls") + + def get_actions_list( + self, + firewall: Firewall | BoundFirewall, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a Firewall. + + :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` or :class:`Firewall <hcloud.firewalls.domain.Firewall>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if status is not None: + params["status"] = status + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + response = self._client.request( + url=f"/firewalls/{firewall.id}/actions", + method="GET", + params=params, + ) + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + return ActionsPageResult(actions, Meta.parse_meta(response)) + + def get_actions( + self, + firewall: Firewall | BoundFirewall, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a Firewall. + + :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` or :class:`Firewall <hcloud.firewalls.domain.Firewall>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._iter_pages( + self.get_actions_list, + firewall, + status=status, + sort=sort, + ) + + def get_by_id(self, id: int) -> BoundFirewall: + """Returns a specific Firewall object. + + :param id: int + :return: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` + """ + response = self._client.request(url=f"/firewalls/{id}", method="GET") + return BoundFirewall(self, response["firewall"]) + + def get_list( + self, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + name: str | None = None, + sort: list[str] | None = None, + ) -> FirewallsPageResult: + """Get a list of floating ips from this account + + :param label_selector: str (optional) + Can be used to filter Firewalls by labels. The response will only contain Firewalls matching the label selector values. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :param name: str (optional) + Can be used to filter networks by their name. + :param sort: List[str] (optional) + Choices: id name created (You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default)) + :return: (List[:class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + + if label_selector is not None: + params["label_selector"] = label_selector + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + if name is not None: + params["name"] = name + if sort is not None: + params["sort"] = sort + response = self._client.request(url="/firewalls", method="GET", params=params) + firewalls = [ + BoundFirewall(self, firewall_data) + for firewall_data in response["firewalls"] + ] + + return FirewallsPageResult(firewalls, Meta.parse_meta(response)) + + def get_all( + self, + label_selector: str | None = None, + name: str | None = None, + sort: list[str] | None = None, + ) -> list[BoundFirewall]: + """Get all floating ips from this account + + :param label_selector: str (optional) + Can be used to filter Firewalls by labels. The response will only contain Firewalls matching the label selector values. + :param name: str (optional) + Can be used to filter networks by their name. + :param sort: List[str] (optional) + Choices: id name created (You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default)) + :return: List[:class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>`] + """ + return self._iter_pages( + self.get_list, + label_selector=label_selector, + name=name, + sort=sort, + ) + + def get_by_name(self, name: str) -> BoundFirewall | None: + """Get Firewall by name + + :param name: str + Used to get Firewall by name. + :return: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` + """ + return self._get_first_by(name=name) + + def create( + self, + name: str, + rules: list[FirewallRule] | None = None, + labels: str | None = None, + resources: list[FirewallResource] | None = None, + ) -> CreateFirewallResponse: + """Creates a new Firewall. + + :param name: str + Firewall Name + :param rules: List[:class:`FirewallRule <hcloud.firewalls.domain.FirewallRule>`] (optional) + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param resources: List[:class:`FirewallResource <hcloud.firewalls.domain.FirewallResource>`] (optional) + :return: :class:`CreateFirewallResponse <hcloud.firewalls.domain.CreateFirewallResponse>` + """ + + data: dict[str, Any] = {"name": name} + if labels is not None: + data["labels"] = labels + + if rules is not None: + data.update({"rules": []}) + for rule in rules: + data["rules"].append(rule.to_payload()) + if resources is not None: + data.update({"apply_to": []}) + for resource in resources: + data["apply_to"].append(resource.to_payload()) + response = self._client.request(url="/firewalls", json=data, method="POST") + + actions = [] + if response.get("actions") is not None: + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + + result = CreateFirewallResponse( + firewall=BoundFirewall(self, response["firewall"]), actions=actions + ) + return result + + def update( + self, + firewall: Firewall | BoundFirewall, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundFirewall: + """Updates the description or labels of a Firewall. + + :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` or :class:`Firewall <hcloud.firewalls.domain.Firewall>` + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param name: str (optional) + New name to set + :return: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` + """ + data: dict[str, Any] = {} + if labels is not None: + data["labels"] = labels + if name is not None: + data["name"] = name + + response = self._client.request( + url=f"/firewalls/{firewall.id}", + method="PUT", + json=data, + ) + return BoundFirewall(self, response["firewall"]) + + def delete(self, firewall: Firewall | BoundFirewall) -> bool: + """Deletes a Firewall. + + :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` or :class:`Firewall <hcloud.firewalls.domain.Firewall>` + :return: boolean + """ + self._client.request( + url=f"/firewalls/{firewall.id}", + method="DELETE", + ) + # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised + return True + + def set_rules( + self, + firewall: Firewall | BoundFirewall, + rules: list[FirewallRule], + ) -> list[BoundAction]: + """Sets the rules of a Firewall. All existing rules will be overwritten. Pass an empty rules array to remove all rules. + + :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` or :class:`Firewall <hcloud.firewalls.domain.Firewall>` + :param rules: List[:class:`FirewallRule <hcloud.firewalls.domain.FirewallRule>`] + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + data: dict[str, Any] = {"rules": []} + for rule in rules: + data["rules"].append(rule.to_payload()) + response = self._client.request( + url=f"/firewalls/{firewall.id}/actions/set_rules", + method="POST", + json=data, + ) + return [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + + def apply_to_resources( + self, + firewall: Firewall | BoundFirewall, + resources: list[FirewallResource], + ) -> list[BoundAction]: + """Applies one Firewall to multiple resources. + + :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` or :class:`Firewall <hcloud.firewalls.domain.Firewall>` + :param resources: List[:class:`FirewallResource <hcloud.firewalls.domain.FirewallResource>`] + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + data: dict[str, Any] = {"apply_to": []} + for resource in resources: + data["apply_to"].append(resource.to_payload()) + response = self._client.request( + url=f"/firewalls/{firewall.id}/actions/apply_to_resources", + method="POST", + json=data, + ) + return [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + + def remove_from_resources( + self, + firewall: Firewall | BoundFirewall, + resources: list[FirewallResource], + ) -> list[BoundAction]: + """Removes one Firewall from multiple resources. + + :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` or :class:`Firewall <hcloud.firewalls.domain.Firewall>` + :param resources: List[:class:`FirewallResource <hcloud.firewalls.domain.FirewallResource>`] + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + data: dict[str, Any] = {"remove_from": []} + for resource in resources: + data["remove_from"].append(resource.to_payload()) + response = self._client.request( + url=f"/firewalls/{firewall.id}/actions/remove_from_resources", + method="POST", + json=data, + ) + return [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/domain.py new file mode 100644 index 000000000..5ce9281d9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/firewalls/domain.py @@ -0,0 +1,221 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..servers import BoundServer, Server + from .client import BoundFirewall + + +class Firewall(BaseDomain): + """Firewall Domain + + :param id: int + ID of the Firewall + :param name: str + Name of the Firewall + :param labels: dict + User-defined labels (key-value pairs) + :param rules: List[:class:`FirewallRule <hcloud.firewalls.domain.FirewallRule>`] + Rules of the Firewall + :param applied_to: List[:class:`FirewallResource <hcloud.firewalls.domain.FirewallResource>`] + Resources currently using the Firewall + :param created: datetime + Point in time when the image was created + """ + + __slots__ = ("id", "name", "labels", "rules", "applied_to", "created") + + def __init__( + self, + id: int | None = None, + name: str | None = None, + labels: dict[str, str] | None = None, + rules: list[FirewallRule] | None = None, + applied_to: list[FirewallResource] | None = None, + created: str | None = None, + ): + self.id = id + self.name = name + self.rules = rules + self.applied_to = applied_to + self.labels = labels + self.created = isoparse(created) if created else None + + +class FirewallRule(BaseDomain): + """Firewall Rule Domain + + :param direction: str + The Firewall which was created + :param port: str + Port to which traffic will be allowed, only applicable for protocols TCP and UDP, specify port ranges by using + - as a indicator, Sample: 80-85 means all ports between 80 & 85 (80, 82, 83, 84, 85) + :param protocol: str + Select traffic direction on which rule should be applied. Use source_ips for direction in and destination_ips for direction out. + :param source_ips: List[str] + List of permitted IPv4/IPv6 addresses in CIDR notation. Use 0.0.0.0/0 to allow all IPv4 addresses and ::/0 to allow all IPv6 addresses. You can specify 100 CIDRs at most. + :param destination_ips: List[str] + List of permitted IPv4/IPv6 addresses in CIDR notation. Use 0.0.0.0/0 to allow all IPv4 addresses and ::/0 to allow all IPv6 addresses. You can specify 100 CIDRs at most. + :param description: str + Short description of the firewall rule + """ + + __slots__ = ( + "direction", + "port", + "protocol", + "source_ips", + "destination_ips", + "description", + ) + + DIRECTION_IN = "in" + """Firewall Rule Direction In""" + DIRECTION_OUT = "out" + """Firewall Rule Direction Out""" + + PROTOCOL_UDP = "udp" + """Firewall Rule Protocol UDP""" + PROTOCOL_ICMP = "icmp" + """Firewall Rule Protocol ICMP""" + PROTOCOL_TCP = "tcp" + """Firewall Rule Protocol TCP""" + PROTOCOL_ESP = "esp" + """Firewall Rule Protocol ESP""" + PROTOCOL_GRE = "gre" + """Firewall Rule Protocol GRE""" + + def __init__( + self, + direction: str, + protocol: str, + source_ips: list[str], + port: str | None = None, + destination_ips: list[str] | None = None, + description: str | None = None, + ): + self.direction = direction + self.port = port + self.protocol = protocol + self.source_ips = source_ips + self.destination_ips = destination_ips or [] + self.description = description + + def to_payload(self) -> dict[str, Any]: + """ + Generates the request payload from this domain object. + """ + payload: dict[str, Any] = { + "direction": self.direction, + "protocol": self.protocol, + "source_ips": self.source_ips, + } + if len(self.destination_ips) > 0: + payload["destination_ips"] = self.destination_ips + if self.port is not None: + payload["port"] = self.port + if self.description is not None: + payload["description"] = self.description + return payload + + +class FirewallResource(BaseDomain): + """Firewall Used By Domain + + :param type: str + Type of resource referenced + :param server: Optional[Server] + Server the Firewall is applied to + :param label_selector: Optional[FirewallResourceLabelSelector] + Label Selector for Servers the Firewall should be applied to + :param applied_to_resources: (read-only) List of effective resources the firewall is + applied to. + """ + + __slots__ = ("type", "server", "label_selector", "applied_to_resources") + + TYPE_SERVER = "server" + """Firewall Used By Type Server""" + TYPE_LABEL_SELECTOR = "label_selector" + """Firewall Used By Type label_selector""" + + def __init__( + self, + type: str, + server: Server | BoundServer | None = None, + label_selector: FirewallResourceLabelSelector | None = None, + applied_to_resources: list[FirewallResourceAppliedToResources] | None = None, + ): + self.type = type + self.server = server + self.label_selector = label_selector + self.applied_to_resources = applied_to_resources + + def to_payload(self) -> dict[str, Any]: + """ + Generates the request payload from this domain object. + """ + payload: dict[str, Any] = {"type": self.type} + if self.server is not None: + payload["server"] = {"id": self.server.id} + + if self.label_selector is not None: + payload["label_selector"] = {"selector": self.label_selector.selector} + return payload + + +class FirewallResourceAppliedToResources(BaseDomain): + """Firewall Resource applied to Domain + + :param type: Type of resource referenced + :param server: Server the Firewall is applied to + """ + + __slots__ = ("type", "server") + + def __init__( + self, + type: str, + server: BoundServer | None = None, + ): + self.type = type + self.server = server + + +class FirewallResourceLabelSelector(BaseDomain): + """FirewallResourceLabelSelector Domain + + :param selector: str Target label selector + """ + + def __init__(self, selector: str | None = None): + self.selector = selector + + +class CreateFirewallResponse(BaseDomain): + """Create Firewall Response Domain + + :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>` + The Firewall which was created + :param actions: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + The Action which shows the progress of the Firewall Creation + """ + + __slots__ = ("firewall", "actions") + + def __init__( + self, + firewall: BoundFirewall, + actions: list[BoundAction] | None, + ): + self.firewall = firewall + self.actions = actions diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/__init__.py new file mode 100644 index 000000000..4e55bf5fb --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundFloatingIP, + FloatingIPsClient, + FloatingIPsPageResult, +) +from .domain import CreateFloatingIPResponse, FloatingIP # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/client.py new file mode 100644 index 000000000..00600e48b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/client.py @@ -0,0 +1,459 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..locations import BoundLocation +from .domain import CreateFloatingIPResponse, FloatingIP + +if TYPE_CHECKING: + from .._client import Client + from ..locations import Location + from ..servers import BoundServer, Server + + +class BoundFloatingIP(BoundModelBase, FloatingIP): + _client: FloatingIPsClient + + model = FloatingIP + + def __init__(self, client: FloatingIPsClient, data: dict, complete: bool = True): + # pylint: disable=import-outside-toplevel + from ..servers import BoundServer + + server = data.get("server") + if server is not None: + data["server"] = BoundServer( + client._client.servers, {"id": server}, complete=False + ) + + home_location = data.get("home_location") + if home_location is not None: + data["home_location"] = BoundLocation( + client._client.locations, home_location + ) + + super().__init__(client, data, complete) + + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a Floating IP. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + return self._client.get_actions_list(self, status, sort, page, per_page) + + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a Floating IP. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.get_actions(self, status, sort) + + def update( + self, + description: str | None = None, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundFloatingIP: + """Updates the description or labels of a Floating IP. + + :param description: str (optional) + New Description to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param name: str (optional) + New Name to set + :return: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` + """ + return self._client.update(self, description, labels, name) + + def delete(self) -> bool: + """Deletes a Floating IP. If it is currently assigned to a server it will automatically get unassigned. + + :return: boolean + """ + return self._client.delete(self) + + def change_protection(self, delete: bool | None = None) -> BoundAction: + """Changes the protection configuration of the Floating IP. + + :param delete: boolean + If true, prevents the Floating IP from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_protection(self, delete) + + def assign(self, server: Server | BoundServer) -> BoundAction: + """Assigns a Floating IP to a server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + Server the Floating IP shall be assigned to + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.assign(self, server) + + def unassign(self) -> BoundAction: + """Unassigns a Floating IP, resulting in it being unreachable. You may assign it to a server again at a later time. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.unassign(self) + + def change_dns_ptr(self, ip: str, dns_ptr: str) -> BoundAction: + """Changes the hostname that will appear when getting the hostname belonging to this Floating IP. + + :param ip: str + The IP address for which to set the reverse DNS entry + :param dns_ptr: str + Hostname to set as a reverse DNS PTR entry, will reset to original default value if `None` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_dns_ptr(self, ip, dns_ptr) + + +class FloatingIPsPageResult(NamedTuple): + floating_ips: list[BoundFloatingIP] + meta: Meta | None + + +class FloatingIPsClient(ClientEntityBase): + _client: Client + + actions: ResourceActionsClient + """Floating IPs scoped actions client + + :type: :class:`ResourceActionsClient <hcloud.actions.client.ResourceActionsClient>` + """ + + def __init__(self, client: Client): + super().__init__(client) + self.actions = ResourceActionsClient(client, "/floating_ips") + + def get_actions_list( + self, + floating_ip: FloatingIP | BoundFloatingIP, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a Floating IP. + + :param floating_ip: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` or :class:`FloatingIP <hcloud.floating_ips.domain.FloatingIP>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if status is not None: + params["status"] = status + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + response = self._client.request( + url=f"/floating_ips/{floating_ip.id}/actions", + method="GET", + params=params, + ) + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + return ActionsPageResult(actions, Meta.parse_meta(response)) + + def get_actions( + self, + floating_ip: FloatingIP | BoundFloatingIP, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a Floating IP. + + :param floating_ip: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` or :class:`FloatingIP <hcloud.floating_ips.domain.FloatingIP>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._iter_pages( + self.get_actions_list, + floating_ip, + status=status, + sort=sort, + ) + + def get_by_id(self, id: int) -> BoundFloatingIP: + """Returns a specific Floating IP object. + + :param id: int + :return: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` + """ + response = self._client.request(url=f"/floating_ips/{id}", method="GET") + return BoundFloatingIP(self, response["floating_ip"]) + + def get_list( + self, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + name: str | None = None, + ) -> FloatingIPsPageResult: + """Get a list of floating ips from this account + + :param label_selector: str (optional) + Can be used to filter Floating IPs by labels. The response will only contain Floating IPs matching the label selector.able values. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :param name: str (optional) + Can be used to filter networks by their name. + :return: (List[:class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + + if label_selector is not None: + params["label_selector"] = label_selector + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + if name is not None: + params["name"] = name + + response = self._client.request( + url="/floating_ips", method="GET", params=params + ) + floating_ips = [ + BoundFloatingIP(self, floating_ip_data) + for floating_ip_data in response["floating_ips"] + ] + + return FloatingIPsPageResult(floating_ips, Meta.parse_meta(response)) + + def get_all( + self, + label_selector: str | None = None, + name: str | None = None, + ) -> list[BoundFloatingIP]: + """Get all floating ips from this account + + :param label_selector: str (optional) + Can be used to filter Floating IPs by labels. The response will only contain Floating IPs matching the label selector.able values. + :param name: str (optional) + Can be used to filter networks by their name. + :return: List[:class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>`] + """ + return self._iter_pages(self.get_list, label_selector=label_selector, name=name) + + def get_by_name(self, name: str) -> BoundFloatingIP | None: + """Get Floating IP by name + + :param name: str + Used to get Floating IP by name. + :return: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` + """ + return self._get_first_by(name=name) + + def create( + self, + type: str, + description: str | None = None, + labels: str | None = None, + home_location: Location | BoundLocation | None = None, + server: Server | BoundServer | None = None, + name: str | None = None, + ) -> CreateFloatingIPResponse: + """Creates a new Floating IP assigned to a server. + + :param type: str + Floating IP type Choices: ipv4, ipv6 + :param description: str (optional) + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param home_location: :class:`BoundLocation <hcloud.locations.client.BoundLocation>` or :class:`Location <hcloud.locations.domain.Location>` ( + Home location (routing is optimized for that location). Only optional if server argument is passed. + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + Server to assign the Floating IP to + :param name: str (optional) + :return: :class:`CreateFloatingIPResponse <hcloud.floating_ips.domain.CreateFloatingIPResponse>` + """ + + data: dict[str, Any] = {"type": type} + if description is not None: + data["description"] = description + if labels is not None: + data["labels"] = labels + if home_location is not None: + data["home_location"] = home_location.id_or_name + if server is not None: + data["server"] = server.id + if name is not None: + data["name"] = name + + response = self._client.request(url="/floating_ips", json=data, method="POST") + + action = None + if response.get("action") is not None: + action = BoundAction(self._client.actions, response["action"]) + + result = CreateFloatingIPResponse( + floating_ip=BoundFloatingIP(self, response["floating_ip"]), action=action + ) + return result + + def update( + self, + floating_ip: FloatingIP | BoundFloatingIP, + description: str | None = None, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundFloatingIP: + """Updates the description or labels of a Floating IP. + + :param floating_ip: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` or :class:`FloatingIP <hcloud.floating_ips.domain.FloatingIP>` + :param description: str (optional) + New Description to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param name: str (optional) + New name to set + :return: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` + """ + data: dict[str, Any] = {} + if description is not None: + data["description"] = description + if labels is not None: + data["labels"] = labels + if name is not None: + data["name"] = name + + response = self._client.request( + url=f"/floating_ips/{floating_ip.id}", + method="PUT", + json=data, + ) + return BoundFloatingIP(self, response["floating_ip"]) + + def delete(self, floating_ip: FloatingIP | BoundFloatingIP) -> bool: + """Deletes a Floating IP. If it is currently assigned to a server it will automatically get unassigned. + + :param floating_ip: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` or :class:`FloatingIP <hcloud.floating_ips.domain.FloatingIP>` + :return: boolean + """ + self._client.request( + url=f"/floating_ips/{floating_ip.id}", + method="DELETE", + ) + # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised + return True + + def change_protection( + self, + floating_ip: FloatingIP | BoundFloatingIP, + delete: bool | None = None, + ) -> BoundAction: + """Changes the protection configuration of the Floating IP. + + :param floating_ip: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` or :class:`FloatingIP <hcloud.floating_ips.domain.FloatingIP>` + :param delete: boolean + If true, prevents the Floating IP from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {} + if delete is not None: + data.update({"delete": delete}) + + response = self._client.request( + url=f"/floating_ips/{floating_ip.id}/actions/change_protection", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def assign( + self, + floating_ip: FloatingIP | BoundFloatingIP, + server: Server | BoundServer, + ) -> BoundAction: + """Assigns a Floating IP to a server. + + :param floating_ip: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` or :class:`FloatingIP <hcloud.floating_ips.domain.FloatingIP>` + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + Server the Floating IP shall be assigned to + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/floating_ips/{floating_ip.id}/actions/assign", + method="POST", + json={"server": server.id}, + ) + return BoundAction(self._client.actions, response["action"]) + + def unassign(self, floating_ip: FloatingIP | BoundFloatingIP) -> BoundAction: + """Unassigns a Floating IP, resulting in it being unreachable. You may assign it to a server again at a later time. + + :param floating_ip: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` or :class:`FloatingIP <hcloud.floating_ips.domain.FloatingIP>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/floating_ips/{floating_ip.id}/actions/unassign", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def change_dns_ptr( + self, + floating_ip: FloatingIP | BoundFloatingIP, + ip: str, + dns_ptr: str, + ) -> BoundAction: + """Changes the hostname that will appear when getting the hostname belonging to this Floating IP. + + :param floating_ip: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` or :class:`FloatingIP <hcloud.floating_ips.domain.FloatingIP>` + :param ip: str + The IP address for which to set the reverse DNS entry + :param dns_ptr: str + Hostname to set as a reverse DNS PTR entry, will reset to original default value if `None` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/floating_ips/{floating_ip.id}/actions/change_dns_ptr", + method="POST", + json={"ip": ip, "dns_ptr": dns_ptr}, + ) + return BoundAction(self._client.actions, response["action"]) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/domain.py new file mode 100644 index 000000000..e1f295bd6 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/floating_ips/domain.py @@ -0,0 +1,109 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..locations import BoundLocation + from ..servers import BoundServer + from .client import BoundFloatingIP + + +class FloatingIP(BaseDomain): + """Floating IP Domain + + :param id: int + ID of the Floating IP + :param description: str, None + Description of the Floating IP + :param ip: str + IP address of the Floating IP + :param type: str + Type of Floating IP. Choices: `ipv4`, `ipv6` + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>`, None + Server the Floating IP is assigned to, None if it is not assigned at all + :param dns_ptr: List[Dict] + Array of reverse DNS entries + :param home_location: :class:`BoundLocation <hcloud.locations.client.BoundLocation>` + Location the Floating IP was created in. Routing is optimized for this location. + :param blocked: boolean + Whether the IP is blocked + :param protection: dict + Protection configuration for the Floating IP + :param labels: dict + User-defined labels (key-value pairs) + :param created: datetime + Point in time when the Floating IP was created + :param name: str + Name of the Floating IP + """ + + __slots__ = ( + "id", + "type", + "description", + "ip", + "server", + "dns_ptr", + "home_location", + "blocked", + "protection", + "labels", + "name", + "created", + ) + + def __init__( + self, + id: int | None = None, + type: str | None = None, + description: str | None = None, + ip: str | None = None, + server: BoundServer | None = None, + dns_ptr: list[dict] | None = None, + home_location: BoundLocation | None = None, + blocked: bool | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + created: str | None = None, + name: str | None = None, + ): + self.id = id + self.type = type + self.description = description + self.ip = ip + self.server = server + self.dns_ptr = dns_ptr + self.home_location = home_location + self.blocked = blocked + self.protection = protection + self.labels = labels + self.created = isoparse(created) if created else None + self.name = name + + +class CreateFloatingIPResponse(BaseDomain): + """Create Floating IP Response Domain + + :param floating_ip: :class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>` + The Floating IP which was created + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + The Action which shows the progress of the Floating IP Creation + """ + + __slots__ = ("floating_ip", "action") + + def __init__( + self, + floating_ip: BoundFloatingIP, + action: BoundAction | None, + ): + self.floating_ip = floating_ip + self.action = action diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/hcloud.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/hcloud.py new file mode 100644 index 000000000..9de1cfe5b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/hcloud.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +import warnings + +warnings.warn( + "The 'hcloud.hcloud' module is deprecated, please import from the 'hcloud' module instead (e.g. 'from hcloud import Client').", + DeprecationWarning, + stacklevel=2, +) + +# pylint: disable=wildcard-import,wrong-import-position,unused-wildcard-import +from ._client import * # noqa diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/helpers/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/helpers/__init__.py new file mode 100644 index 000000000..b6a4cd73d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/helpers/__init__.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +from .labels import LabelValidator # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/helpers/labels.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/helpers/labels.py new file mode 100644 index 000000000..360415784 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/helpers/labels.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +import re + + +class LabelValidator: + KEY_REGEX = re.compile( + r"^([a-z0-9A-Z]((?:[\-_.]|[a-z0-9A-Z]){0,253}[a-z0-9A-Z])?/)?[a-z0-9A-Z]((?:[\-_.]|[a-z0-9A-Z]|){0,61}[a-z0-9A-Z])?$" + ) + VALUE_REGEX = re.compile( + r"^(([a-z0-9A-Z](?:[\-_.]|[a-z0-9A-Z]){0,61})?[a-z0-9A-Z]$|$)" + ) + + @staticmethod + def validate(labels: dict[str, str]) -> bool: + """Validates Labels. If you want to know which key/value pair of the dict is not correctly formatted + use :func:`~hcloud.helpers.labels.validate_verbose`. + + :return: bool + """ + for key, value in labels.items(): + if LabelValidator.KEY_REGEX.match(key) is None: + return False + if LabelValidator.VALUE_REGEX.match(value) is None: + return False + return True + + @staticmethod + def validate_verbose(labels: dict[str, str]) -> tuple[bool, str]: + """Validates Labels and returns the corresponding error message if something is wrong. Returns True, <empty string> + if everything is fine. + + :return: bool, str + """ + for key, value in labels.items(): + if LabelValidator.KEY_REGEX.match(key) is None: + return ( + False, + f"label key {key} is not correctly formatted", + ) + if LabelValidator.VALUE_REGEX.match(value) is None: + return ( + False, + f"label value {value} (key: {key}) is not correctly formatted", + ) + return True, "" diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/__init__.py new file mode 100644 index 000000000..78cb6868f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundImage, ImagesClient, ImagesPageResult # noqa: F401 +from .domain import CreateImageResponse, Image # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/client.py new file mode 100644 index 000000000..65b7546a8 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/client.py @@ -0,0 +1,393 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import Image + +if TYPE_CHECKING: + from .._client import Client + + +class BoundImage(BoundModelBase, Image): + _client: ImagesClient + + model = Image + + def __init__(self, client: ImagesClient, data: dict): + # pylint: disable=import-outside-toplevel + from ..servers import BoundServer + + created_from = data.get("created_from") + if created_from is not None: + data["created_from"] = BoundServer( + client._client.servers, created_from, complete=False + ) + bound_to = data.get("bound_to") + if bound_to is not None: + data["bound_to"] = BoundServer( + client._client.servers, {"id": bound_to}, complete=False + ) + + super().__init__(client, data) + + def get_actions_list( + self, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + ) -> ActionsPageResult: + """Returns a list of action objects for the image. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + return self._client.get_actions_list( + self, sort=sort, page=page, per_page=per_page, status=status + ) + + def get_actions( + self, + sort: list[str] | None = None, + status: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for the image. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.get_actions(self, status=status, sort=sort) + + def update( + self, + description: str | None = None, + type: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundImage: + """Updates the Image. You may change the description, convert a Backup image to a Snapshot Image or change the image labels. + + :param description: str (optional) + New description of Image + :param type: str (optional) + Destination image type to convert to + Choices: snapshot + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundImage <hcloud.images.client.BoundImage>` + """ + return self._client.update(self, description, type, labels) + + def delete(self) -> bool: + """Deletes an Image. Only images of type snapshot and backup can be deleted. + + :return: bool + """ + return self._client.delete(self) + + def change_protection(self, delete: bool | None = None) -> BoundAction: + """Changes the protection configuration of the image. Can only be used on snapshots. + + :param delete: bool + If true, prevents the snapshot from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_protection(self, delete) + + +class ImagesPageResult(NamedTuple): + images: list[BoundImage] + meta: Meta | None + + +class ImagesClient(ClientEntityBase): + _client: Client + + actions: ResourceActionsClient + """Images scoped actions client + + :type: :class:`ResourceActionsClient <hcloud.actions.client.ResourceActionsClient>` + """ + + def __init__(self, client: Client): + super().__init__(client) + self.actions = ResourceActionsClient(client, "/images") + + def get_actions_list( + self, + image: Image | BoundImage, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + ) -> ActionsPageResult: + """Returns a list of action objects for an image. + + :param image: :class:`BoundImage <hcloud.images.client.BoundImage>` or :class:`Image <hcloud.images.domain.Image>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if sort is not None: + params["sort"] = sort + if status is not None: + params["status"] = status + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + response = self._client.request( + url=f"/images/{image.id}/actions", + method="GET", + params=params, + ) + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + return ActionsPageResult(actions, Meta.parse_meta(response)) + + def get_actions( + self, + image: Image | BoundImage, + sort: list[str] | None = None, + status: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for an image. + + :param image: :class:`BoundImage <hcloud.images.client.BoundImage>` or :class:`Image <hcloud.images.domain.Image>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `command` `status` `progress` `started` `finished` . You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default) + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._iter_pages( + self.get_actions_list, + image, + sort=sort, + status=status, + ) + + def get_by_id(self, id: int) -> BoundImage: + """Get a specific Image + + :param id: int + :return: :class:`BoundImage <hcloud.images.client.BoundImage` + """ + response = self._client.request(url=f"/images/{id}", method="GET") + return BoundImage(self, response["image"]) + + def get_list( + self, + name: str | None = None, + label_selector: str | None = None, + bound_to: list[str] | None = None, + type: list[str] | None = None, + architecture: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + include_deprecated: bool | None = None, + ) -> ImagesPageResult: + """Get all images + + :param name: str (optional) + Can be used to filter images by their name. + :param label_selector: str (optional) + Can be used to filter servers by labels. The response will only contain servers matching the label selector. + :param bound_to: List[str] (optional) + Server Id linked to the image. Only available for images of type backup + :param type: List[str] (optional) + Choices: system snapshot backup + :param architecture: List[str] (optional) + Choices: x86 arm + :param status: List[str] (optional) + Can be used to filter images by their status. The response will only contain images matching the status. + :param sort: List[str] (optional) + Choices: id id:asc id:desc name name:asc name:desc created created:asc created:desc + :param include_deprecated: bool (optional) + Include deprecated images in the response. Default: False + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundImage <hcloud.images.client.BoundImage>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if label_selector is not None: + params["label_selector"] = label_selector + if bound_to is not None: + params["bound_to"] = bound_to + if type is not None: + params["type"] = type + if architecture is not None: + params["architecture"] = architecture + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + if status is not None: + params["status"] = per_page + if include_deprecated is not None: + params["include_deprecated"] = include_deprecated + response = self._client.request(url="/images", method="GET", params=params) + images = [BoundImage(self, image_data) for image_data in response["images"]] + + return ImagesPageResult(images, Meta.parse_meta(response)) + + def get_all( + self, + name: str | None = None, + label_selector: str | None = None, + bound_to: list[str] | None = None, + type: list[str] | None = None, + architecture: list[str] | None = None, + sort: list[str] | None = None, + status: list[str] | None = None, + include_deprecated: bool | None = None, + ) -> list[BoundImage]: + """Get all images + + :param name: str (optional) + Can be used to filter images by their name. + :param label_selector: str (optional) + Can be used to filter servers by labels. The response will only contain servers matching the label selector. + :param bound_to: List[str] (optional) + Server Id linked to the image. Only available for images of type backup + :param type: List[str] (optional) + Choices: system snapshot backup + :param architecture: List[str] (optional) + Choices: x86 arm + :param status: List[str] (optional) + Can be used to filter images by their status. The response will only contain images matching the status. + :param sort: List[str] (optional) + Choices: id name created (You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default)) + :param include_deprecated: bool (optional) + Include deprecated images in the response. Default: False + :return: List[:class:`BoundImage <hcloud.images.client.BoundImage>`] + """ + return self._iter_pages( + self.get_list, + name=name, + label_selector=label_selector, + bound_to=bound_to, + type=type, + architecture=architecture, + sort=sort, + status=status, + include_deprecated=include_deprecated, + ) + + def get_by_name(self, name: str) -> BoundImage | None: + """Get image by name + + Deprecated: Use get_by_name_and_architecture instead. + + :param name: str + Used to get image by name. + :return: :class:`BoundImage <hcloud.images.client.BoundImage>` + """ + return self._get_first_by(name=name) + + def get_by_name_and_architecture( + self, + name: str, + architecture: str, + ) -> BoundImage | None: + """Get image by name + + :param name: str + Used to identify the image. + :param architecture: str + Used to identify the image. + :return: :class:`BoundImage <hcloud.images.client.BoundImage>` + """ + return self._get_first_by(name=name, architecture=[architecture]) + + def update( + self, + image: Image | BoundImage, + description: str | None = None, + type: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundImage: + """Updates the Image. You may change the description, convert a Backup image to a Snapshot Image or change the image labels. + + :param image: :class:`BoundImage <hcloud.images.client.BoundImage>` or :class:`Image <hcloud.images.domain.Image>` + :param description: str (optional) + New description of Image + :param type: str (optional) + Destination image type to convert to + Choices: snapshot + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundImage <hcloud.images.client.BoundImage>` + """ + data: dict[str, Any] = {} + if description is not None: + data.update({"description": description}) + if type is not None: + data.update({"type": type}) + if labels is not None: + data.update({"labels": labels}) + response = self._client.request( + url=f"/images/{image.id}", method="PUT", json=data + ) + return BoundImage(self, response["image"]) + + def delete(self, image: Image | BoundImage) -> bool: + """Deletes an Image. Only images of type snapshot and backup can be deleted. + + :param :class:`BoundImage <hcloud.images.client.BoundImage>` or :class:`Image <hcloud.images.domain.Image>` + :return: bool + """ + self._client.request(url=f"/images/{image.id}", method="DELETE") + # Return allays true, because the API does not return an action for it. When an error occurs a APIException will be raised + return True + + def change_protection( + self, + image: Image | BoundImage, + delete: bool | None = None, + ) -> BoundAction: + """Changes the protection configuration of the image. Can only be used on snapshots. + + :param image: :class:`BoundImage <hcloud.images.client.BoundImage>` or :class:`Image <hcloud.images.domain.Image>` + :param delete: bool + If true, prevents the snapshot from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {} + if delete is not None: + data.update({"delete": delete}) + + response = self._client.request( + url=f"/images/{image.id}/actions/change_protection", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/domain.py new file mode 100644 index 000000000..9a58a3cc2 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/images/domain.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain, DomainIdentityMixin + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..servers import BoundServer, Server + from .client import BoundImage + + +class Image(BaseDomain, DomainIdentityMixin): + """Image Domain + + :param id: int + ID of the image + :param type: str + Type of the image Choices: `system`, `snapshot`, `backup`, `app` + :param status: str + Whether the image can be used or if it’s still being created Choices: `available`, `creating` + :param name: str, None + Unique identifier of the image. This value is only set for system images. + :param description: str + Description of the image + :param image_size: number, None + Size of the image file in our storage in GB. For snapshot images this is the value relevant for calculating costs for the image. + :param disk_size: number + Size of the disk contained in the image in GB. + :param created: datetime + Point in time when the image was created + :param created_from: :class:`BoundServer <hcloud.servers.client.BoundServer>`, None + Information about the server the image was created from + :param bound_to: :class:`BoundServer <hcloud.servers.client.BoundServer>`, None + ID of server the image is bound to. Only set for images of type `backup`. + :param os_flavor: str + Flavor of operating system contained in the image Choices: `ubuntu`, `centos`, `debian`, `fedora`, `unknown` + :param os_version: str, None + Operating system version + :param architecture: str + CPU Architecture that the image is compatible with. Choices: `x86`, `arm` + :param rapid_deploy: bool + Indicates that rapid deploy of the image is available + :param protection: dict + Protection configuration for the image + :param deprecated: datetime, None + Point in time when the image is considered to be deprecated (in ISO-8601 format) + :param labels: Dict + User-defined labels (key-value pairs) + """ + + __slots__ = ( + "id", + "name", + "type", + "description", + "image_size", + "disk_size", + "bound_to", + "os_flavor", + "os_version", + "architecture", + "rapid_deploy", + "created_from", + "status", + "protection", + "labels", + "created", + "deprecated", + ) + + # pylint: disable=too-many-locals + def __init__( + self, + id: int | None = None, + name: str | None = None, + type: str | None = None, + created: str | None = None, + description: str | None = None, + image_size: int | None = None, + disk_size: int | None = None, + deprecated: str | None = None, + bound_to: Server | BoundServer | None = None, + os_flavor: str | None = None, + os_version: str | None = None, + architecture: str | None = None, + rapid_deploy: bool | None = None, + created_from: Server | BoundServer | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + status: str | None = None, + ): + self.id = id + self.name = name + self.type = type + self.created = isoparse(created) if created else None + self.description = description + self.image_size = image_size + self.disk_size = disk_size + self.deprecated = isoparse(deprecated) if deprecated else None + self.bound_to = bound_to + self.os_flavor = os_flavor + self.os_version = os_version + self.architecture = architecture + self.rapid_deploy = rapid_deploy + self.created_from = created_from + self.protection = protection + self.labels = labels + self.status = status + + +class CreateImageResponse(BaseDomain): + """Create Image Response Domain + + :param image: :class:`BoundImage <hcloud.images.client.BoundImage>` + The Image which was created + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + The Action which shows the progress of the Floating IP Creation + """ + + __slots__ = ("action", "image") + + def __init__( + self, + action: BoundAction, + image: BoundImage, + ): + self.action = action + self.image = image diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/isos/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/isos/__init__.py new file mode 100644 index 000000000..0d5e38fae --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/isos/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundIso, IsosClient, IsosPageResult # noqa: F401 +from .domain import Iso # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/isos/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/isos/client.py new file mode 100644 index 000000000..cc46af7f9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/isos/client.py @@ -0,0 +1,128 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple +from warnings import warn + +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import Iso + +if TYPE_CHECKING: + from .._client import Client + + +class BoundIso(BoundModelBase, Iso): + _client: IsosClient + + model = Iso + + +class IsosPageResult(NamedTuple): + isos: list[BoundIso] + meta: Meta | None + + +class IsosClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundIso: + """Get a specific ISO by its id + + :param id: int + :return: :class:`BoundIso <hcloud.isos.client.BoundIso>` + """ + response = self._client.request(url=f"/isos/{id}", method="GET") + return BoundIso(self, response["iso"]) + + def get_list( + self, + name: str | None = None, + architecture: list[str] | None = None, + include_wildcard_architecture: bool | None = None, + include_architecture_wildcard: bool | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> IsosPageResult: + """Get a list of ISOs + + :param name: str (optional) + Can be used to filter ISOs by their name. + :param architecture: List[str] (optional) + Can be used to filter ISOs by their architecture. Choices: x86 arm + :param include_wildcard_architecture: bool (optional) + Deprecated, please use `include_architecture_wildcard` instead. + :param include_architecture_wildcard: bool (optional) + Custom ISOs do not have an architecture set. You must also set this flag to True if you are filtering by + architecture and also want custom ISOs. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundIso <hcloud.isos.client.BoundIso>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + + if include_wildcard_architecture is not None: + warn( + "The `include_wildcard_architecture` argument is deprecated, please use the `include_architecture_wildcard` argument instead.", + DeprecationWarning, + ) + include_architecture_wildcard = include_wildcard_architecture + + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if architecture is not None: + params["architecture"] = architecture + if include_architecture_wildcard is not None: + params["include_architecture_wildcard"] = include_architecture_wildcard + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request(url="/isos", method="GET", params=params) + isos = [BoundIso(self, iso_data) for iso_data in response["isos"]] + return IsosPageResult(isos, Meta.parse_meta(response)) + + def get_all( + self, + name: str | None = None, + architecture: list[str] | None = None, + include_wildcard_architecture: bool | None = None, + include_architecture_wildcard: bool | None = None, + ) -> list[BoundIso]: + """Get all ISOs + + :param name: str (optional) + Can be used to filter ISOs by their name. + :param architecture: List[str] (optional) + Can be used to filter ISOs by their architecture. Choices: x86 arm + :param include_wildcard_architecture: bool (optional) + Deprecated, please use `include_architecture_wildcard` instead. + :param include_architecture_wildcard: bool (optional) + Custom ISOs do not have an architecture set. You must also set this flag to True if you are filtering by + architecture and also want custom ISOs. + :return: List[:class:`BoundIso <hcloud.isos.client.BoundIso>`] + """ + + if include_wildcard_architecture is not None: + warn( + "The `include_wildcard_architecture` argument is deprecated, please use the `include_architecture_wildcard` argument instead.", + DeprecationWarning, + ) + include_architecture_wildcard = include_wildcard_architecture + + return self._iter_pages( + self.get_list, + name=name, + architecture=architecture, + include_architecture_wildcard=include_architecture_wildcard, + ) + + def get_by_name(self, name: str) -> BoundIso | None: + """Get iso by name + + :param name: str + Used to get iso by name. + :return: :class:`BoundIso <hcloud.isos.client.BoundIso>` + """ + return self._get_first_by(name=name) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/isos/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/isos/domain.py new file mode 100644 index 000000000..b2f4d30cf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/isos/domain.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from datetime import datetime +from warnings import warn + +from ..core import BaseDomain, DomainIdentityMixin +from ..deprecation import DeprecationInfo + + +class Iso(BaseDomain, DomainIdentityMixin): + """Iso Domain + + :param id: int + ID of the ISO + :param name: str, None + Unique identifier of the ISO. Only set for public ISOs + :param description: str + Description of the ISO + :param type: str + Type of the ISO. Choices: `public`, `private` + :param architecture: str, None + CPU Architecture that the ISO is compatible with. None means that the compatibility is unknown. Choices: `x86`, `arm` + :param deprecated: datetime, None + ISO 8601 timestamp of deprecation, None if ISO is still available. After the deprecation time it will no longer be possible to attach the ISO to servers. This field is deprecated. Use `deprecation` instead. + :param deprecation: :class:`DeprecationInfo <hcloud.deprecation.domain.DeprecationInfo>`, None + Describes if, when & how the resources was deprecated. If this field is set to None the resource is not + deprecated. If it has a value, it is considered deprecated. + """ + + __slots__ = ( + "id", + "name", + "type", + "architecture", + "description", + "deprecation", + ) + + def __init__( + self, + id: int | None = None, + name: str | None = None, + type: str | None = None, + architecture: str | None = None, + description: str | None = None, + deprecated: str | None = None, # pylint: disable=unused-argument + deprecation: dict | None = None, + ): + self.id = id + self.name = name + self.type = type + self.architecture = architecture + self.description = description + self.deprecation = ( + DeprecationInfo.from_dict(deprecation) if deprecation is not None else None + ) + + @property + def deprecated(self) -> datetime | None: + """ + ISO 8601 timestamp of deprecation, None if ISO is still available. + """ + warn( + "The `deprecated` field is deprecated, please use the `deprecation` field instead.", + DeprecationWarning, + ) + if self.deprecation is None: + return None + return self.deprecation.unavailable_after diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancer_types/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancer_types/__init__.py new file mode 100644 index 000000000..fa1dc33c7 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancer_types/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundLoadBalancerType, + LoadBalancerTypesClient, + LoadBalancerTypesPageResult, +) +from .domain import LoadBalancerType # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancer_types/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancer_types/client.py new file mode 100644 index 000000000..9a83dc707 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancer_types/client.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import LoadBalancerType + +if TYPE_CHECKING: + from .._client import Client + + +class BoundLoadBalancerType(BoundModelBase, LoadBalancerType): + _client: LoadBalancerTypesClient + + model = LoadBalancerType + + +class LoadBalancerTypesPageResult(NamedTuple): + load_balancer_types: list[BoundLoadBalancerType] + meta: Meta | None + + +class LoadBalancerTypesClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundLoadBalancerType: + """Returns a specific Load Balancer Type. + + :param id: int + :return: :class:`BoundLoadBalancerType <hcloud.load_balancer_type.client.BoundLoadBalancerType>` + """ + response = self._client.request( + url=f"/load_balancer_types/{id}", + method="GET", + ) + return BoundLoadBalancerType(self, response["load_balancer_type"]) + + def get_list( + self, + name: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> LoadBalancerTypesPageResult: + """Get a list of Load Balancer types + + :param name: str (optional) + Can be used to filter Load Balancer type by their name. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundLoadBalancerType <hcloud.load_balancer_types.client.BoundLoadBalancerType>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url="/load_balancer_types", method="GET", params=params + ) + load_balancer_types = [ + BoundLoadBalancerType(self, load_balancer_type_data) + for load_balancer_type_data in response["load_balancer_types"] + ] + return LoadBalancerTypesPageResult( + load_balancer_types, Meta.parse_meta(response) + ) + + def get_all(self, name: str | None = None) -> list[BoundLoadBalancerType]: + """Get all Load Balancer types + + :param name: str (optional) + Can be used to filter Load Balancer type by their name. + :return: List[:class:`BoundLoadBalancerType <hcloud.load_balancer_types.client.BoundLoadBalancerType>`] + """ + return self._iter_pages(self.get_list, name=name) + + def get_by_name(self, name: str) -> BoundLoadBalancerType | None: + """Get Load Balancer type by name + + :param name: str + Used to get Load Balancer type by name. + :return: :class:`BoundLoadBalancerType <hcloud.load_balancer_types.client.BoundLoadBalancerType>` + """ + return self._get_first_by(name=name) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancer_types/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancer_types/domain.py new file mode 100644 index 000000000..35719dba3 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancer_types/domain.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from ..core import BaseDomain, DomainIdentityMixin + + +class LoadBalancerType(BaseDomain, DomainIdentityMixin): + """LoadBalancerType Domain + + :param id: int + ID of the Load Balancer type + :param name: str + Name of the Load Balancer type + :param description: str + Description of the Load Balancer type + :param max_connections: int + Max amount of connections the Load Balancer can handle + :param max_services: int + Max amount of services the Load Balancer can handle + :param max_targets: int + Max amount of targets the Load Balancer can handle + :param max_assigned_certificates: int + Max amount of certificates the Load Balancer can serve + :param prices: Dict + Prices in different locations + + """ + + __slots__ = ( + "id", + "name", + "description", + "max_connections", + "max_services", + "max_targets", + "max_assigned_certificates", + "prices", + ) + + def __init__( + self, + id: int | None = None, + name: str | None = None, + description: str | None = None, + max_connections: int | None = None, + max_services: int | None = None, + max_targets: int | None = None, + max_assigned_certificates: int | None = None, + prices: dict | None = None, + ): + self.id = id + self.name = name + self.description = description + self.max_connections = max_connections + self.max_services = max_services + self.max_targets = max_targets + self.max_assigned_certificates = max_assigned_certificates + self.prices = prices diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py new file mode 100644 index 000000000..4bfd79940 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/__init__.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundLoadBalancer, + LoadBalancersClient, + LoadBalancersPageResult, +) +from .domain import ( # noqa: F401 + CreateLoadBalancerResponse, + GetMetricsResponse, + IPv4Address, + IPv6Network, + LoadBalancer, + LoadBalancerAlgorithm, + LoadBalancerHealtCheckHttp, + LoadBalancerHealthCheck, + LoadBalancerService, + LoadBalancerServiceHttp, + LoadBalancerTarget, + LoadBalancerTargetHealthStatus, + LoadBalancerTargetIP, + LoadBalancerTargetLabelSelector, + PrivateNet, + PublicNetwork, +) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/client.py new file mode 100644 index 000000000..492121354 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/client.py @@ -0,0 +1,939 @@ +from __future__ import annotations + +from datetime import datetime +from typing import TYPE_CHECKING, Any, NamedTuple + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient +from ..certificates import BoundCertificate +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..load_balancer_types import BoundLoadBalancerType +from ..locations import BoundLocation +from ..metrics import Metrics +from ..networks import BoundNetwork +from ..servers import BoundServer +from .domain import ( + CreateLoadBalancerResponse, + GetMetricsResponse, + IPv4Address, + IPv6Network, + LoadBalancer, + LoadBalancerAlgorithm, + LoadBalancerHealtCheckHttp, + LoadBalancerHealthCheck, + LoadBalancerService, + LoadBalancerServiceHttp, + LoadBalancerTarget, + LoadBalancerTargetHealthStatus, + LoadBalancerTargetIP, + LoadBalancerTargetLabelSelector, + MetricsType, + PrivateNet, + PublicNetwork, +) + +if TYPE_CHECKING: + from .._client import Client + from ..load_balancer_types import LoadBalancerType + from ..locations import Location + from ..networks import Network + + +class BoundLoadBalancer(BoundModelBase, LoadBalancer): + _client: LoadBalancersClient + + model = LoadBalancer + + # pylint: disable=too-many-branches,too-many-locals + def __init__(self, client: LoadBalancersClient, data: dict, complete: bool = True): + algorithm = data.get("algorithm") + if algorithm: + data["algorithm"] = LoadBalancerAlgorithm(type=algorithm["type"]) + + public_net = data.get("public_net") + if public_net: + ipv4_address = IPv4Address.from_dict(public_net["ipv4"]) + ipv6_network = IPv6Network.from_dict(public_net["ipv6"]) + data["public_net"] = PublicNetwork( + ipv4=ipv4_address, ipv6=ipv6_network, enabled=public_net["enabled"] + ) + + private_nets = data.get("private_net") + if private_nets: + private_nets = [ + PrivateNet( + network=BoundNetwork( + client._client.networks, + {"id": private_net["network"]}, + complete=False, + ), + ip=private_net["ip"], + ) + for private_net in private_nets + ] + data["private_net"] = private_nets + + targets = data.get("targets") + if targets: + tmp_targets = [] + for target in targets: + tmp_target = LoadBalancerTarget(type=target["type"]) + if target["type"] == "server": + tmp_target.server = BoundServer( + client._client.servers, data=target["server"], complete=False + ) + tmp_target.use_private_ip = target["use_private_ip"] + elif target["type"] == "label_selector": + tmp_target.label_selector = LoadBalancerTargetLabelSelector( + selector=target["label_selector"]["selector"] + ) + tmp_target.use_private_ip = target["use_private_ip"] + elif target["type"] == "ip": + tmp_target.ip = LoadBalancerTargetIP(ip=target["ip"]["ip"]) + + target_health_status = target.get("health_status") + if target_health_status is not None: + tmp_target.health_status = [ + LoadBalancerTargetHealthStatus( + listen_port=target_health_status_item["listen_port"], + status=target_health_status_item["status"], + ) + for target_health_status_item in target_health_status + ] + + tmp_targets.append(tmp_target) + data["targets"] = tmp_targets + + services = data.get("services") + if services: + tmp_services = [] + for service in services: + tmp_service = LoadBalancerService( + protocol=service["protocol"], + listen_port=service["listen_port"], + destination_port=service["destination_port"], + proxyprotocol=service["proxyprotocol"], + ) + if service["protocol"] != "tcp": + tmp_service.http = LoadBalancerServiceHttp( + sticky_sessions=service["http"]["sticky_sessions"], + redirect_http=service["http"]["redirect_http"], + cookie_name=service["http"]["cookie_name"], + cookie_lifetime=service["http"]["cookie_lifetime"], + ) + tmp_service.http.certificates = [ + BoundCertificate( + client._client.certificates, + {"id": certificate}, + complete=False, + ) + for certificate in service["http"]["certificates"] + ] + + tmp_service.health_check = LoadBalancerHealthCheck( + protocol=service["health_check"]["protocol"], + port=service["health_check"]["port"], + interval=service["health_check"]["interval"], + retries=service["health_check"]["retries"], + timeout=service["health_check"]["timeout"], + ) + if tmp_service.health_check.protocol != "tcp": + tmp_service.health_check.http = LoadBalancerHealtCheckHttp( + domain=service["health_check"]["http"]["domain"], + path=service["health_check"]["http"]["path"], + response=service["health_check"]["http"]["response"], + tls=service["health_check"]["http"]["tls"], + status_codes=service["health_check"]["http"]["status_codes"], + ) + tmp_services.append(tmp_service) + data["services"] = tmp_services + + load_balancer_type = data.get("load_balancer_type") + if load_balancer_type is not None: + data["load_balancer_type"] = BoundLoadBalancerType( + client._client.load_balancer_types, load_balancer_type + ) + + location = data.get("location") + if location is not None: + data["location"] = BoundLocation(client._client.locations, location) + + super().__init__(client, data, complete) + + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundLoadBalancer: + """Updates a Load Balancer. You can update a Load Balancers name and a Load Balancers labels. + + :param name: str (optional) + New name to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` + """ + return self._client.update(self, name, labels) + + def delete(self) -> bool: + """Deletes a Load Balancer. + + :return: boolean + """ + return self._client.delete(self) + + def get_metrics( + self, + type: MetricsType, + start: datetime | str, + end: datetime | str, + step: float | None = None, + ) -> GetMetricsResponse: + """Get Metrics for a LoadBalancer. + + :param type: Type of metrics to get. + :param start: Start of period to get Metrics for (in ISO-8601 format). + :param end: End of period to get Metrics for (in ISO-8601 format). + :param step: Resolution of results in seconds. + """ + return self._client.get_metrics( + self, + type=type, + start=start, + end=end, + step=step, + ) + + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a Load Balancer. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + return self._client.get_actions_list(self, status, sort, page, per_page) + + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a Load Balancer. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.get_actions(self, status, sort) + + def add_service(self, service: LoadBalancerService) -> BoundAction: + """Adds a service to a Load Balancer. + + :param service: :class:`LoadBalancerService <hcloud.load_balancers.domain.LoadBalancerService>` + The LoadBalancerService you want to add to the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.add_service(self, service=service) + + def update_service(self, service: LoadBalancerService) -> BoundAction: + """Updates a service of an Load Balancer. + + :param service: :class:`LoadBalancerService <hcloud.load_balancers.domain.LoadBalancerService>` + The LoadBalancerService you want to update + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.update_service(self, service=service) + + def delete_service(self, service: LoadBalancerService) -> BoundAction: + """Deletes a service from a Load Balancer. + + :param service: :class:`LoadBalancerService <hcloud.load_balancers.domain.LoadBalancerService>` + The LoadBalancerService you want to delete from the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.delete_service(self, service) + + def add_target(self, target: LoadBalancerTarget) -> BoundAction: + """Adds a target to a Load Balancer. + + :param target: :class:`LoadBalancerTarget <hcloud.load_balancers.domain.LoadBalancerTarget>` + The LoadBalancerTarget you want to add to the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.add_target(self, target) + + def remove_target(self, target: LoadBalancerTarget) -> BoundAction: + """Removes a target from a Load Balancer. + + :param target: :class:`LoadBalancerTarget <hcloud.load_balancers.domain.LoadBalancerTarget>` + The LoadBalancerTarget you want to remove from the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.remove_target(self, target) + + def change_algorithm(self, algorithm: LoadBalancerAlgorithm) -> BoundAction: + """Changes the algorithm used by the Load Balancer + + :param algorithm: :class:`LoadBalancerAlgorithm <hcloud.load_balancers.domain.LoadBalancerAlgorithm>` + The LoadBalancerAlgorithm you want to use + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_algorithm(self, algorithm) + + def change_dns_ptr(self, ip: str, dns_ptr: str) -> BoundAction: + """Changes the hostname that will appear when getting the hostname belonging to the public IPs (IPv4 and IPv6) of this Load Balancer. + + :param ip: str + The IP address for which to set the reverse DNS entry + :param dns_ptr: str + Hostname to set as a reverse DNS PTR entry, will reset to original default value if `None` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_dns_ptr(self, ip, dns_ptr) + + def change_protection(self, delete: bool) -> BoundAction: + """Changes the protection configuration of a Load Balancer. + + :param delete: boolean + If True, prevents the Load Balancer from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_protection(self, delete) + + def attach_to_network( + self, + network: Network | BoundNetwork, + ip: str | None = None, + ) -> BoundAction: + """Attaches a Load Balancer to a Network + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param ip: str + IP to request to be assigned to this Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.attach_to_network(self, network, ip) + + def detach_from_network(self, network: Network | BoundNetwork) -> BoundAction: + """Detaches a Load Balancer from a Network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.detach_from_network(self, network) + + def enable_public_interface(self) -> BoundAction: + """Enables the public interface of a Load Balancer. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.enable_public_interface(self) + + def disable_public_interface(self) -> BoundAction: + """Disables the public interface of a Load Balancer. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.disable_public_interface(self) + + def change_type( + self, + load_balancer_type: LoadBalancerType | BoundLoadBalancerType, + ) -> BoundAction: + """Changes the type of a Load Balancer. + + :param load_balancer_type: :class:`BoundLoadBalancerType <hcloud.load_balancer_types.client.BoundLoadBalancerType>` or :class:`LoadBalancerType <hcloud.load_balancer_types.domain.LoadBalancerType>` + Load Balancer type the Load Balancer should migrate to + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_type(self, load_balancer_type) + + +class LoadBalancersPageResult(NamedTuple): + load_balancers: list[BoundLoadBalancer] + meta: Meta | None + + +class LoadBalancersClient(ClientEntityBase): + _client: Client + + actions: ResourceActionsClient + """Load Balancers scoped actions client + + :type: :class:`ResourceActionsClient <hcloud.actions.client.ResourceActionsClient>` + """ + + def __init__(self, client: Client): + super().__init__(client) + self.actions = ResourceActionsClient(client, "/load_balancers") + + def get_by_id(self, id: int) -> BoundLoadBalancer: + """Get a specific Load Balancer + + :param id: int + :return: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` + """ + response = self._client.request( + url=f"/load_balancers/{id}", + method="GET", + ) + return BoundLoadBalancer(self, response["load_balancer"]) + + def get_list( + self, + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> LoadBalancersPageResult: + """Get a list of Load Balancers from this account + + :param name: str (optional) + Can be used to filter Load Balancers by their name. + :param label_selector: str (optional) + Can be used to filter Load Balancers by labels. The response will only contain Load Balancers matching the label selector. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if label_selector is not None: + params["label_selector"] = label_selector + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url="/load_balancers", method="GET", params=params + ) + + load_balancers = [ + BoundLoadBalancer(self, load_balancer_data) + for load_balancer_data in response["load_balancers"] + ] + return LoadBalancersPageResult(load_balancers, Meta.parse_meta(response)) + + def get_all( + self, + name: str | None = None, + label_selector: str | None = None, + ) -> list[BoundLoadBalancer]: + """Get all Load Balancers from this account + + :param name: str (optional) + Can be used to filter Load Balancers by their name. + :param label_selector: str (optional) + Can be used to filter Load Balancers by labels. The response will only contain Load Balancers matching the label selector. + :return: List[:class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>`] + """ + return self._iter_pages(self.get_list, name=name, label_selector=label_selector) + + def get_by_name(self, name: str) -> BoundLoadBalancer | None: + """Get Load Balancer by name + + :param name: str + Used to get Load Balancer by name. + :return: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` + """ + return self._get_first_by(name=name) + + def create( + self, + name: str, + load_balancer_type: LoadBalancerType | BoundLoadBalancerType, + algorithm: LoadBalancerAlgorithm | None = None, + services: list[LoadBalancerService] | None = None, + targets: list[LoadBalancerTarget] | None = None, + labels: dict[str, str] | None = None, + location: Location | BoundLocation | None = None, + network_zone: str | None = None, + public_interface: bool | None = None, + network: Network | BoundNetwork | None = None, + ) -> CreateLoadBalancerResponse: + """Creates a Load Balancer . + + :param name: str + Name of the Load Balancer + :param load_balancer_type: LoadBalancerType + Type of the Load Balancer + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param location: Location + Location of the Load Balancer + :param network_zone: str + Network Zone of the Load Balancer + :param algorithm: LoadBalancerAlgorithm (optional) + The algorithm the Load Balancer is currently using + :param services: LoadBalancerService + The services the Load Balancer is currently serving + :param targets: LoadBalancerTarget + The targets the Load Balancer is currently serving + :param public_interface: bool + Enable or disable the public interface of the Load Balancer + :param network: Network + Adds the Load Balancer to a Network + :return: :class:`CreateLoadBalancerResponse <hcloud.load_balancers.domain.CreateLoadBalancerResponse>` + """ + data: dict[str, Any] = { + "name": name, + "load_balancer_type": load_balancer_type.id_or_name, + } + if network is not None: + data["network"] = network.id + if public_interface is not None: + data["public_interface"] = public_interface + if labels is not None: + data["labels"] = labels + if algorithm is not None: + data["algorithm"] = {"type": algorithm.type} + if services is not None: + data["services"] = [service.to_payload() for service in services] + if targets is not None: + data["targets"] = [target.to_payload() for target in targets] + if network_zone is not None: + data["network_zone"] = network_zone + if location is not None: + data["location"] = location.id_or_name + + response = self._client.request(url="/load_balancers", method="POST", json=data) + + return CreateLoadBalancerResponse( + load_balancer=BoundLoadBalancer(self, response["load_balancer"]), + action=BoundAction(self._client.actions, response["action"]), + ) + + def update( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundLoadBalancer: + """Updates a LoadBalancer. You can update a LoadBalancer’s name and a LoadBalancer’s labels. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param name: str (optional) + New name to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` + """ + data: dict[str, Any] = {} + if name is not None: + data.update({"name": name}) + if labels is not None: + data.update({"labels": labels}) + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}", + method="PUT", + json=data, + ) + return BoundLoadBalancer(self, response["load_balancer"]) + + def delete(self, load_balancer: LoadBalancer | BoundLoadBalancer) -> bool: + """Deletes a Load Balancer. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :return: boolean + """ + self._client.request( + url=f"/load_balancers/{load_balancer.id}", + method="DELETE", + ) + return True + + def get_metrics( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + type: MetricsType | list[MetricsType], + start: datetime | str, + end: datetime | str, + step: float | None = None, + ) -> GetMetricsResponse: + """Get Metrics for a LoadBalancer. + + :param load_balancer: The Load Balancer to get the metrics for. + :param type: Type of metrics to get. + :param start: Start of period to get Metrics for (in ISO-8601 format). + :param end: End of period to get Metrics for (in ISO-8601 format). + :param step: Resolution of results in seconds. + """ + if not isinstance(type, list): + type = [type] + if isinstance(start, str): + start = isoparse(start) + if isinstance(end, str): + end = isoparse(end) + + params: dict[str, Any] = { + "type": ",".join(type), + "start": start.isoformat(), + "end": end.isoformat(), + } + if step is not None: + params["step"] = step + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/metrics", + method="GET", + params=params, + ) + return GetMetricsResponse( + metrics=Metrics(**response["metrics"]), + ) + + def get_actions_list( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a Load Balancer. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if status is not None: + params["status"] = status + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions", + method="GET", + params=params, + ) + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + return ActionsPageResult(actions, Meta.parse_meta(response)) + + def get_actions( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a Load Balancer. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._iter_pages( + self.get_actions_list, + load_balancer, + status=status, + sort=sort, + ) + + def add_service( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + service: LoadBalancerService, + ) -> BoundAction: + """Adds a service to a Load Balancer. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param service: :class:`LoadBalancerService <hcloud.load_balancers.domain.LoadBalancerService>` + The LoadBalancerService you want to add to the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = service.to_payload() + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/add_service", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def update_service( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + service: LoadBalancerService, + ) -> BoundAction: + """Updates a service of an Load Balancer. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param service: :class:`LoadBalancerService <hcloud.load_balancers.domain.LoadBalancerService>` + The LoadBalancerService with updated values within for the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = service.to_payload() + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/update_service", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def delete_service( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + service: LoadBalancerService, + ) -> BoundAction: + """Deletes a service from a Load Balancer. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param service: :class:`LoadBalancerService <hcloud.load_balancers.domain.LoadBalancerService>` + The LoadBalancerService you want to delete from the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"listen_port": service.listen_port} + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/delete_service", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def add_target( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + target: LoadBalancerTarget, + ) -> BoundAction: + """Adds a target to a Load Balancer. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param target: :class:`LoadBalancerTarget <hcloud.load_balancers.domain.LoadBalancerTarget>` + The LoadBalancerTarget you want to add to the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = target.to_payload() + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/add_target", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def remove_target( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + target: LoadBalancerTarget, + ) -> BoundAction: + """Removes a target from a Load Balancer. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param target: :class:`LoadBalancerTarget <hcloud.load_balancers.domain.LoadBalancerTarget>` + The LoadBalancerTarget you want to remove from the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = target.to_payload() + # Do not send use_private_ip on remove_target + data.pop("use_private_ip", None) + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/remove_target", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def change_algorithm( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + algorithm: LoadBalancerAlgorithm, + ) -> BoundAction: + """Changes the algorithm used by the Load Balancer + + :param load_balancer: :class:` <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param algorithm: :class:`LoadBalancerAlgorithm <hcloud.load_balancers.domain.LoadBalancerAlgorithm>` + The LoadBalancerSubnet you want to add to the Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"type": algorithm.type} + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/change_algorithm", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def change_dns_ptr( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + ip: str, + dns_ptr: str, + ) -> BoundAction: + """Changes the hostname that will appear when getting the hostname belonging to the public IPs (IPv4 and IPv6) of this Load Balancer. + + :param ip: str + The IP address for which to set the reverse DNS entry + :param dns_ptr: str + Hostname to set as a reverse DNS PTR entry, will reset to original default value if `None` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/change_dns_ptr", + method="POST", + json={"ip": ip, "dns_ptr": dns_ptr}, + ) + return BoundAction(self._client.actions, response["action"]) + + def change_protection( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + delete: bool | None = None, + ) -> BoundAction: + """Changes the protection configuration of a Load Balancer. + + :param load_balancer: :class:` <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param delete: boolean + If True, prevents the Load Balancer from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {} + if delete is not None: + data.update({"delete": delete}) + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/change_protection", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def attach_to_network( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + network: Network | BoundNetwork, + ip: str | None = None, + ) -> BoundAction: + """Attach a Load Balancer to a Network. + + :param load_balancer: :class:` <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param ip: str + IP to request to be assigned to this Load Balancer + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"network": network.id} + if ip is not None: + data.update({"ip": ip}) + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/attach_to_network", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def detach_from_network( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + network: Network | BoundNetwork, + ) -> BoundAction: + """Detaches a Load Balancer from a Network. + + :param load_balancer: :class:` <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"network": network.id} + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/detach_from_network", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def enable_public_interface( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + ) -> BoundAction: + """Enables the public interface of a Load Balancer. + + :param load_balancer: :class:` <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/enable_public_interface", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def disable_public_interface( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + ) -> BoundAction: + """Disables the public interface of a Load Balancer. + + :param load_balancer: :class:` <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/disable_public_interface", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def change_type( + self, + load_balancer: LoadBalancer | BoundLoadBalancer, + load_balancer_type: LoadBalancerType | BoundLoadBalancerType, + ) -> BoundAction: + """Changes the type of a Load Balancer. + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` or :class:`LoadBalancer <hcloud.load_balancers.domain.LoadBalancer>` + :param load_balancer_type: :class:`BoundLoadBalancerType <hcloud.load_balancer_types.client.BoundLoadBalancerType>` or :class:`LoadBalancerType <hcloud.load_balancer_types.domain.LoadBalancerType>` + Load Balancer type the Load Balancer should migrate to + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"load_balancer_type": load_balancer_type.id_or_name} + response = self._client.request( + url=f"/load_balancers/{load_balancer.id}/actions/change_type", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/domain.py new file mode 100644 index 000000000..6d6f0700a --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/load_balancers/domain.py @@ -0,0 +1,537 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Literal + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..certificates import BoundCertificate + from ..load_balancer_types import BoundLoadBalancerType + from ..locations import BoundLocation + from ..metrics import Metrics + from ..networks import BoundNetwork + from ..servers import BoundServer + from .client import BoundLoadBalancer + + +class LoadBalancer(BaseDomain): + """LoadBalancer Domain + + :param id: int + ID of the Load Balancer + :param name: str + Name of the Load Balancer (must be unique per project) + :param created: datetime + Point in time when the Load Balancer was created + :param protection: dict + Protection configuration for the Load Balancer + :param labels: dict + User-defined labels (key-value pairs) + :param location: Location + Location of the Load Balancer + :param public_net: :class:`PublicNetwork <hcloud.load_balancers.domain.PublicNetwork>` + Public network information. + :param private_net: List[:class:`PrivateNet <hcloud.load_balancers.domain.PrivateNet`] + Private networks information. + :param algorithm: LoadBalancerAlgorithm + The algorithm the Load Balancer is currently using + :param services: List[LoadBalancerService] + The services the LoadBalancer is currently serving + :param targets: LoadBalancerTarget + The targets the LoadBalancer is currently serving + :param load_balancer_type: LoadBalancerType + The type of the Load Balancer + :param outgoing_traffic: int, None + Outbound Traffic for the current billing period in bytes + :param ingoing_traffic: int, None + Inbound Traffic for the current billing period in bytes + :param included_traffic: int + Free Traffic for the current billing period in bytes + """ + + __slots__ = ( + "id", + "name", + "public_net", + "private_net", + "location", + "algorithm", + "services", + "load_balancer_type", + "protection", + "labels", + "targets", + "created", + "outgoing_traffic", + "ingoing_traffic", + "included_traffic", + ) + + # pylint: disable=too-many-locals + def __init__( + self, + id: int, + name: str | None = None, + public_net: PublicNetwork | None = None, + private_net: PrivateNet | None = None, + location: BoundLocation | None = None, + algorithm: LoadBalancerAlgorithm | None = None, + services: list[LoadBalancerService] | None = None, + load_balancer_type: BoundLoadBalancerType | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + targets: list[LoadBalancerTarget] | None = None, + created: str | None = None, + outgoing_traffic: int | None = None, + ingoing_traffic: int | None = None, + included_traffic: int | None = None, + ): + self.id = id + self.name = name + self.created = isoparse(created) if created else None + self.public_net = public_net + self.private_net = private_net + self.location = location + self.algorithm = algorithm + self.services = services + self.load_balancer_type = load_balancer_type + self.targets = targets + self.protection = protection + self.labels = labels + self.outgoing_traffic = outgoing_traffic + self.ingoing_traffic = ingoing_traffic + self.included_traffic = included_traffic + + +class LoadBalancerService(BaseDomain): + """LoadBalancerService Domain + + :param protocol: str + Protocol of the service Choices: tcp, http, https + :param listen_port: int + Required when protocol is tcp, must be unique per Load Balancer. + :param destination_port: int + Required when protocol is tcp + :param proxyprotocol: bool + Enable proxyprotocol + :param health_check: LoadBalancerHealthCheck + Configuration for health checks + :param http: LoadBalancerServiceHttp + Configuration for http/https protocols, required when protocol is http/https + """ + + def __init__( + self, + protocol: str | None = None, + listen_port: int | None = None, + destination_port: int | None = None, + proxyprotocol: bool | None = None, + health_check: LoadBalancerHealthCheck | None = None, + http: LoadBalancerServiceHttp | None = None, + ): + self.protocol = protocol + self.listen_port = listen_port + self.destination_port = destination_port + self.proxyprotocol = proxyprotocol + self.health_check = health_check + self.http = http + + # pylint: disable=too-many-branches + def to_payload(self) -> dict[str, Any]: + """ + Generates the request payload from this domain object. + """ + payload: dict[str, Any] = {} + + if self.protocol is not None: + payload["protocol"] = self.protocol + if self.listen_port is not None: + payload["listen_port"] = self.listen_port + if self.destination_port is not None: + payload["destination_port"] = self.destination_port + if self.proxyprotocol is not None: + payload["proxyprotocol"] = self.proxyprotocol + + if self.http is not None: + http: dict[str, Any] = {} + if self.http.cookie_name is not None: + http["cookie_name"] = self.http.cookie_name + if self.http.cookie_lifetime is not None: + http["cookie_lifetime"] = self.http.cookie_lifetime + if self.http.redirect_http is not None: + http["redirect_http"] = self.http.redirect_http + if self.http.sticky_sessions is not None: + http["sticky_sessions"] = self.http.sticky_sessions + + http["certificates"] = [ + certificate.id for certificate in self.http.certificates or [] + ] + + payload["http"] = http + + if self.health_check is not None: + health_check: dict[str, Any] = { + "protocol": self.health_check.protocol, + "port": self.health_check.port, + "interval": self.health_check.interval, + "timeout": self.health_check.timeout, + "retries": self.health_check.retries, + } + if self.health_check.protocol is not None: + health_check["protocol"] = self.health_check.protocol + if self.health_check.port is not None: + health_check["port"] = self.health_check.port + if self.health_check.interval is not None: + health_check["interval"] = self.health_check.interval + if self.health_check.timeout is not None: + health_check["timeout"] = self.health_check.timeout + if self.health_check.retries is not None: + health_check["retries"] = self.health_check.retries + + if self.health_check.http is not None: + health_check_http: dict[str, Any] = {} + if self.health_check.http.domain is not None: + health_check_http["domain"] = self.health_check.http.domain + if self.health_check.http.path is not None: + health_check_http["path"] = self.health_check.http.path + if self.health_check.http.response is not None: + health_check_http["response"] = self.health_check.http.response + if self.health_check.http.status_codes is not None: + health_check_http[ + "status_codes" + ] = self.health_check.http.status_codes + if self.health_check.http.tls is not None: + health_check_http["tls"] = self.health_check.http.tls + + health_check["http"] = health_check_http + + payload["health_check"] = health_check + return payload + + +class LoadBalancerServiceHttp(BaseDomain): + """LoadBalancerServiceHttp Domain + + :param cookie_name: str + Name of the cookie used for Session Stickness + :param cookie_lifetime: str + Lifetime of the cookie used for Session Stickness + :param certificates: list + IDs of the Certificates to use for TLS/SSL termination by the Load Balancer; empty for TLS/SSL passthrough or if protocol is "http" + :param redirect_http: bool + Redirect traffic from http port 80 to port 443 + :param sticky_sessions: bool + Use sticky sessions. Only available if protocol is "http" or "https". + """ + + def __init__( + self, + cookie_name: str | None = None, + cookie_lifetime: str | None = None, + certificates: list[BoundCertificate] | None = None, + redirect_http: bool | None = None, + sticky_sessions: bool | None = None, + ): + self.cookie_name = cookie_name + self.cookie_lifetime = cookie_lifetime + self.certificates = certificates + self.redirect_http = redirect_http + self.sticky_sessions = sticky_sessions + + +class LoadBalancerHealthCheck(BaseDomain): + """LoadBalancerHealthCheck Domain + + :param protocol: str + Protocol of the service Choices: tcp, http, https + :param port: int + Port the healthcheck will be performed on + :param interval: int + Interval we trigger health check in + :param timeout: int + Timeout in sec after a try is assumed as timeout + :param retries: int + Retries we perform until we assume a target as unhealthy + :param http: LoadBalancerHealtCheckHttp + HTTP Config + """ + + def __init__( + self, + protocol: str | None = None, + port: int | None = None, + interval: int | None = None, + timeout: int | None = None, + retries: int | None = None, + http: LoadBalancerHealtCheckHttp | None = None, + ): + self.protocol = protocol + self.port = port + self.interval = interval + self.timeout = timeout + self.retries = retries + self.http = http + + +class LoadBalancerHealtCheckHttp(BaseDomain): + """LoadBalancerHealtCheckHttp Domain + + :param domain: str + Domain name to send in HTTP request. Can be null: In that case we will not send a domain name + :param path: str + HTTP Path send in Request + :param response: str + Optional HTTP response to receive in order to pass the health check + :param status_codes: list + List of HTTP status codes to receive in order to pass the health check + :param tls: bool + Type of health check + """ + + def __init__( + self, + domain: str | None = None, + path: str | None = None, + response: str | None = None, + status_codes: list | None = None, + tls: bool | None = None, + ): + self.domain = domain + self.path = path + self.response = response + self.status_codes = status_codes + self.tls = tls + + +class LoadBalancerTarget(BaseDomain): + """LoadBalancerTarget Domain + + :param type: str + Type of the resource, can be server or label_selector + :param server: Server + Target server + :param label_selector: LoadBalancerTargetLabelSelector + Target label selector + :param ip: LoadBalancerTargetIP + Target IP + :param use_private_ip: bool + use the private IP instead of primary public IP + :param health_status: list + List of health statuses of the services on this target. Only present for target types "server" and "ip". + """ + + def __init__( + self, + type: str | None = None, + server: BoundServer | None = None, + label_selector: LoadBalancerTargetLabelSelector | None = None, + ip: LoadBalancerTargetIP | None = None, + use_private_ip: bool | None = None, + health_status: list[LoadBalancerTargetHealthStatus] | None = None, + ): + self.type = type + self.server = server + self.label_selector = label_selector + self.ip = ip + self.use_private_ip = use_private_ip + self.health_status = health_status + + def to_payload(self) -> dict[str, Any]: + """ + Generates the request payload from this domain object. + """ + payload: dict[str, Any] = { + "type": self.type, + } + if self.use_private_ip is not None: + payload["use_private_ip"] = self.use_private_ip + + if self.type == "server": + if self.server is None: + raise ValueError(f"server is not defined in target {self!r}") + payload["server"] = {"id": self.server.id} + + elif self.type == "label_selector": + if self.label_selector is None: + raise ValueError(f"label_selector is not defined in target {self!r}") + payload["label_selector"] = {"selector": self.label_selector.selector} + + elif self.type == "ip": + if self.ip is None: + raise ValueError(f"ip is not defined in target {self!r}") + payload["ip"] = {"ip": self.ip.ip} + + return payload + + +class LoadBalancerTargetHealthStatus(BaseDomain): + """LoadBalancerTargetHealthStatus Domain + + :param listen_port: Load Balancer Target listen port + :param status: Load Balancer Target status. Choices: healthy, unhealthy, unknown + """ + + def __init__( + self, + listen_port: int | None = None, + status: str | None = None, + ): + self.listen_port = listen_port + self.status = status + + +class LoadBalancerTargetLabelSelector(BaseDomain): + """LoadBalancerTargetLabelSelector Domain + + :param selector: str Target label selector + """ + + def __init__(self, selector: str | None = None): + self.selector = selector + + +class LoadBalancerTargetIP(BaseDomain): + """LoadBalancerTargetIP Domain + + :param ip: str Target IP + """ + + def __init__(self, ip: str | None = None): + self.ip = ip + + +class LoadBalancerAlgorithm(BaseDomain): + """LoadBalancerAlgorithm Domain + + :param type: str + Algorithm of the Load Balancer. Choices: round_robin, least_connections + """ + + def __init__(self, type: str | None = None): + self.type = type + + +class PublicNetwork(BaseDomain): + """Public Network Domain + + :param ipv4: :class:`IPv4Address <hcloud.load_balancers.domain.IPv4Address>` + :param ipv6: :class:`IPv6Network <hcloud.load_balancers.domain.IPv6Network>` + :param enabled: boolean + """ + + __slots__ = ("ipv4", "ipv6", "enabled") + + def __init__( + self, + ipv4: IPv4Address, + ipv6: IPv6Network, + enabled: bool, + ): + self.ipv4 = ipv4 + self.ipv6 = ipv6 + self.enabled = enabled + + +class IPv4Address(BaseDomain): + """IPv4 Address Domain + + :param ip: str + The IPv4 Address + """ + + __slots__ = ("ip", "dns_ptr") + + def __init__( + self, + ip: str, + dns_ptr: str, + ): + self.ip = ip + self.dns_ptr = dns_ptr + + +class IPv6Network(BaseDomain): + """IPv6 Network Domain + + :param ip: str + The IPv6 Network as CIDR Notation + """ + + __slots__ = ("ip", "dns_ptr") + + def __init__( + self, + ip: str, + dns_ptr: str, + ): + self.ip = ip + self.dns_ptr = dns_ptr + + +class PrivateNet(BaseDomain): + """PrivateNet Domain + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` + The Network the LoadBalancer is attached to + :param ip: str + The main IP Address of the LoadBalancer in the Network + """ + + __slots__ = ("network", "ip") + + def __init__( + self, + network: BoundNetwork, + ip: str, + ): + self.network = network + self.ip = ip + + +class CreateLoadBalancerResponse(BaseDomain): + """Create Load Balancer Response Domain + + :param load_balancer: :class:`BoundLoadBalancer <hcloud.load_balancers.client.BoundLoadBalancer>` + The created Load Balancer + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + Shows the progress of the Load Balancer creation + """ + + __slots__ = ("load_balancer", "action") + + def __init__( + self, + load_balancer: BoundLoadBalancer, + action: BoundAction, + ): + self.load_balancer = load_balancer + self.action = action + + +MetricsType = Literal[ + "open_connections", + "connections_per_second", + "requests_per_second", + "bandwidth", +] + + +class GetMetricsResponse(BaseDomain): + """Get a Load Balancer Metrics Response Domain + + :param metrics: The Load Balancer metrics + """ + + __slots__ = ("metrics",) + + def __init__( + self, + metrics: Metrics, + ): + self.metrics = metrics diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/locations/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/locations/__init__.py new file mode 100644 index 000000000..1c23a5178 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/locations/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundLocation, LocationsClient, LocationsPageResult # noqa: F401 +from .domain import Location # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/locations/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/locations/client.py new file mode 100644 index 000000000..047ad9d4b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/locations/client.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import Location + +if TYPE_CHECKING: + from .._client import Client + + +class BoundLocation(BoundModelBase, Location): + _client: LocationsClient + + model = Location + + +class LocationsPageResult(NamedTuple): + locations: list[BoundLocation] + meta: Meta | None + + +class LocationsClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundLocation: + """Get a specific location by its ID. + + :param id: int + :return: :class:`BoundLocation <hcloud.locations.client.BoundLocation>` + """ + response = self._client.request(url=f"/locations/{id}", method="GET") + return BoundLocation(self, response["location"]) + + def get_list( + self, + name: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> LocationsPageResult: + """Get a list of locations + + :param name: str (optional) + Can be used to filter locations by their name. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundLocation <hcloud.locations.client.BoundLocation>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request(url="/locations", method="GET", params=params) + locations = [ + BoundLocation(self, location_data) + for location_data in response["locations"] + ] + return LocationsPageResult(locations, Meta.parse_meta(response)) + + def get_all(self, name: str | None = None) -> list[BoundLocation]: + """Get all locations + + :param name: str (optional) + Can be used to filter locations by their name. + :return: List[:class:`BoundLocation <hcloud.locations.client.BoundLocation>`] + """ + return self._iter_pages(self.get_list, name=name) + + def get_by_name(self, name: str) -> BoundLocation | None: + """Get location by name + + :param name: str + Used to get location by name. + :return: :class:`BoundLocation <hcloud.locations.client.BoundLocation>` + """ + return self._get_first_by(name=name) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/locations/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/locations/domain.py new file mode 100644 index 000000000..7d4af2230 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/locations/domain.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +from ..core import BaseDomain, DomainIdentityMixin + + +class Location(BaseDomain, DomainIdentityMixin): + """Location Domain + + :param id: int + ID of location + :param name: str + Name of location + :param description: str + Description of location + :param country: str + ISO 3166-1 alpha-2 code of the country the location resides in + :param city: str + City the location is closest to + :param latitude: float + Latitude of the city closest to the location + :param longitude: float + Longitude of the city closest to the location + :param network_zone: str + Name of network zone this location resides in + """ + + __slots__ = ( + "id", + "name", + "description", + "country", + "city", + "latitude", + "longitude", + "network_zone", + ) + + def __init__( + self, + id: int | None = None, + name: str | None = None, + description: str | None = None, + country: str | None = None, + city: str | None = None, + latitude: float | None = None, + longitude: float | None = None, + network_zone: str | None = None, + ): + self.id = id + self.name = name + self.description = description + self.country = country + self.city = city + self.latitude = latitude + self.longitude = longitude + self.network_zone = network_zone diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/metrics/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/metrics/__init__.py new file mode 100644 index 000000000..65d393c8a --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/metrics/__init__.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +from .domain import Metrics, TimeSeries # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/metrics/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/metrics/domain.py new file mode 100644 index 000000000..a9e737774 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/metrics/domain.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from datetime import datetime +from typing import Dict, List, Literal, Tuple + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + +TimeSeries = Dict[str, Dict[Literal["values"], List[Tuple[float, str]]]] + + +class Metrics(BaseDomain): + """Metrics Domain + + :param start: Start of period of metrics reported. + :param end: End of period of metrics reported. + :param step: Resolution of results in seconds. + :param time_series: Dict with time series data, using the name of the time series as + key. The metrics timestamps and values are stored in a list of tuples + ``[(timestamp, value), ...]``. + """ + + start: datetime + end: datetime + step: float + time_series: TimeSeries + + __slots__ = ( + "start", + "end", + "step", + "time_series", + ) + + def __init__( + self, + start: str, + end: str, + step: float, + time_series: TimeSeries, + ): + self.start = isoparse(start) + self.end = isoparse(end) + self.step = step + self.time_series = time_series diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/__init__.py new file mode 100644 index 000000000..5bf4a88f0 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/__init__.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from .client import BoundNetwork, NetworksClient, NetworksPageResult # noqa: F401 +from .domain import ( # noqa: F401 + CreateNetworkResponse, + Network, + NetworkRoute, + NetworkSubnet, +) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/client.py new file mode 100644 index 000000000..d819d5805 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/client.py @@ -0,0 +1,556 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import Network, NetworkRoute, NetworkSubnet + +if TYPE_CHECKING: + from .._client import Client + + +class BoundNetwork(BoundModelBase, Network): + _client: NetworksClient + + model = Network + + def __init__(self, client: NetworksClient, data: dict, complete: bool = True): + subnets = data.get("subnets", []) + if subnets is not None: + subnets = [NetworkSubnet.from_dict(subnet) for subnet in subnets] + data["subnets"] = subnets + + routes = data.get("routes", []) + if routes is not None: + routes = [NetworkRoute.from_dict(route) for route in routes] + data["routes"] = routes + + # pylint: disable=import-outside-toplevel + from ..servers import BoundServer + + servers = data.get("servers", []) + if servers is not None: + servers = [ + BoundServer(client._client.servers, {"id": server}, complete=False) + for server in servers + ] + data["servers"] = servers + + super().__init__(client, data, complete) + + def update( + self, + name: str | None = None, + expose_routes_to_vswitch: bool | None = None, + labels: dict[str, str] | None = None, + ) -> BoundNetwork: + """Updates a network. You can update a network’s name and a networks’s labels. + + :param name: str (optional) + New name to set + :param expose_routes_to_vswitch: Optional[bool] + Indicates if the routes from this network should be exposed to the vSwitch connection. + The exposing only takes effect if a vSwitch connection is active. + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` + """ + return self._client.update( + self, + name=name, + expose_routes_to_vswitch=expose_routes_to_vswitch, + labels=labels, + ) + + def delete(self) -> bool: + """Deletes a network. + + :return: boolean + """ + return self._client.delete(self) + + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a network. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + return self._client.get_actions_list(self, status, sort, page, per_page) + + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a network. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.get_actions(self, status, sort) + + def add_subnet(self, subnet: NetworkSubnet) -> BoundAction: + """Adds a subnet entry to a network. + + :param subnet: :class:`NetworkSubnet <hcloud.networks.domain.NetworkSubnet>` + The NetworkSubnet you want to add to the Network + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.add_subnet(self, subnet=subnet) + + def delete_subnet(self, subnet: NetworkSubnet) -> BoundAction: + """Removes a subnet entry from a network + + :param subnet: :class:`NetworkSubnet <hcloud.networks.domain.NetworkSubnet>` + The NetworkSubnet you want to remove from the Network + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.delete_subnet(self, subnet=subnet) + + def add_route(self, route: NetworkRoute) -> BoundAction: + """Adds a route entry to a network. + + :param route: :class:`NetworkRoute <hcloud.networks.domain.NetworkRoute>` + The NetworkRoute you want to add to the Network + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.add_route(self, route=route) + + def delete_route(self, route: NetworkRoute) -> BoundAction: + """Removes a route entry to a network. + + :param route: :class:`NetworkRoute <hcloud.networks.domain.NetworkRoute>` + The NetworkRoute you want to remove from the Network + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.delete_route(self, route=route) + + def change_ip_range(self, ip_range: str) -> BoundAction: + """Changes the IP range of a network. + + :param ip_range: str + The new prefix for the whole network. + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_ip_range(self, ip_range=ip_range) + + def change_protection(self, delete: bool | None = None) -> BoundAction: + """Changes the protection configuration of a network. + + :param delete: boolean + If True, prevents the network from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_protection(self, delete=delete) + + +class NetworksPageResult(NamedTuple): + networks: list[BoundNetwork] + meta: Meta | None + + +class NetworksClient(ClientEntityBase): + _client: Client + + actions: ResourceActionsClient + """Networks scoped actions client + + :type: :class:`ResourceActionsClient <hcloud.actions.client.ResourceActionsClient>` + """ + + def __init__(self, client: Client): + super().__init__(client) + self.actions = ResourceActionsClient(client, "/networks") + + def get_by_id(self, id: int) -> BoundNetwork: + """Get a specific network + + :param id: int + :return: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` + """ + response = self._client.request(url=f"/networks/{id}", method="GET") + return BoundNetwork(self, response["network"]) + + def get_list( + self, + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> NetworksPageResult: + """Get a list of networks from this account + + :param name: str (optional) + Can be used to filter networks by their name. + :param label_selector: str (optional) + Can be used to filter networks by labels. The response will only contain networks matching the label selector. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundNetwork <hcloud.networks.client.BoundNetwork>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if label_selector is not None: + params["label_selector"] = label_selector + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request(url="/networks", method="GET", params=params) + + networks = [ + BoundNetwork(self, network_data) for network_data in response["networks"] + ] + return NetworksPageResult(networks, Meta.parse_meta(response)) + + def get_all( + self, + name: str | None = None, + label_selector: str | None = None, + ) -> list[BoundNetwork]: + """Get all networks from this account + + :param name: str (optional) + Can be used to filter networks by their name. + :param label_selector: str (optional) + Can be used to filter networks by labels. The response will only contain networks matching the label selector. + :return: List[:class:`BoundNetwork <hcloud.networks.client.BoundNetwork>`] + """ + return self._iter_pages(self.get_list, name=name, label_selector=label_selector) + + def get_by_name(self, name: str) -> BoundNetwork | None: + """Get network by name + + :param name: str + Used to get network by name. + :return: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` + """ + return self._get_first_by(name=name) + + def create( + self, + name: str, + ip_range: str, + subnets: list[NetworkSubnet] | None = None, + routes: list[NetworkRoute] | None = None, + expose_routes_to_vswitch: bool | None = None, + labels: dict[str, str] | None = None, + ) -> BoundNetwork: + """Creates a network with range ip_range. + + :param name: str + Name of the network + :param ip_range: str + IP range of the whole network which must span all included subnets and route destinations + :param subnets: List[:class:`NetworkSubnet <hcloud.networks.domain.NetworkSubnet>`] + Array of subnets allocated + :param routes: List[:class:`NetworkRoute <hcloud.networks.domain.NetworkRoute>`] + Array of routes set in this network + :param expose_routes_to_vswitch: Optional[bool] + Indicates if the routes from this network should be exposed to the vSwitch connection. + The exposing only takes effect if a vSwitch connection is active. + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` + """ + data: dict[str, Any] = {"name": name, "ip_range": ip_range} + if subnets is not None: + data_subnets = [] + for subnet in subnets: + data_subnet: dict[str, Any] = { + "type": subnet.type, + "ip_range": subnet.ip_range, + "network_zone": subnet.network_zone, + } + if subnet.vswitch_id is not None: + data_subnet["vswitch_id"] = subnet.vswitch_id + + data_subnets.append(data_subnet) + data["subnets"] = data_subnets + + if routes is not None: + data["routes"] = [ + {"destination": route.destination, "gateway": route.gateway} + for route in routes + ] + + if expose_routes_to_vswitch is not None: + data["expose_routes_to_vswitch"] = expose_routes_to_vswitch + + if labels is not None: + data["labels"] = labels + + response = self._client.request(url="/networks", method="POST", json=data) + + return BoundNetwork(self, response["network"]) + + def update( + self, + network: Network | BoundNetwork, + name: str | None = None, + expose_routes_to_vswitch: bool | None = None, + labels: dict[str, str] | None = None, + ) -> BoundNetwork: + """Updates a network. You can update a network’s name and a network’s labels. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param name: str (optional) + New name to set + :param expose_routes_to_vswitch: Optional[bool] + Indicates if the routes from this network should be exposed to the vSwitch connection. + The exposing only takes effect if a vSwitch connection is active. + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` + """ + data: dict[str, Any] = {} + if name is not None: + data.update({"name": name}) + + if expose_routes_to_vswitch is not None: + data["expose_routes_to_vswitch"] = expose_routes_to_vswitch + + if labels is not None: + data.update({"labels": labels}) + + response = self._client.request( + url=f"/networks/{network.id}", + method="PUT", + json=data, + ) + return BoundNetwork(self, response["network"]) + + def delete(self, network: Network | BoundNetwork) -> bool: + """Deletes a network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :return: boolean + """ + self._client.request(url=f"/networks/{network.id}", method="DELETE") + return True + + def get_actions_list( + self, + network: Network | BoundNetwork, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if status is not None: + params["status"] = status + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url=f"/networks/{network.id}/actions", + method="GET", + params=params, + ) + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + return ActionsPageResult(actions, Meta.parse_meta(response)) + + def get_actions( + self, + network: Network | BoundNetwork, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._iter_pages( + self.get_actions_list, + network, + status=status, + sort=sort, + ) + + def add_subnet( + self, + network: Network | BoundNetwork, + subnet: NetworkSubnet, + ) -> BoundAction: + """Adds a subnet entry to a network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param subnet: :class:`NetworkSubnet <hcloud.networks.domain.NetworkSubnet>` + The NetworkSubnet you want to add to the Network + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = { + "type": subnet.type, + "network_zone": subnet.network_zone, + } + if subnet.ip_range is not None: + data["ip_range"] = subnet.ip_range + if subnet.vswitch_id is not None: + data["vswitch_id"] = subnet.vswitch_id + + response = self._client.request( + url=f"/networks/{network.id}/actions/add_subnet", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def delete_subnet( + self, + network: Network | BoundNetwork, + subnet: NetworkSubnet, + ) -> BoundAction: + """Removes a subnet entry from a network + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param subnet: :class:`NetworkSubnet <hcloud.networks.domain.NetworkSubnet>` + The NetworkSubnet you want to remove from the Network + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"ip_range": subnet.ip_range} + + response = self._client.request( + url=f"/networks/{network.id}/actions/delete_subnet", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def add_route( + self, + network: Network | BoundNetwork, + route: NetworkRoute, + ) -> BoundAction: + """Adds a route entry to a network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param route: :class:`NetworkRoute <hcloud.networks.domain.NetworkRoute>` + The NetworkRoute you want to add to the Network + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = { + "destination": route.destination, + "gateway": route.gateway, + } + + response = self._client.request( + url=f"/networks/{network.id}/actions/add_route", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def delete_route( + self, + network: Network | BoundNetwork, + route: NetworkRoute, + ) -> BoundAction: + """Removes a route entry to a network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param route: :class:`NetworkRoute <hcloud.networks.domain.NetworkRoute>` + The NetworkRoute you want to remove from the Network + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = { + "destination": route.destination, + "gateway": route.gateway, + } + + response = self._client.request( + url=f"/networks/{network.id}/actions/delete_route", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def change_ip_range( + self, + network: Network | BoundNetwork, + ip_range: str, + ) -> BoundAction: + """Changes the IP range of a network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param ip_range: str + The new prefix for the whole network. + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"ip_range": ip_range} + + response = self._client.request( + url=f"/networks/{network.id}/actions/change_ip_range", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def change_protection( + self, + network: Network | BoundNetwork, + delete: bool | None = None, + ) -> BoundAction: + """Changes the protection configuration of a network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param delete: boolean + If True, prevents the network from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {} + if delete is not None: + data.update({"delete": delete}) + + response = self._client.request( + url=f"/networks/{network.id}/actions/change_protection", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/domain.py new file mode 100644 index 000000000..c307bf989 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/networks/domain.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..servers import BoundServer + from .client import BoundNetwork + + +class Network(BaseDomain): + """Network Domain + + :param id: int + ID of the network + :param name: str + Name of the network + :param ip_range: str + IPv4 prefix of the whole network + :param subnets: List[:class:`NetworkSubnet <hcloud.networks.domain.NetworkSubnet>`] + Subnets allocated in this network + :param routes: List[:class:`NetworkRoute <hcloud.networks.domain.NetworkRoute>`] + Routes set in this network + :param expose_routes_to_vswitch: bool + Indicates if the routes from this network should be exposed to the vSwitch connection. + :param servers: List[:class:`BoundServer <hcloud.servers.client.BoundServer>`] + Servers attached to this network + :param protection: dict + Protection configuration for the network + :param labels: dict + User-defined labels (key-value pairs) + """ + + __slots__ = ( + "id", + "name", + "ip_range", + "subnets", + "routes", + "expose_routes_to_vswitch", + "servers", + "protection", + "labels", + "created", + ) + + def __init__( + self, + id: int, + name: str | None = None, + created: str | None = None, + ip_range: str | None = None, + subnets: list[NetworkSubnet] | None = None, + routes: list[NetworkRoute] | None = None, + expose_routes_to_vswitch: bool | None = None, + servers: list[BoundServer] | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + ): + self.id = id + self.name = name + self.created = isoparse(created) if created else None + self.ip_range = ip_range + self.subnets = subnets + self.routes = routes + self.expose_routes_to_vswitch = expose_routes_to_vswitch + self.servers = servers + self.protection = protection + self.labels = labels + + +class NetworkSubnet(BaseDomain): + """Network Subnet Domain + + :param type: str + Type of sub network. + :param ip_range: str + Range to allocate IPs from. + :param network_zone: str + Name of network zone. + :param gateway: str + Gateway for the route. + :param vswitch_id: int + ID of the vSwitch. + """ + + TYPE_SERVER = "server" + """Subnet Type server, deprecated, use TYPE_CLOUD instead""" + TYPE_CLOUD = "cloud" + """Subnet Type cloud""" + TYPE_VSWITCH = "vswitch" + """Subnet Type vSwitch""" + __slots__ = ("type", "ip_range", "network_zone", "gateway", "vswitch_id") + + def __init__( + self, + ip_range: str, + type: str | None = None, + network_zone: str | None = None, + gateway: str | None = None, + vswitch_id: int | None = None, + ): + self.type = type + self.ip_range = ip_range + self.network_zone = network_zone + self.gateway = gateway + self.vswitch_id = vswitch_id + + +class NetworkRoute(BaseDomain): + """Network Route Domain + + :param destination: str + Destination network or host of this route. + :param gateway: str + Gateway for the route. + """ + + __slots__ = ("destination", "gateway") + + def __init__(self, destination: str, gateway: str): + self.destination = destination + self.gateway = gateway + + +class CreateNetworkResponse(BaseDomain): + """Create Network Response Domain + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` + The network which was created + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + The Action which shows the progress of the network Creation + """ + + __slots__ = ("network", "action") + + def __init__( + self, + network: BoundNetwork, + action: BoundAction, + ): + self.network = network + self.action = action diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/__init__.py new file mode 100644 index 000000000..9c25dd7f6 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundPlacementGroup, + PlacementGroupsClient, + PlacementGroupsPageResult, +) +from .domain import CreatePlacementGroupResponse, PlacementGroup # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/client.py new file mode 100644 index 000000000..fcfd86aec --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/client.py @@ -0,0 +1,214 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import BoundAction +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import CreatePlacementGroupResponse, PlacementGroup + +if TYPE_CHECKING: + from .._client import Client + + +class BoundPlacementGroup(BoundModelBase, PlacementGroup): + _client: PlacementGroupsClient + + model = PlacementGroup + + def update( + self, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundPlacementGroup: + """Updates the name or labels of a Placement Group + + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param name: str, (optional) + New Name to set + :return: :class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` + """ + return self._client.update(self, labels, name) + + def delete(self) -> bool: + """Deletes a Placement Group + + :return: boolean + """ + return self._client.delete(self) + + +class PlacementGroupsPageResult(NamedTuple): + placement_groups: list[BoundPlacementGroup] + meta: Meta | None + + +class PlacementGroupsClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundPlacementGroup: + """Returns a specific Placement Group object + + :param id: int + :return: :class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` + """ + response = self._client.request( + url=f"/placement_groups/{id}", + method="GET", + ) + return BoundPlacementGroup(self, response["placement_group"]) + + def get_list( + self, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + name: str | None = None, + sort: list[str] | None = None, + type: str | None = None, + ) -> PlacementGroupsPageResult: + """Get a list of Placement Groups + + :param label_selector: str (optional) + Can be used to filter Placement Groups by labels. The response will only contain Placement Groups matching the label selector values. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :param name: str (optional) + Can be used to filter Placement Groups by their name. + :param sort: List[str] (optional) + Choices: id name created (You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default)) + :return: (List[:class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + + params: dict[str, Any] = {} + + if label_selector is not None: + params["label_selector"] = label_selector + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + if name is not None: + params["name"] = name + if sort is not None: + params["sort"] = sort + if type is not None: + params["type"] = type + response = self._client.request( + url="/placement_groups", method="GET", params=params + ) + placement_groups = [ + BoundPlacementGroup(self, placement_group_data) + for placement_group_data in response["placement_groups"] + ] + + return PlacementGroupsPageResult(placement_groups, Meta.parse_meta(response)) + + def get_all( + self, + label_selector: str | None = None, + name: str | None = None, + sort: list[str] | None = None, + ) -> list[BoundPlacementGroup]: + """Get all Placement Groups + + :param label_selector: str (optional) + Can be used to filter Placement Groups by labels. The response will only contain Placement Groups matching the label selector values. + :param name: str (optional) + Can be used to filter Placement Groups by their name. + :param sort: List[str] (optional) + Choices: id name created (You can add one of ":asc", ":desc" to modify sort order. ( ":asc" is default)) + :return: List[:class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>`] + """ + return self._iter_pages( + self.get_list, + label_selector=label_selector, + name=name, + sort=sort, + ) + + def get_by_name(self, name: str) -> BoundPlacementGroup | None: + """Get Placement Group by name + + :param name: str + Used to get Placement Group by name + :return: class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` + """ + return self._get_first_by(name=name) + + def create( + self, + name: str, + type: str, + labels: dict[str, str] | None = None, + ) -> CreatePlacementGroupResponse: + """Creates a new Placement Group. + + :param name: str + Placement Group Name + :param type: str + Type of the Placement Group + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + + :return: :class:`CreatePlacementGroupResponse <hcloud.placement_groups.domain.CreatePlacementGroupResponse>` + """ + data: dict[str, Any] = {"name": name, "type": type} + if labels is not None: + data["labels"] = labels + response = self._client.request( + url="/placement_groups", json=data, method="POST" + ) + + action = None + if response.get("action") is not None: + action = BoundAction(self._client.actions, response["action"]) + + result = CreatePlacementGroupResponse( + placement_group=BoundPlacementGroup(self, response["placement_group"]), + action=action, + ) + return result + + def update( + self, + placement_group: PlacementGroup | BoundPlacementGroup, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundPlacementGroup: + """Updates the description or labels of a Placement Group. + + :param placement_group: :class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` or :class:`PlacementGroup <hcloud.placement_groups.domain.PlacementGroup>` + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param name: str (optional) + New name to set + :return: :class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` + """ + + data: dict[str, Any] = {} + if labels is not None: + data["labels"] = labels + if name is not None: + data["name"] = name + + response = self._client.request( + url=f"/placement_groups/{placement_group.id}", + method="PUT", + json=data, + ) + return BoundPlacementGroup(self, response["placement_group"]) + + def delete(self, placement_group: PlacementGroup | BoundPlacementGroup) -> bool: + """Deletes a Placement Group. + + :param placement_group: :class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` or :class:`PlacementGroup <hcloud.placement_groups.domain.PlacementGroup>` + :return: boolean + """ + self._client.request( + url=f"/placement_groups/{placement_group.id}", + method="DELETE", + ) + return True diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/domain.py new file mode 100644 index 000000000..16b2a390d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/placement_groups/domain.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from .client import BoundPlacementGroup + + +class PlacementGroup(BaseDomain): + """Placement Group Domain + + :param id: int + ID of the Placement Group + :param name: str + Name of the Placement Group + :param labels: dict + User-defined labels (key-value pairs) + :param servers: List[ int ] + List of server IDs assigned to the Placement Group + :param type: str + Type of the Placement Group + :param created: datetime + Point in time when the image was created + """ + + __slots__ = ("id", "name", "labels", "servers", "type", "created") + + """Placement Group type spread + spreads all servers in the group on different vhosts + """ + TYPE_SPREAD = "spread" + + def __init__( + self, + id: int | None = None, + name: str | None = None, + labels: dict[str, str] | None = None, + servers: list[int] | None = None, + type: str | None = None, + created: str | None = None, + ): + self.id = id + self.name = name + self.labels = labels + self.servers = servers + self.type = type + self.created = isoparse(created) if created else None + + +class CreatePlacementGroupResponse(BaseDomain): + """Create Placement Group Response Domain + + :param placement_group: :class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` + The Placement Group which was created + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + The Action which shows the progress of the Placement Group Creation + """ + + __slots__ = ("placement_group", "action") + + def __init__( + self, + placement_group: BoundPlacementGroup, + action: BoundAction | None, + ): + self.placement_group = placement_group + self.action = action diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/__init__.py new file mode 100644 index 000000000..d079a23b1 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundPrimaryIP, PrimaryIPsClient, PrimaryIPsPageResult # noqa: F401 +from .domain import CreatePrimaryIPResponse, PrimaryIP # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/client.py new file mode 100644 index 000000000..ece8d88f4 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/client.py @@ -0,0 +1,361 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import BoundAction, ResourceActionsClient +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import CreatePrimaryIPResponse, PrimaryIP + +if TYPE_CHECKING: + from .._client import Client + from ..datacenters import BoundDatacenter, Datacenter + + +class BoundPrimaryIP(BoundModelBase, PrimaryIP): + _client: PrimaryIPsClient + + model = PrimaryIP + + def __init__(self, client: PrimaryIPsClient, data: dict, complete: bool = True): + # pylint: disable=import-outside-toplevel + from ..datacenters import BoundDatacenter + + datacenter = data.get("datacenter", {}) + if datacenter: + data["datacenter"] = BoundDatacenter(client._client.datacenters, datacenter) + + super().__init__(client, data, complete) + + def update( + self, + auto_delete: bool | None = None, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundPrimaryIP: + """Updates the description or labels of a Primary IP. + + :param auto_delete: bool (optional) + Auto delete IP when assignee gets deleted + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param name: str (optional) + New Name to set + :return: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` + """ + return self._client.update( + self, auto_delete=auto_delete, labels=labels, name=name + ) + + def delete(self) -> bool: + """Deletes a Primary IP. If it is currently assigned to a server it will automatically get unassigned. + + :return: boolean + """ + return self._client.delete(self) + + def change_protection(self, delete: bool | None = None) -> BoundAction: + """Changes the protection configuration of the Primary IP. + + :param delete: boolean + If true, prevents the Primary IP from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_protection(self, delete) + + def assign(self, assignee_id: int, assignee_type: str) -> BoundAction: + """Assigns a Primary IP to a assignee. + + :param assignee_id: int` + Id of an assignee the Primary IP shall be assigned to + :param assignee_type: string` + Assignee type (e.g server) the Primary IP shall be assigned to + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.assign(self, assignee_id, assignee_type) + + def unassign(self) -> BoundAction: + """Unassigns a Primary IP, resulting in it being unreachable. You may assign it to a server again at a later time. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.unassign(self) + + def change_dns_ptr(self, ip: str, dns_ptr: str) -> BoundAction: + """Changes the hostname that will appear when getting the hostname belonging to this Primary IP. + + :param ip: str + The IP address for which to set the reverse DNS entry + :param dns_ptr: str + Hostname to set as a reverse DNS PTR entry, will reset to original default value if `None` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_dns_ptr(self, ip, dns_ptr) + + +class PrimaryIPsPageResult(NamedTuple): + primary_ips: list[BoundPrimaryIP] + meta: Meta | None + + +class PrimaryIPsClient(ClientEntityBase): + _client: Client + + actions: ResourceActionsClient + """Primary IPs scoped actions client + + :type: :class:`ResourceActionsClient <hcloud.actions.client.ResourceActionsClient>` + """ + + def __init__(self, client: Client): + super().__init__(client) + self.actions = ResourceActionsClient(client, "/primary_ips") + + def get_by_id(self, id: int) -> BoundPrimaryIP: + """Returns a specific Primary IP object. + + :param id: int + :return: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` + """ + response = self._client.request(url=f"/primary_ips/{id}", method="GET") + return BoundPrimaryIP(self, response["primary_ip"]) + + def get_list( + self, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + name: str | None = None, + ip: str | None = None, + ) -> PrimaryIPsPageResult: + """Get a list of primary ips from this account + + :param label_selector: str (optional) + Can be used to filter Primary IPs by labels. The response will only contain Primary IPs matching the label selectorable values. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :param name: str (optional) + Can be used to filter networks by their name. + :param ip: str (optional) + Can be used to filter resources by their ip. The response will only contain the resources matching the specified ip. + :return: (List[:class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + + if label_selector is not None: + params["label_selector"] = label_selector + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + if name is not None: + params["name"] = name + if ip is not None: + params["ip"] = ip + + response = self._client.request(url="/primary_ips", method="GET", params=params) + primary_ips = [ + BoundPrimaryIP(self, primary_ip_data) + for primary_ip_data in response["primary_ips"] + ] + + return PrimaryIPsPageResult(primary_ips, Meta.parse_meta(response)) + + def get_all( + self, + label_selector: str | None = None, + name: str | None = None, + ) -> list[BoundPrimaryIP]: + """Get all primary ips from this account + + :param label_selector: str (optional) + Can be used to filter Primary IPs by labels. The response will only contain Primary IPs matching the label selector.able values. + :param name: str (optional) + Can be used to filter networks by their name. + :return: List[:class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>`] + """ + return self._iter_pages(self.get_list, label_selector=label_selector, name=name) + + def get_by_name(self, name: str) -> BoundPrimaryIP | None: + """Get Primary IP by name + + :param name: str + Used to get Primary IP by name. + :return: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` + """ + return self._get_first_by(name=name) + + def create( + self, + type: str, + # TODO: Make the datacenter argument optional + datacenter: Datacenter | BoundDatacenter | None, + name: str, + assignee_type: str | None = "server", + assignee_id: int | None = None, + auto_delete: bool | None = False, + labels: dict | None = None, + ) -> CreatePrimaryIPResponse: + """Creates a new Primary IP assigned to a server. + + :param type: str + Primary IP type Choices: ipv4, ipv6 + :param assignee_type: str + :param assignee_id: int (optional) + :param datacenter: Datacenter + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param name: str + :param auto_delete: bool (optional) + :return: :class:`CreatePrimaryIPResponse <hcloud.primary_ips.domain.CreatePrimaryIPResponse>` + """ + + data: dict[str, Any] = { + "type": type, + "assignee_type": assignee_type, + "auto_delete": auto_delete, + "name": name, + } + if datacenter is not None: + data["datacenter"] = datacenter.id_or_name + if assignee_id is not None: + data["assignee_id"] = assignee_id + if labels is not None: + data["labels"] = labels + + response = self._client.request(url="/primary_ips", json=data, method="POST") + + action = None + if response.get("action") is not None: + action = BoundAction(self._client.actions, response["action"]) + + result = CreatePrimaryIPResponse( + primary_ip=BoundPrimaryIP(self, response["primary_ip"]), action=action + ) + return result + + def update( + self, + primary_ip: PrimaryIP | BoundPrimaryIP, + auto_delete: bool | None = None, + labels: dict[str, str] | None = None, + name: str | None = None, + ) -> BoundPrimaryIP: + """Updates the name, auto_delete or labels of a Primary IP. + + :param primary_ip: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` or :class:`PrimaryIP <hcloud.primary_ips.domain.PrimaryIP>` + :param auto_delete: bool (optional) + Delete this Primary IP when the resource it is assigned to is deleted + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :param name: str (optional) + New name to set + :return: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` + """ + data: dict[str, Any] = {} + if auto_delete is not None: + data["auto_delete"] = auto_delete + if labels is not None: + data["labels"] = labels + if name is not None: + data["name"] = name + + response = self._client.request( + url=f"/primary_ips/{primary_ip.id}", + method="PUT", + json=data, + ) + return BoundPrimaryIP(self, response["primary_ip"]) + + def delete(self, primary_ip: PrimaryIP | BoundPrimaryIP) -> bool: + """Deletes a Primary IP. If it is currently assigned to an assignee it will automatically get unassigned. + + :param primary_ip: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` or :class:`PrimaryIP <hcloud.primary_ips.domain.PrimaryIP>` + :return: boolean + """ + self._client.request( + url=f"/primary_ips/{primary_ip.id}", + method="DELETE", + ) + # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised + return True + + def change_protection( + self, + primary_ip: PrimaryIP | BoundPrimaryIP, + delete: bool | None = None, + ) -> BoundAction: + """Changes the protection configuration of the Primary IP. + + :param primary_ip: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` or :class:`PrimaryIP <hcloud.primary_ips.domain.PrimaryIP>` + :param delete: boolean + If true, prevents the Primary IP from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {} + if delete is not None: + data.update({"delete": delete}) + + response = self._client.request( + url=f"/primary_ips/{primary_ip.id}/actions/change_protection", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def assign( + self, + primary_ip: PrimaryIP | BoundPrimaryIP, + assignee_id: int, + assignee_type: str = "server", + ) -> BoundAction: + """Assigns a Primary IP to a assignee_id. + + :param primary_ip: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` or :class:`PrimaryIP <hcloud.primary_ips.domain.PrimaryIP>` + :param assignee_id: int + Assignee the Primary IP shall be assigned to + :param assignee_type: str + Assignee the Primary IP shall be assigned to + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/primary_ips/{primary_ip.id}/actions/assign", + method="POST", + json={"assignee_id": assignee_id, "assignee_type": assignee_type}, + ) + return BoundAction(self._client.actions, response["action"]) + + def unassign(self, primary_ip: PrimaryIP | BoundPrimaryIP) -> BoundAction: + """Unassigns a Primary IP, resulting in it being unreachable. You may assign it to a server again at a later time. + + :param primary_ip: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` or :class:`PrimaryIP <hcloud.primary_ips.domain.PrimaryIP>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/primary_ips/{primary_ip.id}/actions/unassign", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def change_dns_ptr( + self, + primary_ip: PrimaryIP | BoundPrimaryIP, + ip: str, + dns_ptr: str, + ) -> BoundAction: + """Changes the dns ptr that will appear when getting the dns ptr belonging to this Primary IP. + + :param primary_ip: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` or :class:`PrimaryIP <hcloud.primary_ips.domain.PrimaryIP>` + :param ip: str + The IP address for which to set the reverse DNS entry + :param dns_ptr: str + Hostname to set as a reverse DNS PTR entry, will reset to original default value if `None` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/primary_ips/{primary_ip.id}/actions/change_dns_ptr", + method="POST", + json={"ip": ip, "dns_ptr": dns_ptr}, + ) + return BoundAction(self._client.actions, response["action"]) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/domain.py new file mode 100644 index 000000000..aeb943f0a --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/primary_ips/domain.py @@ -0,0 +1,113 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..datacenters import BoundDatacenter + from .client import BoundPrimaryIP + + +class PrimaryIP(BaseDomain): + """Primary IP Domain + + :param id: int + ID of the Primary IP + :param ip: str + IP address of the Primary IP + :param type: str + Type of Primary IP. Choices: `ipv4`, `ipv6` + :param dns_ptr: List[Dict] + Array of reverse DNS entries + :param datacenter: :class:`Datacenter <hcloud.datacenters.client.BoundDatacenter>` + Datacenter the Primary IP was created in. + :param blocked: boolean + Whether the IP is blocked + :param protection: dict + Protection configuration for the Primary IP + :param labels: dict + User-defined labels (key-value pairs) + :param created: datetime + Point in time when the Primary IP was created + :param name: str + Name of the Primary IP + :param assignee_id: int + Assignee ID the Primary IP is assigned to + :param assignee_type: str + Assignee Type of entity the Primary IP is assigned to + :param auto_delete: bool + Delete the Primary IP when the Assignee it is assigned to is deleted. + """ + + __slots__ = ( + "id", + "ip", + "type", + "dns_ptr", + "datacenter", + "blocked", + "protection", + "labels", + "created", + "name", + "assignee_id", + "assignee_type", + "auto_delete", + ) + + def __init__( + self, + id: int | None = None, + type: str | None = None, + ip: str | None = None, + dns_ptr: list[dict] | None = None, + datacenter: BoundDatacenter | None = None, + blocked: bool | None = None, + protection: dict | None = None, + labels: dict[str, dict] | None = None, + created: str | None = None, + name: str | None = None, + assignee_id: int | None = None, + assignee_type: str | None = None, + auto_delete: bool | None = None, + ): + self.id = id + self.type = type + self.ip = ip + self.dns_ptr = dns_ptr + self.datacenter = datacenter + self.blocked = blocked + self.protection = protection + self.labels = labels + self.created = isoparse(created) if created else None + self.name = name + self.assignee_id = assignee_id + self.assignee_type = assignee_type + self.auto_delete = auto_delete + + +class CreatePrimaryIPResponse(BaseDomain): + """Create Primary IP Response Domain + + :param primary_ip: :class:`BoundPrimaryIP <hcloud.primary_ips.client.BoundPrimaryIP>` + The Primary IP which was created + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + The Action which shows the progress of the Primary IP Creation + """ + + __slots__ = ("primary_ip", "action") + + def __init__( + self, + primary_ip: BoundPrimaryIP, + action: BoundAction | None, + ): + self.primary_ip = primary_ip + self.action = action diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/py.typed b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/py.typed new file mode 100644 index 000000000..1242d4327 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/server_types/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/server_types/__init__.py new file mode 100644 index 000000000..1e978d094 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/server_types/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .client import ( # noqa: F401 + BoundServerType, + ServerTypesClient, + ServerTypesPageResult, +) +from .domain import ServerType # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/server_types/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/server_types/client.py new file mode 100644 index 000000000..31f56a20c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/server_types/client.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import ServerType + +if TYPE_CHECKING: + from .._client import Client + + +class BoundServerType(BoundModelBase, ServerType): + _client: ServerTypesClient + + model = ServerType + + +class ServerTypesPageResult(NamedTuple): + server_types: list[BoundServerType] + meta: Meta | None + + +class ServerTypesClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundServerType: + """Returns a specific Server Type. + + :param id: int + :return: :class:`BoundServerType <hcloud.server_types.client.BoundServerType>` + """ + response = self._client.request(url=f"/server_types/{id}", method="GET") + return BoundServerType(self, response["server_type"]) + + def get_list( + self, + name: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ServerTypesPageResult: + """Get a list of Server types + + :param name: str (optional) + Can be used to filter server type by their name. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundServerType <hcloud.server_types.client.BoundServerType>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url="/server_types", method="GET", params=params + ) + server_types = [ + BoundServerType(self, server_type_data) + for server_type_data in response["server_types"] + ] + return ServerTypesPageResult(server_types, Meta.parse_meta(response)) + + def get_all(self, name: str | None = None) -> list[BoundServerType]: + """Get all Server types + + :param name: str (optional) + Can be used to filter server type by their name. + :return: List[:class:`BoundServerType <hcloud.server_types.client.BoundServerType>`] + """ + return self._iter_pages(self.get_list, name=name) + + def get_by_name(self, name: str) -> BoundServerType | None: + """Get Server type by name + + :param name: str + Used to get Server type by name. + :return: :class:`BoundServerType <hcloud.server_types.client.BoundServerType>` + """ + return self._get_first_by(name=name) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/server_types/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/server_types/domain.py new file mode 100644 index 000000000..ab2553b10 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/server_types/domain.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +from ..core import BaseDomain, DomainIdentityMixin +from ..deprecation import DeprecationInfo + + +class ServerType(BaseDomain, DomainIdentityMixin): + """ServerType Domain + + :param id: int + ID of the server type + :param name: str + Unique identifier of the server type + :param description: str + Description of the server type + :param cores: int + Number of cpu cores a server of this type will have + :param memory: int + Memory a server of this type will have in GB + :param disk: int + Disk size a server of this type will have in GB + :param prices: Dict + Prices in different locations + :param storage_type: str + Type of server boot drive. Local has higher speed. Network has better availability. Choices: `local`, `network` + :param cpu_type: string + Type of cpu. Choices: `shared`, `dedicated` + :param architecture: string + Architecture of cpu. Choices: `x86`, `arm` + :param deprecated: bool + True if server type is deprecated. This field is deprecated. Use `deprecation` instead. + :param deprecation: :class:`DeprecationInfo <hcloud.deprecation.domain.DeprecationInfo>`, None + Describes if, when & how the resources was deprecated. If this field is set to None the resource is not + deprecated. If it has a value, it is considered deprecated. + :param included_traffic: int + Free traffic per month in bytes + """ + + __slots__ = ( + "id", + "name", + "description", + "cores", + "memory", + "disk", + "prices", + "storage_type", + "cpu_type", + "architecture", + "deprecated", + "deprecation", + "included_traffic", + ) + + def __init__( + self, + id: int | None = None, + name: str | None = None, + description: str | None = None, + cores: int | None = None, + memory: int | None = None, + disk: int | None = None, + prices: dict | None = None, + storage_type: str | None = None, + cpu_type: str | None = None, + architecture: str | None = None, + deprecated: bool | None = None, + deprecation: dict | None = None, + included_traffic: int | None = None, + ): + self.id = id + self.name = name + self.description = description + self.cores = cores + self.memory = memory + self.disk = disk + self.prices = prices + self.storage_type = storage_type + self.cpu_type = cpu_type + self.architecture = architecture + self.deprecated = deprecated + self.deprecation = ( + DeprecationInfo.from_dict(deprecation) if deprecation is not None else None + ) + self.included_traffic = included_traffic diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/__init__.py new file mode 100644 index 000000000..58c811e51 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/__init__.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from .client import BoundServer, ServersClient, ServersPageResult # noqa: F401 +from .domain import ( # noqa: F401 + CreateServerResponse, + EnableRescueResponse, + GetMetricsResponse, + IPv4Address, + IPv6Network, + PrivateNet, + PublicNetwork, + PublicNetworkFirewall, + RequestConsoleResponse, + ResetPasswordResponse, + Server, + ServerCreatePublicNetwork, +) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/client.py new file mode 100644 index 000000000..b959b9d87 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/client.py @@ -0,0 +1,1265 @@ +from __future__ import annotations + +import warnings +from datetime import datetime +from typing import TYPE_CHECKING, Any, NamedTuple + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..datacenters import BoundDatacenter +from ..firewalls import BoundFirewall +from ..floating_ips import BoundFloatingIP +from ..images import BoundImage, CreateImageResponse +from ..isos import BoundIso +from ..metrics import Metrics +from ..placement_groups import BoundPlacementGroup +from ..primary_ips import BoundPrimaryIP +from ..server_types import BoundServerType +from ..volumes import BoundVolume +from .domain import ( + CreateServerResponse, + EnableRescueResponse, + GetMetricsResponse, + IPv4Address, + IPv6Network, + MetricsType, + PrivateNet, + PublicNetwork, + PublicNetworkFirewall, + RebuildResponse, + RequestConsoleResponse, + ResetPasswordResponse, + Server, +) + +if TYPE_CHECKING: + from .._client import Client + from ..datacenters import Datacenter + from ..firewalls import Firewall + from ..images import Image + from ..isos import Iso + from ..locations import BoundLocation, Location + from ..networks import BoundNetwork, Network + from ..placement_groups import PlacementGroup + from ..server_types import ServerType + from ..ssh_keys import BoundSSHKey, SSHKey + from ..volumes import Volume + from .domain import ServerCreatePublicNetwork + + +class BoundServer(BoundModelBase, Server): + _client: ServersClient + + model = Server + + # pylint: disable=too-many-locals + def __init__(self, client: ServersClient, data: dict, complete: bool = True): + datacenter = data.get("datacenter") + if datacenter is not None: + data["datacenter"] = BoundDatacenter(client._client.datacenters, datacenter) + + volumes = data.get("volumes", []) + if volumes: + volumes = [ + BoundVolume(client._client.volumes, {"id": volume}, complete=False) + for volume in volumes + ] + data["volumes"] = volumes + + image = data.get("image", None) + if image is not None: + data["image"] = BoundImage(client._client.images, image) + + iso = data.get("iso", None) + if iso is not None: + data["iso"] = BoundIso(client._client.isos, iso) + + server_type = data.get("server_type") + if server_type is not None: + data["server_type"] = BoundServerType( + client._client.server_types, server_type + ) + + public_net = data.get("public_net") + if public_net: + ipv4_address = ( + IPv4Address.from_dict(public_net["ipv4"]) + if public_net["ipv4"] is not None + else None + ) + ipv4_primary_ip = ( + BoundPrimaryIP( + client._client.primary_ips, + {"id": public_net["ipv4"]["id"]}, + complete=False, + ) + if public_net["ipv4"] is not None + else None + ) + ipv6_network = ( + IPv6Network.from_dict(public_net["ipv6"]) + if public_net["ipv6"] is not None + else None + ) + ipv6_primary_ip = ( + BoundPrimaryIP( + client._client.primary_ips, + {"id": public_net["ipv6"]["id"]}, + complete=False, + ) + if public_net["ipv6"] is not None + else None + ) + floating_ips = [ + BoundFloatingIP( + client._client.floating_ips, {"id": floating_ip}, complete=False + ) + for floating_ip in public_net["floating_ips"] + ] + firewalls = [ + PublicNetworkFirewall( + BoundFirewall( + client._client.firewalls, {"id": firewall["id"]}, complete=False + ), + status=firewall["status"], + ) + for firewall in public_net.get("firewalls", []) + ] + data["public_net"] = PublicNetwork( + ipv4=ipv4_address, + ipv6=ipv6_network, + primary_ipv4=ipv4_primary_ip, + primary_ipv6=ipv6_primary_ip, + floating_ips=floating_ips, + firewalls=firewalls, + ) + + private_nets = data.get("private_net") + if private_nets: + # pylint: disable=import-outside-toplevel + from ..networks import BoundNetwork + + private_nets = [ + PrivateNet( + network=BoundNetwork( + client._client.networks, + {"id": private_net["network"]}, + complete=False, + ), + ip=private_net["ip"], + alias_ips=private_net["alias_ips"], + mac_address=private_net["mac_address"], + ) + for private_net in private_nets + ] + data["private_net"] = private_nets + + placement_group = data.get("placement_group") + if placement_group: + placement_group = BoundPlacementGroup( + client._client.placement_groups, placement_group + ) + data["placement_group"] = placement_group + + super().__init__(client, data, complete) + + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a server. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + return self._client.get_actions_list(self, status, sort, page, per_page) + + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a server. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.get_actions(self, status, sort) + + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundServer: + """Updates a server. You can update a server’s name and a server’s labels. + + :param name: str (optional) + New name to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundServer <hcloud.servers.client.BoundServer>` + """ + return self._client.update(self, name, labels) + + def get_metrics( + self, + type: MetricsType | list[MetricsType], + start: datetime | str, + end: datetime | str, + step: float | None = None, + ) -> GetMetricsResponse: + """Get Metrics for a Server. + + :param server: The Server to get the metrics for. + :param type: Type of metrics to get. + :param start: Start of period to get Metrics for (in ISO-8601 format). + :param end: End of period to get Metrics for (in ISO-8601 format). + :param step: Resolution of results in seconds. + """ + return self._client.get_metrics( + self, + type=type, + start=start, + end=end, + step=step, + ) + + def delete(self) -> BoundAction: + """Deletes a server. This immediately removes the server from your account, and it is no longer accessible. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.delete(self) + + def power_off(self) -> BoundAction: + """Cuts power to the server. This forcefully stops it without giving the server operating system time to gracefully stop + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.power_off(self) + + def power_on(self) -> BoundAction: + """Starts a server by turning its power on. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.power_on(self) + + def reboot(self) -> BoundAction: + """Reboots a server gracefully by sending an ACPI request. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.reboot(self) + + def reset(self) -> BoundAction: + """Cuts power to a server and starts it again. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.reset(self) + + def shutdown(self) -> BoundAction: + """Shuts down a server gracefully by sending an ACPI shutdown request. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.shutdown(self) + + def reset_password(self) -> ResetPasswordResponse: + """Resets the root password. Only works for Linux systems that are running the qemu guest agent. + + :return: :class:`ResetPasswordResponse <hcloud.servers.domain.ResetPasswordResponse>` + """ + return self._client.reset_password(self) + + def enable_rescue( + self, + type: str | None = None, + ssh_keys: list[str] | None = None, + ) -> EnableRescueResponse: + """Enable the Hetzner Rescue System for this server. + + :param type: str + Type of rescue system to boot (default: linux64) + Choices: linux64, linux32, freebsd64 + :param ssh_keys: List[str] + Array of SSH key IDs which should be injected into the rescue system. Only available for types: linux64 and linux32. + :return: :class:`EnableRescueResponse <hcloud.servers.domain.EnableRescueResponse>` + """ + return self._client.enable_rescue(self, type=type, ssh_keys=ssh_keys) + + def disable_rescue(self) -> BoundAction: + """Disables the Hetzner Rescue System for a server. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.disable_rescue(self) + + def create_image( + self, + description: str | None = None, + type: str | None = None, + labels: dict[str, str] | None = None, + ) -> CreateImageResponse: + """Creates an image (snapshot) from a server by copying the contents of its disks. + + :param description: str (optional) + Description of the image. If you do not set this we auto-generate one for you. + :param type: str (optional) + Type of image to create (default: snapshot) + Choices: snapshot, backup + :param labels: Dict[str, str] + User-defined labels (key-value pairs) + :return: :class:`CreateImageResponse <hcloud.images.domain.CreateImageResponse>` + """ + return self._client.create_image(self, description, type, labels) + + def rebuild( + self, + image: Image | BoundImage, + *, + return_response: bool = False, + ) -> RebuildResponse | BoundAction: + """Rebuilds a server overwriting its disk with the content of an image, thereby destroying all data on the target server. + + :param image: Image to use for the rebuilt server + :param return_response: Whether to return the full response or only the action. + """ + return self._client.rebuild(self, image, return_response=return_response) + + def change_type( + self, + server_type: ServerType | BoundServerType, + upgrade_disk: bool, + ) -> BoundAction: + """Changes the type (Cores, RAM and disk sizes) of a server. + + :param server_type: :class:`BoundServerType <hcloud.server_types.client.BoundServerType>` or :class:`ServerType <hcloud.server_types.domain.ServerType>` + Server type the server should migrate to + :param upgrade_disk: boolean + If false, do not upgrade the disk. This allows downgrading the server type later. + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_type(self, server_type, upgrade_disk) + + def enable_backup(self) -> BoundAction: + """Enables and configures the automatic daily backup option for the server. Enabling automatic backups will increase the price of the server by 20%. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.enable_backup(self) + + def disable_backup(self) -> BoundAction: + """Disables the automatic backup option and deletes all existing Backups for a Server. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.disable_backup(self) + + def attach_iso(self, iso: Iso | BoundIso) -> BoundAction: + """Attaches an ISO to a server. + + :param iso: :class:`BoundIso <hcloud.isos.client.BoundIso>` or :class:`Server <hcloud.isos.domain.Iso>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.attach_iso(self, iso) + + def detach_iso(self) -> BoundAction: + """Detaches an ISO from a server. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.detach_iso(self) + + def change_dns_ptr(self, ip: str, dns_ptr: str | None) -> BoundAction: + """Changes the hostname that will appear when getting the hostname belonging to the primary IPs (ipv4 and ipv6) of this server. + + :param ip: str + The IP address for which to set the reverse DNS entry + :param dns_ptr: + Hostname to set as a reverse DNS PTR entry, will reset to original default value if `None` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_dns_ptr(self, ip, dns_ptr) + + def change_protection( + self, + delete: bool | None = None, + rebuild: bool | None = None, + ) -> BoundAction: + """Changes the protection configuration of the server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param delete: boolean + If true, prevents the server from being deleted (currently delete and rebuild attribute needs to have the same value) + :param rebuild: boolean + If true, prevents the server from being rebuilt (currently delete and rebuild attribute needs to have the same value) + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_protection(self, delete, rebuild) + + def request_console(self) -> RequestConsoleResponse: + """Requests credentials for remote access via vnc over websocket to keyboard, monitor, and mouse for a server. + + :return: :class:`RequestConsoleResponse <hcloud.servers.domain.RequestConsoleResponse>` + """ + return self._client.request_console(self) + + def attach_to_network( + self, + network: Network | BoundNetwork, + ip: str | None = None, + alias_ips: list[str] | None = None, + ) -> BoundAction: + """Attaches a server to a network + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param ip: str + IP to request to be assigned to this server + :param alias_ips: List[str] + New alias IPs to set for this server. + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.attach_to_network(self, network, ip, alias_ips) + + def detach_from_network(self, network: Network | BoundNetwork) -> BoundAction: + """Detaches a server from a network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.detach_from_network(self, network) + + def change_alias_ips( + self, + network: Network | BoundNetwork, + alias_ips: list[str], + ) -> BoundAction: + """Changes the alias IPs of an already attached network. + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param alias_ips: List[str] + New alias IPs to set for this server. + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_alias_ips(self, network, alias_ips) + + def add_to_placement_group( + self, + placement_group: PlacementGroup | BoundPlacementGroup, + ) -> BoundAction: + """Adds a server to a placement group. + + :param placement_group: :class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` or :class:`Network <hcloud.placement_groups.domain.PlacementGroup>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.add_to_placement_group(self, placement_group) + + def remove_from_placement_group(self) -> BoundAction: + """Removes a server from a placement group. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.remove_from_placement_group(self) + + +class ServersPageResult(NamedTuple): + servers: list[BoundServer] + meta: Meta | None + + +class ServersClient(ClientEntityBase): + _client: Client + + actions: ResourceActionsClient + """Servers scoped actions client + + :type: :class:`ResourceActionsClient <hcloud.actions.client.ResourceActionsClient>` + """ + + def __init__(self, client: Client): + super().__init__(client) + self.actions = ResourceActionsClient(client, "/servers") + + def get_by_id(self, id: int) -> BoundServer: + """Get a specific server + + :param id: int + :return: :class:`BoundServer <hcloud.servers.client.BoundServer>` + """ + response = self._client.request(url=f"/servers/{id}", method="GET") + return BoundServer(self, response["server"]) + + def get_list( + self, + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + ) -> ServersPageResult: + """Get a list of servers from this account + + :param name: str (optional) + Can be used to filter servers by their name. + :param label_selector: str (optional) + Can be used to filter servers by labels. The response will only contain servers matching the label selector. + :param status: List[str] (optional) + Can be used to filter servers by their status. The response will only contain servers matching the status. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundServer <hcloud.servers.client.BoundServer>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if label_selector is not None: + params["label_selector"] = label_selector + if status is not None: + params["status"] = status + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request(url="/servers", method="GET", params=params) + + ass_servers = [ + BoundServer(self, server_data) for server_data in response["servers"] + ] + return ServersPageResult(ass_servers, Meta.parse_meta(response)) + + def get_all( + self, + name: str | None = None, + label_selector: str | None = None, + status: list[str] | None = None, + ) -> list[BoundServer]: + """Get all servers from this account + + :param name: str (optional) + Can be used to filter servers by their name. + :param label_selector: str (optional) + Can be used to filter servers by labels. The response will only contain servers matching the label selector. + :param status: List[str] (optional) + Can be used to filter servers by their status. The response will only contain servers matching the status. + :return: List[:class:`BoundServer <hcloud.servers.client.BoundServer>`] + """ + return self._iter_pages( + self.get_list, + name=name, + label_selector=label_selector, + status=status, + ) + + def get_by_name(self, name: str) -> BoundServer | None: + """Get server by name + + :param name: str + Used to get server by name. + :return: :class:`BoundServer <hcloud.servers.client.BoundServer>` + """ + return self._get_first_by(name=name) + + # pylint: disable=too-many-branches,too-many-locals + def create( + self, + name: str, + server_type: ServerType | BoundServerType, + image: Image, + ssh_keys: list[SSHKey | BoundSSHKey] | None = None, + volumes: list[Volume | BoundVolume] | None = None, + firewalls: list[Firewall | BoundFirewall] | None = None, + networks: list[Network | BoundNetwork] | None = None, + user_data: str | None = None, + labels: dict[str, str] | None = None, + location: Location | BoundLocation | None = None, + datacenter: Datacenter | BoundDatacenter | None = None, + start_after_create: bool | None = True, + automount: bool | None = None, + placement_group: PlacementGroup | BoundPlacementGroup | None = None, + public_net: ServerCreatePublicNetwork | None = None, + ) -> CreateServerResponse: + """Creates a new server. Returns preliminary information about the server as well as an action that covers progress of creation. + + :param name: str + Name of the server to create (must be unique per project and a valid hostname as per RFC 1123) + :param server_type: :class:`BoundServerType <hcloud.server_types.client.BoundServerType>` or :class:`ServerType <hcloud.server_types.domain.ServerType>` + Server type this server should be created with + :param image: :class:`BoundImage <hcloud.images.client.BoundImage>` or :class:`Image <hcloud.images.domain.Image>` + Image the server is created from + :param ssh_keys: List[:class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>` or :class:`SSHKey <hcloud.ssh_keys.domain.SSHKey>`] (optional) + SSH keys which should be injected into the server at creation time + :param volumes: List[:class:`BoundVolume <hcloud.volumes.client.BoundVolume>` or :class:`Volume <hcloud.volumes.domain.Volume>`] (optional) + Volumes which should be attached to the server at the creation time. Volumes must be in the same location. + :param networks: List[:class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>`] (optional) + Networks which should be attached to the server at the creation time. + :param user_data: str (optional) + Cloud-Init user data to use during server creation. This field is limited to 32KiB. + :param labels: Dict[str,str] (optional) + User-defined labels (key-value pairs) + :param location: :class:`BoundLocation <hcloud.locations.client.BoundLocation>` or :class:`Location <hcloud.locations.domain.Location>` + :param datacenter: :class:`BoundDatacenter <hcloud.datacenters.client.BoundDatacenter>` or :class:`Datacenter <hcloud.datacenters.domain.Datacenter>` + :param start_after_create: boolean (optional) + Start Server right after creation. Defaults to True. + :param automount: boolean (optional) + Auto mount volumes after attach. + :param placement_group: :class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` or :class:`Location <hcloud.placement_groups.domain.PlacementGroup>` + Placement Group where server should be added during creation + :param public_net: :class:`ServerCreatePublicNetwork <hcloud.servers.domain.ServerCreatePublicNetwork>` + Options to configure the public network of a server on creation + :return: :class:`CreateServerResponse <hcloud.servers.domain.CreateServerResponse>` + """ + data: dict[str, Any] = { + "name": name, + "server_type": server_type.id_or_name, + "start_after_create": start_after_create, + "image": image.id_or_name, + } + + if location is not None: + data["location"] = location.id_or_name + if datacenter is not None: + data["datacenter"] = datacenter.id_or_name + if ssh_keys is not None: + data["ssh_keys"] = [ssh_key.id_or_name for ssh_key in ssh_keys] + if volumes is not None: + data["volumes"] = [volume.id for volume in volumes] + if networks is not None: + data["networks"] = [network.id for network in networks] + if firewalls is not None: + data["firewalls"] = [{"firewall": firewall.id} for firewall in firewalls] + if user_data is not None: + data["user_data"] = user_data + if labels is not None: + data["labels"] = labels + if automount is not None: + data["automount"] = automount + if placement_group is not None: + data["placement_group"] = placement_group.id + + if public_net is not None: + data_public_net: dict[str, Any] = { + "enable_ipv4": public_net.enable_ipv4, + "enable_ipv6": public_net.enable_ipv6, + } + if public_net.ipv4 is not None: + data_public_net["ipv4"] = public_net.ipv4.id + if public_net.ipv6 is not None: + data_public_net["ipv6"] = public_net.ipv6.id + data["public_net"] = data_public_net + + response = self._client.request(url="/servers", method="POST", json=data) + + result = CreateServerResponse( + server=BoundServer(self, response["server"]), + action=BoundAction(self._client.actions, response["action"]), + next_actions=[ + BoundAction(self._client.actions, action) + for action in response["next_actions"] + ], + root_password=response["root_password"], + ) + return result + + def get_actions_list( + self, + server: Server | BoundServer, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if status is not None: + params["status"] = status + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url=f"/servers/{server.id}/actions", + method="GET", + params=params, + ) + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + return ActionsPageResult(actions, Meta.parse_meta(response)) + + def get_actions( + self, + server: Server | BoundServer, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._iter_pages( + self.get_actions_list, + server, + status=status, + sort=sort, + ) + + def update( + self, + server: Server | BoundServer, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundServer: + """Updates a server. You can update a server’s name and a server’s labels. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param name: str (optional) + New name to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundServer <hcloud.servers.client.BoundServer>` + """ + data: dict[str, Any] = {} + if name is not None: + data.update({"name": name}) + if labels is not None: + data.update({"labels": labels}) + response = self._client.request( + url=f"/servers/{server.id}", + method="PUT", + json=data, + ) + return BoundServer(self, response["server"]) + + def get_metrics( + self, + server: Server | BoundServer, + type: MetricsType | list[MetricsType], + start: datetime | str, + end: datetime | str, + step: float | None = None, + ) -> GetMetricsResponse: + """Get Metrics for a Server. + + :param server: The Server to get the metrics for. + :param type: Type of metrics to get. + :param start: Start of period to get Metrics for (in ISO-8601 format). + :param end: End of period to get Metrics for (in ISO-8601 format). + :param step: Resolution of results in seconds. + """ + if not isinstance(type, list): + type = [type] + if isinstance(start, str): + start = isoparse(start) + if isinstance(end, str): + end = isoparse(end) + + params: dict[str, Any] = { + "type": ",".join(type), + "start": start.isoformat(), + "end": end.isoformat(), + } + if step is not None: + params["step"] = step + + response = self._client.request( + url=f"/servers/{server.id}/metrics", + method="GET", + params=params, + ) + return GetMetricsResponse( + metrics=Metrics(**response["metrics"]), + ) + + def delete(self, server: Server | BoundServer) -> BoundAction: + """Deletes a server. This immediately removes the server from your account, and it is no longer accessible. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request(url=f"/servers/{server.id}", method="DELETE") + return BoundAction(self._client.actions, response["action"]) + + def power_off(self, server: Server | BoundServer) -> BoundAction: + """Cuts power to the server. This forcefully stops it without giving the server operating system time to gracefully stop + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/poweroff", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def power_on(self, server: Server | BoundServer) -> BoundAction: + """Starts a server by turning its power on. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/poweron", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def reboot(self, server: Server | BoundServer) -> BoundAction: + """Reboots a server gracefully by sending an ACPI request. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/reboot", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def reset(self, server: Server | BoundServer) -> BoundAction: + """Cuts power to a server and starts it again. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/reset", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def shutdown(self, server: Server | BoundServer) -> BoundAction: + """Shuts down a server gracefully by sending an ACPI shutdown request. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/shutdown", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def reset_password(self, server: Server | BoundServer) -> ResetPasswordResponse: + """Resets the root password. Only works for Linux systems that are running the qemu guest agent. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`ResetPasswordResponse <hcloud.servers.domain.ResetPasswordResponse>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/reset_password", + method="POST", + ) + return ResetPasswordResponse( + action=BoundAction(self._client.actions, response["action"]), + root_password=response["root_password"], + ) + + def change_type( + self, + server: Server | BoundServer, + server_type: ServerType | BoundServerType, + upgrade_disk: bool, + ) -> BoundAction: + """Changes the type (Cores, RAM and disk sizes) of a server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param server_type: :class:`BoundServerType <hcloud.server_types.client.BoundServerType>` or :class:`ServerType <hcloud.server_types.domain.ServerType>` + Server type the server should migrate to + :param upgrade_disk: boolean + If false, do not upgrade the disk. This allows downgrading the server type later. + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = { + "server_type": server_type.id_or_name, + "upgrade_disk": upgrade_disk, + } + response = self._client.request( + url=f"/servers/{server.id}/actions/change_type", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def enable_rescue( + self, + server: Server | BoundServer, + type: str | None = None, + ssh_keys: list[str] | None = None, + ) -> EnableRescueResponse: + """Enable the Hetzner Rescue System for this server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param type: str + Type of rescue system to boot (default: linux64) + Choices: linux64, linux32, freebsd64 + :param ssh_keys: List[str] + Array of SSH key IDs which should be injected into the rescue system. Only available for types: linux64 and linux32. + :return: :class:`EnableRescueResponse <hcloud.servers.domain.EnableRescueResponse>` + """ + data: dict[str, Any] = {"type": type} + if ssh_keys is not None: + data.update({"ssh_keys": ssh_keys}) + + response = self._client.request( + url=f"/servers/{server.id}/actions/enable_rescue", + method="POST", + json=data, + ) + return EnableRescueResponse( + action=BoundAction(self._client.actions, response["action"]), + root_password=response["root_password"], + ) + + def disable_rescue(self, server: Server | BoundServer) -> BoundAction: + """Disables the Hetzner Rescue System for a server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/disable_rescue", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def create_image( + self, + server: Server | BoundServer, + description: str | None = None, + type: str | None = None, + labels: dict[str, str] | None = None, + ) -> CreateImageResponse: + """Creates an image (snapshot) from a server by copying the contents of its disks. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param description: str (optional) + Description of the image. If you do not set this we auto-generate one for you. + :param type: str (optional) + Type of image to create (default: snapshot) + Choices: snapshot, backup + :param labels: Dict[str, str] + User-defined labels (key-value pairs) + :return: :class:`CreateImageResponse <hcloud.images.domain.CreateImageResponse>` + """ + data: dict[str, Any] = {} + if description is not None: + data.update({"description": description}) + + if type is not None: + data.update({"type": type}) + + if labels is not None: + data.update({"labels": labels}) + + response = self._client.request( + url=f"/servers/{server.id}/actions/create_image", + method="POST", + json=data, + ) + return CreateImageResponse( + action=BoundAction(self._client.actions, response["action"]), + image=BoundImage(self._client.images, response["image"]), + ) + + def rebuild( + self, + server: Server | BoundServer, + image: Image | BoundImage, + *, + return_response: bool = False, + ) -> RebuildResponse | BoundAction: + """Rebuilds a server overwriting its disk with the content of an image, thereby destroying all data on the target server. + + :param server: Server to rebuild + :param image: Image to use for the rebuilt server + :param return_response: Whether to return the full response or only the action. + """ + data: dict[str, Any] = {"image": image.id_or_name} + response = self._client.request( + url=f"/servers/{server.id}/actions/rebuild", + method="POST", + json=data, + ) + + rebuild_response = RebuildResponse( + action=BoundAction(self._client.actions, response["action"]), + root_password=response.get("root_password"), + ) + + if not return_response: + warnings.warn( + "Returning only the 'action' is deprecated, please set the " + "'return_response' keyword argument to 'True' to return the full " + "rebuild response and update your code accordingly.", + DeprecationWarning, + stacklevel=2, + ) + return rebuild_response.action + return rebuild_response + + def enable_backup(self, server: Server | BoundServer) -> BoundAction: + """Enables and configures the automatic daily backup option for the server. Enabling automatic backups will increase the price of the server by 20%. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/enable_backup", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def disable_backup(self, server: Server | BoundServer) -> BoundAction: + """Disables the automatic backup option and deletes all existing Backups for a Server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/disable_backup", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def attach_iso( + self, + server: Server | BoundServer, + iso: Iso | BoundIso, + ) -> BoundAction: + """Attaches an ISO to a server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param iso: :class:`BoundIso <hcloud.isos.client.BoundIso>` or :class:`Server <hcloud.isos.domain.Iso>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"iso": iso.id_or_name} + response = self._client.request( + url=f"/servers/{server.id}/actions/attach_iso", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def detach_iso(self, server: Server | BoundServer) -> BoundAction: + """Detaches an ISO from a server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/detach_iso", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) + + def change_dns_ptr( + self, + server: Server | BoundServer, + ip: str, + dns_ptr: str | None, + ) -> BoundAction: + """Changes the hostname that will appear when getting the hostname belonging to the primary IPs (ipv4 and ipv6) of this server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param ip: str + The IP address for which to set the reverse DNS entry + :param dns_ptr: + Hostname to set as a reverse DNS PTR entry, will reset to original default value if `None` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"ip": ip, "dns_ptr": dns_ptr} + response = self._client.request( + url=f"/servers/{server.id}/actions/change_dns_ptr", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def change_protection( + self, + server: Server | BoundServer, + delete: bool | None = None, + rebuild: bool | None = None, + ) -> BoundAction: + """Changes the protection configuration of the server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param delete: boolean + If true, prevents the server from being deleted (currently delete and rebuild attribute needs to have the same value) + :param rebuild: boolean + If true, prevents the server from being rebuilt (currently delete and rebuild attribute needs to have the same value) + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {} + if delete is not None: + data.update({"delete": delete}) + if rebuild is not None: + data.update({"rebuild": rebuild}) + + response = self._client.request( + url=f"/servers/{server.id}/actions/change_protection", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def request_console(self, server: Server | BoundServer) -> RequestConsoleResponse: + """Requests credentials for remote access via vnc over websocket to keyboard, monitor, and mouse for a server. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`RequestConsoleResponse <hcloud.servers.domain.RequestConsoleResponse>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/request_console", + method="POST", + ) + return RequestConsoleResponse( + action=BoundAction(self._client.actions, response["action"]), + wss_url=response["wss_url"], + password=response["password"], + ) + + def attach_to_network( + self, + server: Server | BoundServer, + network: Network | BoundNetwork, + ip: str | None = None, + alias_ips: list[str] | None = None, + ) -> BoundAction: + """Attaches a server to a network + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param ip: str + IP to request to be assigned to this server + :param alias_ips: List[str] + New alias IPs to set for this server. + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"network": network.id} + if ip is not None: + data.update({"ip": ip}) + if alias_ips is not None: + data.update({"alias_ips": alias_ips}) + response = self._client.request( + url=f"/servers/{server.id}/actions/attach_to_network", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def detach_from_network( + self, + server: Server | BoundServer, + network: Network | BoundNetwork, + ) -> BoundAction: + """Detaches a server from a network. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"network": network.id} + response = self._client.request( + url=f"/servers/{server.id}/actions/detach_from_network", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def change_alias_ips( + self, + server: Server | BoundServer, + network: Network | BoundNetwork, + alias_ips: list[str], + ) -> BoundAction: + """Changes the alias IPs of an already attached network. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` or :class:`Network <hcloud.networks.domain.Network>` + :param alias_ips: List[str] + New alias IPs to set for this server. + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"network": network.id, "alias_ips": alias_ips} + response = self._client.request( + url=f"/servers/{server.id}/actions/change_alias_ips", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def add_to_placement_group( + self, + server: Server | BoundServer, + placement_group: PlacementGroup | BoundPlacementGroup, + ) -> BoundAction: + """Adds a server to a placement group. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param placement_group: :class:`BoundPlacementGroup <hcloud.placement_groups.client.BoundPlacementGroup>` or :class:`Network <hcloud.placement_groups.domain.PlacementGroup>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"placement_group": str(placement_group.id)} + response = self._client.request( + url=f"/servers/{server.id}/actions/add_to_placement_group", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) + + def remove_from_placement_group(self, server: Server | BoundServer) -> BoundAction: + """Removes a server from a placement group. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + response = self._client.request( + url=f"/servers/{server.id}/actions/remove_from_placement_group", + method="POST", + ) + return BoundAction(self._client.actions, response["action"]) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/domain.py new file mode 100644 index 000000000..0a0d34688 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/servers/domain.py @@ -0,0 +1,455 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Literal + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..datacenters import BoundDatacenter + from ..firewalls import BoundFirewall + from ..floating_ips import BoundFloatingIP + from ..images import BoundImage + from ..isos import BoundIso + from ..metrics import Metrics + from ..networks import BoundNetwork + from ..placement_groups import BoundPlacementGroup + from ..primary_ips import BoundPrimaryIP, PrimaryIP + from ..server_types import BoundServerType + from ..volumes import BoundVolume + from .client import BoundServer + + +class Server(BaseDomain): + """Server Domain + + :param id: int + ID of the server + :param name: str + Name of the server (must be unique per project and a valid hostname as per RFC 1123) + :param status: str + Status of the server Choices: `running`, `initializing`, `starting`, `stopping`, `off`, `deleting`, `migrating`, `rebuilding`, `unknown` + :param created: datetime + Point in time when the server was created + :param public_net: :class:`PublicNetwork <hcloud.servers.domain.PublicNetwork>` + Public network information. + :param server_type: :class:`BoundServerType <hcloud.server_types.client.BoundServerType>` + :param datacenter: :class:`BoundDatacenter <hcloud.datacenters.client.BoundDatacenter>` + :param image: :class:`BoundImage <hcloud.images.client.BoundImage>`, None + :param iso: :class:`BoundIso <hcloud.isos.client.BoundIso>`, None + :param rescue_enabled: bool + True if rescue mode is enabled: Server will then boot into rescue system on next reboot. + :param locked: bool + True if server has been locked and is not available to user. + :param backup_window: str, None + Time window (UTC) in which the backup will run, or None if the backups are not enabled + :param outgoing_traffic: int, None + Outbound Traffic for the current billing period in bytes + :param ingoing_traffic: int, None + Inbound Traffic for the current billing period in bytes + :param included_traffic: int + Free Traffic for the current billing period in bytes + :param primary_disk_size: int + Size of the primary Disk + :param protection: dict + Protection configuration for the server + :param labels: dict + User-defined labels (key-value pairs) + :param volumes: List[:class:`BoundVolume <hcloud.volumes.client.BoundVolume>`] + Volumes assigned to this server. + :param private_net: List[:class:`PrivateNet <hcloud.servers.domain.PrivateNet>`] + Private networks information. + """ + + STATUS_RUNNING = "running" + """Server Status running""" + STATUS_INIT = "initializing" + """Server Status initializing""" + STATUS_STARTING = "starting" + """Server Status starting""" + STATUS_STOPPING = "stopping" + """Server Status stopping""" + STATUS_OFF = "off" + """Server Status off""" + STATUS_DELETING = "deleting" + """Server Status deleting""" + STATUS_MIGRATING = "migrating" + """Server Status migrating""" + STATUS_REBUILDING = "rebuilding" + """Server Status rebuilding""" + STATUS_UNKNOWN = "unknown" + """Server Status unknown""" + __slots__ = ( + "id", + "name", + "status", + "public_net", + "server_type", + "datacenter", + "image", + "iso", + "rescue_enabled", + "locked", + "backup_window", + "outgoing_traffic", + "ingoing_traffic", + "included_traffic", + "protection", + "labels", + "volumes", + "private_net", + "created", + "primary_disk_size", + "placement_group", + ) + + # pylint: disable=too-many-locals + def __init__( + self, + id: int, + name: str | None = None, + status: str | None = None, + created: str | None = None, + public_net: PublicNetwork | None = None, + server_type: BoundServerType | None = None, + datacenter: BoundDatacenter | None = None, + image: BoundImage | None = None, + iso: BoundIso | None = None, + rescue_enabled: bool | None = None, + locked: bool | None = None, + backup_window: str | None = None, + outgoing_traffic: int | None = None, + ingoing_traffic: int | None = None, + included_traffic: int | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + volumes: list[BoundVolume] | None = None, + private_net: list[PrivateNet] | None = None, + primary_disk_size: int | None = None, + placement_group: BoundPlacementGroup | None = None, + ): + self.id = id + self.name = name + self.status = status + self.created = isoparse(created) if created else None + self.public_net = public_net + self.server_type = server_type + self.datacenter = datacenter + self.image = image + self.iso = iso + self.rescue_enabled = rescue_enabled + self.locked = locked + self.backup_window = backup_window + self.outgoing_traffic = outgoing_traffic + self.ingoing_traffic = ingoing_traffic + self.included_traffic = included_traffic + self.protection = protection + self.labels = labels + self.volumes = volumes + self.private_net = private_net + self.primary_disk_size = primary_disk_size + self.placement_group = placement_group + + +class CreateServerResponse(BaseDomain): + """Create Server Response Domain + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` + The created server + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + Shows the progress of the server creation + :param next_actions: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + Additional actions like a `start_server` action after the server creation + :param root_password: str, None + The root password of the server if no SSH-Key was given on server creation + """ + + __slots__ = ("server", "action", "next_actions", "root_password") + + def __init__( + self, + server: BoundServer, + action: BoundAction, + next_actions: list[BoundAction], + root_password: str | None, + ): + self.server = server + self.action = action + self.next_actions = next_actions + self.root_password = root_password + + +class ResetPasswordResponse(BaseDomain): + """Reset Password Response Domain + + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + Shows the progress of the server passwort reset action + :param root_password: str + The root password of the server + """ + + __slots__ = ("action", "root_password") + + def __init__( + self, + action: BoundAction, + root_password: str, + ): + self.action = action + self.root_password = root_password + + +class EnableRescueResponse(BaseDomain): + """Enable Rescue Response Domain + + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + Shows the progress of the server enable rescue action + :param root_password: str + The root password of the server in the rescue mode + """ + + __slots__ = ("action", "root_password") + + def __init__( + self, + action: BoundAction, + root_password: str, + ): + self.action = action + self.root_password = root_password + + +class RequestConsoleResponse(BaseDomain): + """Request Console Response Domain + + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + Shows the progress of the server request console action + :param wss_url: str + URL of websocket proxy to use. This includes a token which is valid for a limited time only. + :param password: str + VNC password to use for this connection. This password only works in combination with a wss_url with valid token. + """ + + __slots__ = ("action", "wss_url", "password") + + def __init__( + self, + action: BoundAction, + wss_url: str, + password: str, + ): + self.action = action + self.wss_url = wss_url + self.password = password + + +class RebuildResponse(BaseDomain): + """Rebuild Response Domain + + :param action: Shows the progress of the server rebuild action + :param root_password: The root password of the server when not using SSH keys + """ + + __slots__ = ("action", "root_password") + + def __init__( + self, + action: BoundAction, + root_password: str | None, + ): + self.action = action + self.root_password = root_password + + +class PublicNetwork(BaseDomain): + """Public Network Domain + + :param ipv4: :class:`IPv4Address <hcloud.servers.domain.IPv4Address>` + :param ipv6: :class:`IPv6Network <hcloud.servers.domain.IPv6Network>` + :param floating_ips: List[:class:`BoundFloatingIP <hcloud.floating_ips.client.BoundFloatingIP>`] + :param primary_ipv4: :class:`BoundPrimaryIP <hcloud.primary_ips.domain.BoundPrimaryIP>` + :param primary_ipv6: :class:`BoundPrimaryIP <hcloud.primary_ips.domain.BoundPrimaryIP>` + :param firewalls: List[:class:`PublicNetworkFirewall <hcloud.servers.client.PublicNetworkFirewall>`] + """ + + __slots__ = ( + "ipv4", + "ipv6", + "floating_ips", + "firewalls", + "primary_ipv4", + "primary_ipv6", + ) + + def __init__( + self, + ipv4: IPv4Address, + ipv6: IPv6Network, + floating_ips: list[BoundFloatingIP], + primary_ipv4: BoundPrimaryIP | None, + primary_ipv6: BoundPrimaryIP | None, + firewalls: list[PublicNetworkFirewall] | None = None, + ): + self.ipv4 = ipv4 + self.ipv6 = ipv6 + self.floating_ips = floating_ips + self.firewalls = firewalls + self.primary_ipv4 = primary_ipv4 + self.primary_ipv6 = primary_ipv6 + + +class PublicNetworkFirewall(BaseDomain): + """Public Network Domain + + :param firewall: :class:`BoundFirewall <hcloud.firewalls.domain.BoundFirewall>` + :param status: str + """ + + __slots__ = ("firewall", "status") + + STATUS_APPLIED = "applied" + """Public Network Firewall Status applied""" + STATUS_PENDING = "pending" + """Public Network Firewall Status pending""" + + def __init__( + self, + firewall: BoundFirewall, + status: str, + ): + self.firewall = firewall + self.status = status + + +class IPv4Address(BaseDomain): + """IPv4 Address Domain + + :param ip: str + The IPv4 Address + :param blocked: bool + Determine if the IP is blocked + :param dns_ptr: str + DNS PTR for the ip + """ + + __slots__ = ("ip", "blocked", "dns_ptr") + + def __init__( + self, + ip: str, + blocked: bool, + dns_ptr: str, + ): + self.ip = ip + self.blocked = blocked + self.dns_ptr = dns_ptr + + +class IPv6Network(BaseDomain): + """IPv6 Network Domain + + :param ip: str + The IPv6 Network as CIDR Notation + :param blocked: bool + Determine if the Network is blocked + :param dns_ptr: dict + DNS PTR Records for the Network as Dict + :param network: str + The network without the network mask + :param network_mask: str + The network mask + """ + + __slots__ = ("ip", "blocked", "dns_ptr", "network", "network_mask") + + def __init__( + self, + ip: str, + blocked: bool, + dns_ptr: list, + ): + self.ip = ip + self.blocked = blocked + self.dns_ptr = dns_ptr + ip_parts = self.ip.split("/") # 2001:db8::/64 to 2001:db8:: and 64 + self.network = ip_parts[0] + self.network_mask = ip_parts[1] + + +class PrivateNet(BaseDomain): + """PrivateNet Domain + + :param network: :class:`BoundNetwork <hcloud.networks.client.BoundNetwork>` + The network the server is attached to + :param ip: str + The main IP Address of the server in the Network + :param alias_ips: List[str] + The alias ips for a server + :param mac_address: str + The mac address of the interface on the server + """ + + __slots__ = ("network", "ip", "alias_ips", "mac_address") + + def __init__( + self, + network: BoundNetwork, + ip: str, + alias_ips: list[str], + mac_address: str, + ): + self.network = network + self.ip = ip + self.alias_ips = alias_ips + self.mac_address = mac_address + + +class ServerCreatePublicNetwork(BaseDomain): + """Server Create Public Network Domain + + :param ipv4: Optional[:class:`PrimaryIP <hcloud.primary_ips.domain.PrimaryIP>`] + :param ipv6: Optional[:class:`PrimaryIP <hcloud.primary_ips.domain.PrimaryIP>`] + :param enable_ipv4: bool + :param enable_ipv6: bool + """ + + __slots__ = ("ipv4", "ipv6", "enable_ipv4", "enable_ipv6") + + def __init__( + self, + ipv4: PrimaryIP | None = None, + ipv6: PrimaryIP | None = None, + enable_ipv4: bool = True, + enable_ipv6: bool = True, + ): + self.ipv4 = ipv4 + self.ipv6 = ipv6 + self.enable_ipv4 = enable_ipv4 + self.enable_ipv6 = enable_ipv6 + + +MetricsType = Literal[ + "cpu", + "disk", + "network", +] + + +class GetMetricsResponse(BaseDomain): + """Get a Server Metrics Response Domain + + :param metrics: The Server metrics + """ + + __slots__ = ("metrics",) + + def __init__( + self, + metrics: Metrics, + ): + self.metrics = metrics diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/ssh_keys/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/ssh_keys/__init__.py new file mode 100644 index 000000000..251559060 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/ssh_keys/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundSSHKey, SSHKeysClient, SSHKeysPageResult # noqa: F401 +from .domain import SSHKey # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/ssh_keys/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/ssh_keys/client.py new file mode 100644 index 000000000..69c1683d7 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/ssh_keys/client.py @@ -0,0 +1,194 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..core import BoundModelBase, ClientEntityBase, Meta +from .domain import SSHKey + +if TYPE_CHECKING: + from .._client import Client + + +class BoundSSHKey(BoundModelBase, SSHKey): + _client: SSHKeysClient + + model = SSHKey + + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundSSHKey: + """Updates an SSH key. You can update an SSH key name and an SSH key labels. + + :param description: str (optional) + New Description to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>` + """ + return self._client.update(self, name, labels) + + def delete(self) -> bool: + """Deletes an SSH key. It cannot be used anymore. + :return: boolean + """ + return self._client.delete(self) + + +class SSHKeysPageResult(NamedTuple): + ssh_keys: list[BoundSSHKey] + meta: Meta | None + + +class SSHKeysClient(ClientEntityBase): + _client: Client + + def get_by_id(self, id: int) -> BoundSSHKey: + """Get a specific SSH Key by its ID + + :param id: int + :return: :class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>` + """ + response = self._client.request(url=f"/ssh_keys/{id}", method="GET") + return BoundSSHKey(self, response["ssh_key"]) + + def get_list( + self, + name: str | None = None, + fingerprint: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> SSHKeysPageResult: + """Get a list of SSH keys from the account + + :param name: str (optional) + Can be used to filter SSH keys by their name. The response will only contain the SSH key matching the specified name. + :param fingerprint: str (optional) + Can be used to filter SSH keys by their fingerprint. The response will only contain the SSH key matching the specified fingerprint. + :param label_selector: str (optional) + Can be used to filter SSH keys by labels. The response will only contain SSH keys matching the label selector. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if fingerprint is not None: + params["fingerprint"] = fingerprint + if label_selector is not None: + params["label_selector"] = label_selector + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request(url="/ssh_keys", method="GET", params=params) + + ssh_keys = [ + BoundSSHKey(self, server_data) for server_data in response["ssh_keys"] + ] + return SSHKeysPageResult(ssh_keys, Meta.parse_meta(response)) + + def get_all( + self, + name: str | None = None, + fingerprint: str | None = None, + label_selector: str | None = None, + ) -> list[BoundSSHKey]: + """Get all SSH keys from the account + + :param name: str (optional) + Can be used to filter SSH keys by their name. The response will only contain the SSH key matching the specified name. + :param fingerprint: str (optional) + Can be used to filter SSH keys by their fingerprint. The response will only contain the SSH key matching the specified fingerprint. + :param label_selector: str (optional) + Can be used to filter SSH keys by labels. The response will only contain SSH keys matching the label selector. + :return: List[:class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>`] + """ + return self._iter_pages( + self.get_list, + name=name, + fingerprint=fingerprint, + label_selector=label_selector, + ) + + def get_by_name(self, name: str) -> BoundSSHKey | None: + """Get ssh key by name + + :param name: str + Used to get ssh key by name. + :return: :class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>` + """ + return self._get_first_by(name=name) + + def get_by_fingerprint(self, fingerprint: str) -> BoundSSHKey | None: + """Get ssh key by fingerprint + + :param fingerprint: str + Used to get ssh key by fingerprint. + :return: :class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>` + """ + return self._get_first_by(fingerprint=fingerprint) + + def create( + self, + name: str, + public_key: str, + labels: dict[str, str] | None = None, + ) -> BoundSSHKey: + """Creates a new SSH key with the given name and public_key. + + :param name: str + :param public_key: str + Public Key of the SSH Key you want create + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>` + """ + data: dict[str, Any] = {"name": name, "public_key": public_key} + if labels is not None: + data["labels"] = labels + response = self._client.request(url="/ssh_keys", method="POST", json=data) + return BoundSSHKey(self, response["ssh_key"]) + + def update( + self, + ssh_key: SSHKey | BoundSSHKey, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundSSHKey: + """Updates an SSH key. You can update an SSH key name and an SSH key labels. + + :param ssh_key: :class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>` or :class:`SSHKey <hcloud.ssh_keys.domain.SSHKey>` + :param name: str (optional) + New Description to set + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>` + """ + data: dict[str, Any] = {} + if name is not None: + data["name"] = name + if labels is not None: + data["labels"] = labels + response = self._client.request( + url=f"/ssh_keys/{ssh_key.id}", + method="PUT", + json=data, + ) + return BoundSSHKey(self, response["ssh_key"]) + + def delete(self, ssh_key: SSHKey | BoundSSHKey) -> bool: + """Deletes an SSH key. It cannot be used anymore. + + :param ssh_key: :class:`BoundSSHKey <hcloud.ssh_keys.client.BoundSSHKey>` or :class:`SSHKey <hcloud.ssh_keys.domain.SSHKey>` + :return: True + """ + self._client.request(url=f"/ssh_keys/{ssh_key.id}", method="DELETE") + # Return always true, because the API does not return an action for it. When an error occurs a HcloudAPIException will be raised + return True diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/ssh_keys/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/ssh_keys/domain.py new file mode 100644 index 000000000..3c880c4d8 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/ssh_keys/domain.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain, DomainIdentityMixin + + +class SSHKey(BaseDomain, DomainIdentityMixin): + """SSHKey Domain + + :param id: int + ID of the SSH key + :param name: str + Name of the SSH key (must be unique per project) + :param fingerprint: str + Fingerprint of public key + :param public_key: str + Public Key + :param labels: Dict + User-defined labels (key-value pairs) + :param created: datetime + Point in time when the SSH Key was created + """ + + __slots__ = ("id", "name", "fingerprint", "public_key", "labels", "created") + + def __init__( + self, + id: int | None = None, + name: str | None = None, + fingerprint: str | None = None, + public_key: str | None = None, + labels: dict[str, str] | None = None, + created: str | None = None, + ): + self.id = id + self.name = name + self.fingerprint = fingerprint + self.public_key = public_key + self.labels = labels + self.created = isoparse(created) if created else None diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/volumes/__init__.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/volumes/__init__.py new file mode 100644 index 000000000..dc8ccbb61 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/volumes/__init__.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from .client import BoundVolume, VolumesClient, VolumesPageResult # noqa: F401 +from .domain import CreateVolumeResponse, Volume # noqa: F401 diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/volumes/client.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/volumes/client.py new file mode 100644 index 000000000..a4709748a --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/volumes/client.py @@ -0,0 +1,458 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, NamedTuple + +from ..actions import ActionsPageResult, BoundAction, ResourceActionsClient +from ..core import BoundModelBase, ClientEntityBase, Meta +from ..locations import BoundLocation +from .domain import CreateVolumeResponse, Volume + +if TYPE_CHECKING: + from .._client import Client + from ..locations import Location + from ..servers import BoundServer, Server + + +class BoundVolume(BoundModelBase, Volume): + _client: VolumesClient + + model = Volume + + def __init__(self, client: VolumesClient, data: dict, complete: bool = True): + location = data.get("location") + if location is not None: + data["location"] = BoundLocation(client._client.locations, location) + + # pylint: disable=import-outside-toplevel + from ..servers import BoundServer + + server = data.get("server") + if server is not None: + data["server"] = BoundServer( + client._client.servers, {"id": server}, complete=False + ) + super().__init__(client, data, complete) + + def get_actions_list( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a volume. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + return self._client.get_actions_list(self, status, sort, page, per_page) + + def get_actions( + self, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a volume. + + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._client.get_actions(self, status, sort) + + def update( + self, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundVolume: + """Updates the volume properties. + + :param name: str (optional) + New volume name + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.update(self, name, labels) + + def delete(self) -> bool: + """Deletes a volume. All volume data is irreversibly destroyed. The volume must not be attached to a server and it must not have delete protection enabled. + + :return: boolean + """ + return self._client.delete(self) + + def attach( + self, + server: Server | BoundServer, + automount: bool | None = None, + ) -> BoundAction: + """Attaches a volume to a server. Works only if the server is in the same location as the volume. + + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param automount: boolean + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.attach(self, server, automount) + + def detach(self) -> BoundAction: + """Detaches a volume from the server it’s attached to. You may attach it to a server again at a later time. + + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.detach(self) + + def resize(self, size: int) -> BoundAction: + """Changes the size of a volume. Note that downsizing a volume is not possible. + + :param size: int + New volume size in GB (must be greater than current size) + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.resize(self, size) + + def change_protection(self, delete: bool | None = None) -> BoundAction: + """Changes the protection configuration of a volume. + + :param delete: boolean + If True, prevents the volume from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + return self._client.change_protection(self, delete) + + +class VolumesPageResult(NamedTuple): + volumes: list[BoundVolume] + meta: Meta | None + + +class VolumesClient(ClientEntityBase): + _client: Client + + actions: ResourceActionsClient + """Volumes scoped actions client + + :type: :class:`ResourceActionsClient <hcloud.actions.client.ResourceActionsClient>` + """ + + def __init__(self, client: Client): + super().__init__(client) + self.actions = ResourceActionsClient(client, "/volumes") + + def get_by_id(self, id: int) -> BoundVolume: + """Get a specific volume by its id + + :param id: int + :return: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` + """ + response = self._client.request(url=f"/volumes/{id}", method="GET") + return BoundVolume(self, response["volume"]) + + def get_list( + self, + name: str | None = None, + label_selector: str | None = None, + page: int | None = None, + per_page: int | None = None, + status: list[str] | None = None, + ) -> VolumesPageResult: + """Get a list of volumes from this account + + :param name: str (optional) + Can be used to filter volumes by their name. + :param label_selector: str (optional) + Can be used to filter volumes by labels. The response will only contain volumes matching the label selector. + :param status: List[str] (optional) + Can be used to filter volumes by their status. The response will only contain volumes matching the status. + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundVolume <hcloud.volumes.client.BoundVolume>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if name is not None: + params["name"] = name + if label_selector is not None: + params["label_selector"] = label_selector + if status is not None: + params["status"] = status + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request(url="/volumes", method="GET", params=params) + volumes = [ + BoundVolume(self, volume_data) for volume_data in response["volumes"] + ] + return VolumesPageResult(volumes, Meta.parse_meta(response)) + + def get_all( + self, + label_selector: str | None = None, + status: list[str] | None = None, + ) -> list[BoundVolume]: + """Get all volumes from this account + + :param label_selector: + Can be used to filter volumes by labels. The response will only contain volumes matching the label selector. + :param status: List[str] (optional) + Can be used to filter volumes by their status. The response will only contain volumes matching the status. + :return: List[:class:`BoundVolume <hcloud.volumes.client.BoundVolume>`] + """ + return self._iter_pages( + self.get_list, + label_selector=label_selector, + status=status, + ) + + def get_by_name(self, name: str) -> BoundVolume | None: + """Get volume by name + + :param name: str + Used to get volume by name. + :return: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` + """ + return self._get_first_by(name=name) + + def create( + self, + size: int, + name: str, + labels: str | None = None, + location: Location | None = None, + server: Server | None = None, + automount: bool | None = None, + format: str | None = None, + ) -> CreateVolumeResponse: + """Creates a new volume attached to a server. + + :param size: int + Size of the volume in GB + :param name: str + Name of the volume + :param labels: Dict[str,str] (optional) + User-defined labels (key-value pairs) + :param location: :class:`BoundLocation <hcloud.locations.client.BoundLocation>` or :class:`Location <hcloud.locations.domain.Location>` + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param automount: boolean (optional) + Auto mount volumes after attach. + :param format: str (optional) + Format volume after creation. One of: xfs, ext4 + :return: :class:`CreateVolumeResponse <hcloud.volumes.domain.CreateVolumeResponse>` + """ + + if size <= 0: + raise ValueError("size must be greater than 0") + + if not bool(location) ^ bool(server): + raise ValueError("only one of server or location must be provided") + + data: dict[str, Any] = {"name": name, "size": size} + if labels is not None: + data["labels"] = labels + if location is not None: + data["location"] = location.id_or_name + + if server is not None: + data["server"] = server.id + if automount is not None: + data["automount"] = automount + if format is not None: + data["format"] = format + + response = self._client.request(url="/volumes", json=data, method="POST") + + result = CreateVolumeResponse( + volume=BoundVolume(self, response["volume"]), + action=BoundAction(self._client.actions, response["action"]), + next_actions=[ + BoundAction(self._client.actions, action) + for action in response["next_actions"] + ], + ) + return result + + def get_actions_list( + self, + volume: Volume | BoundVolume, + status: list[str] | None = None, + sort: list[str] | None = None, + page: int | None = None, + per_page: int | None = None, + ) -> ActionsPageResult: + """Returns all action objects for a volume. + + :param volume: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` or :class:`Volume <hcloud.volumes.domain.Volume>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :param page: int (optional) + Specifies the page to fetch + :param per_page: int (optional) + Specifies how many results are returned by page + :return: (List[:class:`BoundAction <hcloud.actions.client.BoundAction>`], :class:`Meta <hcloud.core.domain.Meta>`) + """ + params: dict[str, Any] = {} + if status is not None: + params["status"] = status + if sort is not None: + params["sort"] = sort + if page is not None: + params["page"] = page + if per_page is not None: + params["per_page"] = per_page + + response = self._client.request( + url=f"/volumes/{volume.id}/actions", + method="GET", + params=params, + ) + actions = [ + BoundAction(self._client.actions, action_data) + for action_data in response["actions"] + ] + return ActionsPageResult(actions, Meta.parse_meta(response)) + + def get_actions( + self, + volume: Volume | BoundVolume, + status: list[str] | None = None, + sort: list[str] | None = None, + ) -> list[BoundAction]: + """Returns all action objects for a volume. + + :param volume: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` or :class:`Volume <hcloud.volumes.domain.Volume>` + :param status: List[str] (optional) + Response will have only actions with specified statuses. Choices: `running` `success` `error` + :param sort: List[str] (optional) + Specify how the results are sorted. Choices: `id` `id:asc` `id:desc` `command` `command:asc` `command:desc` `status` `status:asc` `status:desc` `progress` `progress:asc` `progress:desc` `started` `started:asc` `started:desc` `finished` `finished:asc` `finished:desc` + :return: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + """ + return self._iter_pages( + self.get_actions_list, + volume, + status=status, + sort=sort, + ) + + def update( + self, + volume: Volume | BoundVolume, + name: str | None = None, + labels: dict[str, str] | None = None, + ) -> BoundVolume: + """Updates the volume properties. + + :param volume: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` or :class:`Volume <hcloud.volumes.domain.Volume>` + :param name: str (optional) + New volume name + :param labels: Dict[str, str] (optional) + User-defined labels (key-value pairs) + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {} + if name is not None: + data.update({"name": name}) + if labels is not None: + data.update({"labels": labels}) + response = self._client.request( + url=f"/volumes/{volume.id}", + method="PUT", + json=data, + ) + return BoundVolume(self, response["volume"]) + + def delete(self, volume: Volume | BoundVolume) -> bool: + """Deletes a volume. All volume data is irreversibly destroyed. The volume must not be attached to a server and it must not have delete protection enabled. + + :param volume: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` or :class:`Volume <hcloud.volumes.domain.Volume>` + :return: boolean + """ + self._client.request(url=f"/volumes/{volume.id}", method="DELETE") + return True + + def resize(self, volume: Volume | BoundVolume, size: int) -> BoundAction: + """Changes the size of a volume. Note that downsizing a volume is not possible. + + :param volume: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` or :class:`Volume <hcloud.volumes.domain.Volume>` + :param size: int + New volume size in GB (must be greater than current size) + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data = self._client.request( + url=f"/volumes/{volume.id}/actions/resize", + json={"size": size}, + method="POST", + ) + return BoundAction(self._client.actions, data["action"]) + + def attach( + self, + volume: Volume | BoundVolume, + server: Server | BoundServer, + automount: bool | None = None, + ) -> BoundAction: + """Attaches a volume to a server. Works only if the server is in the same location as the volume. + + :param volume: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` or :class:`Volume <hcloud.volumes.domain.Volume>` + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>` or :class:`Server <hcloud.servers.domain.Server>` + :param automount: boolean + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {"server": server.id} + if automount is not None: + data["automount"] = automount + + data = self._client.request( + url=f"/volumes/{volume.id}/actions/attach", + json=data, + method="POST", + ) + return BoundAction(self._client.actions, data["action"]) + + def detach(self, volume: Volume | BoundVolume) -> BoundAction: + """Detaches a volume from the server it’s attached to. You may attach it to a server again at a later time. + + :param volume: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` or :class:`Volume <hcloud.volumes.domain.Volume>` + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data = self._client.request( + url=f"/volumes/{volume.id}/actions/detach", + method="POST", + ) + return BoundAction(self._client.actions, data["action"]) + + def change_protection( + self, + volume: Volume | BoundVolume, + delete: bool | None = None, + ) -> BoundAction: + """Changes the protection configuration of a volume. + + :param volume: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` or :class:`Volume <hcloud.volumes.domain.Volume>` + :param delete: boolean + If True, prevents the volume from being deleted + :return: :class:`BoundAction <hcloud.actions.client.BoundAction>` + """ + data: dict[str, Any] = {} + if delete is not None: + data.update({"delete": delete}) + + response = self._client.request( + url=f"/volumes/{volume.id}/actions/change_protection", + method="POST", + json=data, + ) + return BoundAction(self._client.actions, response["action"]) diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/volumes/domain.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/volumes/domain.py new file mode 100644 index 000000000..7eb544021 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/vendor/hcloud/volumes/domain.py @@ -0,0 +1,113 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +try: + from dateutil.parser import isoparse +except ImportError: + isoparse = None + +from ..core import BaseDomain, DomainIdentityMixin + +if TYPE_CHECKING: + from ..actions import BoundAction + from ..locations import BoundLocation, Location + from ..servers import BoundServer, Server + from .client import BoundVolume + + +class Volume(BaseDomain, DomainIdentityMixin): + """Volume Domain + + :param id: int + ID of the Volume + :param name: str + Name of the Volume + :param server: :class:`BoundServer <hcloud.servers.client.BoundServer>`, None + Server the Volume is attached to, None if it is not attached at all. + :param created: datetime + Point in time when the Volume was created + :param location: :class:`BoundLocation <hcloud.locations.client.BoundLocation>` + Location of the Volume. Volume can only be attached to Servers in the same location. + :param size: int + Size in GB of the Volume + :param linux_device: str + Device path on the file system for the Volume + :param protection: dict + Protection configuration for the Volume + :param labels: dict + User-defined labels (key-value pairs) + :param status: str + Current status of the volume Choices: `creating`, `available` + :param format: str, None + Filesystem of the volume if formatted on creation, None if not formatted on creation. + """ + + STATUS_CREATING = "creating" + """Volume Status creating""" + STATUS_AVAILABLE = "available" + """Volume Status available""" + + __slots__ = ( + "id", + "name", + "server", + "location", + "size", + "linux_device", + "format", + "protection", + "labels", + "status", + "created", + ) + + def __init__( + self, + id: int, + name: str | None = None, + server: Server | BoundServer | None = None, + created: str | None = None, + location: Location | BoundLocation | None = None, + size: int | None = None, + linux_device: str | None = None, + format: str | None = None, + protection: dict | None = None, + labels: dict[str, str] | None = None, + status: str | None = None, + ): + self.id = id + self.name = name + self.server = server + self.created = isoparse(created) if created else None + self.location = location + self.size = size + self.linux_device = linux_device + self.format = format + self.protection = protection + self.labels = labels + self.status = status + + +class CreateVolumeResponse(BaseDomain): + """Create Volume Response Domain + + :param volume: :class:`BoundVolume <hcloud.volumes.client.BoundVolume>` + The created volume + :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>` + The action that shows the progress of the Volume Creation + :param next_actions: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`] + List of actions that are performed after the creation, like attaching to a server + """ + + __slots__ = ("volume", "action", "next_actions") + + def __init__( + self, + volume: BoundVolume, + action: BoundAction, + next_actions: list[BoundAction], + ): + self.volume = volume + self.action = action + self.next_actions = next_actions diff --git a/ansible_collections/hetzner/hcloud/plugins/module_utils/version.py b/ansible_collections/hetzner/hcloud/plugins/module_utils/version.py new file mode 100644 index 000000000..e78c320a2 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/module_utils/version.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +version = "2.5.0" # x-release-please-version diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/certificate.py b/ansible_collections/hetzner/hcloud/plugins/modules/certificate.py new file mode 100644 index 000000000..ea39be6ca --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/certificate.py @@ -0,0 +1,289 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: certificate + +short_description: Create and manage certificates on the Hetzner Cloud. + + +description: + - Create, update and manage certificates on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@lkaemmerling) + +options: + id: + description: + - The ID of the Hetzner Cloud certificate to manage. + - Only required if no certificate I(name) is given + type: int + name: + description: + - The Name of the Hetzner Cloud certificate to manage. + - Only required if no certificate I(id) is given or a certificate does not exist. + type: str + labels: + description: + - User-defined labels (key-value pairs) + type: dict + certificate: + description: + - Certificate and chain in PEM format, in order so that each record directly certifies the one preceding. + - Required if certificate does not exist and I(type=uploaded). + type: str + private_key: + description: + - Certificate key in PEM format. + - Required if certificate does not exist and I(type=uploaded). + type: str + domain_names: + description: + - Domains and subdomains that should be contained in the Certificate issued by Let's Encrypt. + - Required if I(type=managed). + type: list + default: [ ] + elements: str + type: + description: + - Choose between uploading a Certificate in PEM format or requesting a managed Let's Encrypt Certificate. + default: uploaded + choices: [ uploaded, managed ] + type: str + state: + description: + - State of the certificate. + default: present + choices: [ absent, present ] + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Create a basic certificate + hetzner.hcloud.certificate: + name: my-certificate + certificate: -----BEGIN CERTIFICATE-----... + private_key: -----BEGIN PRIVATE KEY-----... + state: present + +- name: Create a certificate with labels + hetzner.hcloud.certificate: + name: my-certificate + certificate: -----BEGIN CERTIFICATE-----... + private_key: -----BEGIN PRIVATE KEY-----... + labels: + key: value + mylabel: 123 + state: present + +- name: Create a managed certificate + hetzner.hcloud.certificate: + name: my-certificate + type: managed + domain_names: + - example.com + - www.example.com + state: present + +- name: Ensure the certificate is absent (remove if needed) + hetzner.hcloud.certificate: + name: my-certificate + state: absent +""" + +RETURN = """ +hcloud_certificate: + description: The certificate instance + returned: Always + type: complex + contains: + id: + description: Numeric identifier of the certificate + returned: always + type: int + sample: 1937415 + name: + description: Name of the certificate + returned: always + type: str + sample: my website cert + fingerprint: + description: Fingerprint of the certificate + returned: always + type: str + sample: "03:c7:55:9b:2a:d1:04:17:09:f6:d0:7f:18:34:63:d4:3e:5f" + certificate: + description: Certificate and chain in PEM format + returned: always + type: str + sample: "-----BEGIN CERTIFICATE-----..." + domain_names: + description: List of Domains and Subdomains covered by the Certificate + returned: always + type: dict + not_valid_before: + description: Point in time when the Certificate becomes valid (in ISO-8601 format) + returned: always + type: str + not_valid_after: + description: Point in time when the Certificate stops being valid (in ISO-8601 format) + returned: always + type: str + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.certificates import BoundCertificate + + +class AnsibleHCloudCertificate(AnsibleHCloud): + represent = "hcloud_certificate" + + hcloud_certificate: BoundCertificate | None = None + + def _prepare_result(self): + return { + "id": to_native(self.hcloud_certificate.id), + "name": to_native(self.hcloud_certificate.name), + "type": to_native(self.hcloud_certificate.type), + "fingerprint": to_native(self.hcloud_certificate.fingerprint), + "certificate": to_native(self.hcloud_certificate.certificate), + "not_valid_before": to_native(self.hcloud_certificate.not_valid_before), + "not_valid_after": to_native(self.hcloud_certificate.not_valid_after), + "domain_names": [to_native(domain) for domain in self.hcloud_certificate.domain_names], + "labels": self.hcloud_certificate.labels, + } + + def _get_certificate(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_certificate = self.client.certificates.get_by_id(self.module.params.get("id")) + elif self.module.params.get("name") is not None: + self.hcloud_certificate = self.client.certificates.get_by_name(self.module.params.get("name")) + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_certificate(self): + self.module.fail_on_missing_params(required_params=["name"]) + + params = { + "name": self.module.params.get("name"), + "labels": self.module.params.get("labels"), + } + if self.module.params.get("type") == "uploaded": + self.module.fail_on_missing_params(required_params=["certificate", "private_key"]) + params["certificate"] = self.module.params.get("certificate") + params["private_key"] = self.module.params.get("private_key") + if not self.module.check_mode: + try: + self.client.certificates.create(**params) + except HCloudException as exception: + self.fail_json_hcloud(exception) + else: + self.module.fail_on_missing_params(required_params=["domain_names"]) + params["domain_names"] = self.module.params.get("domain_names") + if not self.module.check_mode: + try: + resp = self.client.certificates.create_managed(**params) + resp.action.wait_until_finished(max_retries=1000) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + self._mark_as_changed() + self._get_certificate() + + def _update_certificate(self): + try: + name = self.module.params.get("name") + if name is not None and self.hcloud_certificate.name != name: + self.module.fail_on_missing_params(required_params=["id"]) + if not self.module.check_mode: + self.hcloud_certificate.update(name=name) + self._mark_as_changed() + + labels = self.module.params.get("labels") + if labels is not None and self.hcloud_certificate.labels != labels: + if not self.module.check_mode: + self.hcloud_certificate.update(labels=labels) + self._mark_as_changed() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._get_certificate() + + def present_certificate(self): + self._get_certificate() + if self.hcloud_certificate is None: + self._create_certificate() + else: + self._update_certificate() + + def delete_certificate(self): + self._get_certificate() + if self.hcloud_certificate is not None: + if not self.module.check_mode: + try: + self.client.certificates.delete(self.hcloud_certificate) + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self.hcloud_certificate = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + type={ + "choices": ["uploaded", "managed"], + "default": "uploaded", + }, + domain_names={"type": "list", "elements": "str", "default": []}, + certificate={"type": "str"}, + private_key={"type": "str", "no_log": True}, + labels={"type": "dict"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name"]], + required_if=[["state", "present", ["name"]]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudCertificate.define_module() + + hcloud = AnsibleHCloudCertificate(module) + state = module.params.get("state") + if state == "absent": + hcloud.delete_certificate() + elif state == "present": + hcloud.present_certificate() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py new file mode 100644 index 000000000..e074046fd --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/certificate_info.py @@ -0,0 +1,162 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: certificate_info +short_description: Gather infos about your Hetzner Cloud certificates. +description: + - Gather facts about your Hetzner Cloud certificates. +author: + - Lukas Kaemmerling (@LKaemmerling) +options: + id: + description: + - The ID of the certificate you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the certificate you want to get. + type: str + label_selector: + description: + - The label selector for the certificate you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud certificate infos + hetzner.hcloud.certificate_info: + register: output +- name: Print the gathered infos + debug: + var: output.hcloud_certificate_info +""" + +RETURN = """ +hcloud_certificate_info: + description: The certificate instances + returned: Always + type: complex + contains: + id: + description: Numeric identifier of the certificate + returned: always + type: int + sample: 1937415 + name: + description: Name of the certificate + returned: always + type: str + sample: my website cert + fingerprint: + description: Fingerprint of the certificate + returned: always + type: str + sample: "03:c7:55:9b:2a:d1:04:17:09:f6:d0:7f:18:34:63:d4:3e:5f" + certificate: + description: Certificate and chain in PEM format + returned: always + type: str + sample: "-----BEGIN CERTIFICATE-----..." + domain_names: + description: List of Domains and Subdomains covered by the Certificate + returned: always + type: dict + not_valid_before: + description: Point in time when the Certificate becomes valid (in ISO-8601 format) + returned: always + type: str + not_valid_after: + description: Point in time when the Certificate stops being valid (in ISO-8601 format) + returned: always + type: str + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.certificates import BoundCertificate + + +class AnsibleHCloudCertificateInfo(AnsibleHCloud): + represent = "hcloud_certificate_info" + + hcloud_certificate_info: list[BoundCertificate] | None = None + + def _prepare_result(self): + certificates = [] + + for certificate in self.hcloud_certificate_info: + if certificate: + certificates.append( + { + "id": to_native(certificate.id), + "name": to_native(certificate.name), + "fingerprint": to_native(certificate.fingerprint), + "certificate": to_native(certificate.certificate), + "not_valid_before": to_native(certificate.not_valid_before), + "not_valid_after": to_native(certificate.not_valid_after), + "domain_names": [to_native(domain) for domain in certificate.domain_names], + "labels": certificate.labels, + } + ) + return certificates + + def get_certificates(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_certificate_info = [self.client.certificates.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_certificate_info = [self.client.certificates.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("label_selector") is not None: + self.hcloud_certificate_info = self.client.certificates.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_certificate_info = self.client.certificates.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + label_selector={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudCertificateInfo.define_module() + hcloud = AnsibleHCloudCertificateInfo(module) + + hcloud.get_certificates() + result = hcloud.get_result() + + ansible_info = {"hcloud_certificate_info": result["hcloud_certificate_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py new file mode 100644 index 000000000..f6665a6fb --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/datacenter_info.py @@ -0,0 +1,192 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: datacenter_info + +short_description: Gather info about the Hetzner Cloud datacenters. + +description: + - Gather info about your Hetzner Cloud datacenters. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the datacenter you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the datacenter you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud datacenter info + hetzner.hcloud.datacenter_info: + register: output + +- name: Print the gathered info + debug: + var: output + +- name: List available server_types in a datacenter + block: + - name: Gather a hcloud datacenter + hetzner.hcloud.datacenter_info: + name: fsn1-dc14 + register: output + + - name: Gather a hcloud datacenter available server_types + hetzner.hcloud.server_type_info: + id: "{{ item }}" + loop: "{{ output.hcloud_datacenter_info[0].server_types.available }}" + register: available_server_types + + - name: Print a hcloud datacenter available server_types + ansible.builtin.debug: + var: available_server_types.results | map(attribute='hcloud_server_type_info') +""" + +RETURN = """ +hcloud_datacenter_info: + description: + - The datacenter info as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the datacenter + returned: always + type: int + sample: 1937415 + name: + description: Name of the datacenter + returned: always + type: str + sample: fsn1-dc8 + description: + description: Detail description of the datacenter + returned: always + type: str + sample: Falkenstein DC 8 + location: + description: Name of the location where the datacenter resides in + returned: always + type: str + sample: fsn1 + city: + description: City of the location + returned: always + type: str + sample: fsn1 + server_types: + description: The Server types the Datacenter can handle + returned: always + type: dict + contains: + available: + description: IDs of Server types that are supported and for which the Datacenter has enough resources left + returned: always + type: list + elements: int + sample: [1, 2, 3] + available_for_migration: + description: IDs of Server types that are supported and for which the Datacenter has enough resources left + returned: always + type: list + elements: int + sample: [1, 2, 3] + supported: + description: IDs of Server types that are supported in the Datacenter + returned: always + type: list + elements: int + sample: [1, 2, 3] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.datacenters import BoundDatacenter + + +class AnsibleHCloudDatacenterInfo(AnsibleHCloud): + represent = "hcloud_datacenter_info" + + hcloud_datacenter_info: list[BoundDatacenter] | None = None + + def _prepare_result(self): + tmp = [] + + for datacenter in self.hcloud_datacenter_info: + if datacenter is None: + continue + + tmp.append( + { + "id": to_native(datacenter.id), + "name": to_native(datacenter.name), + "description": to_native(datacenter.description), + "location": to_native(datacenter.location.name), + "server_types": { + "available": [o.id for o in datacenter.server_types.available], + "available_for_migration": [o.id for o in datacenter.server_types.available_for_migration], + "supported": [o.id for o in datacenter.server_types.supported], + }, + } + ) + + return tmp + + def get_datacenters(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_datacenter_info = [self.client.datacenters.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_datacenter_info = [self.client.datacenters.get_by_name(self.module.params.get("name"))] + else: + self.hcloud_datacenter_info = self.client.datacenters.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudDatacenterInfo.define_module() + hcloud = AnsibleHCloudDatacenterInfo(module) + + hcloud.get_datacenters() + result = hcloud.get_result() + + ansible_info = {"hcloud_datacenter_info": result["hcloud_datacenter_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/firewall.py b/ansible_collections/hetzner/hcloud/plugins/modules/firewall.py new file mode 100644 index 000000000..3c51b5c0a --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/firewall.py @@ -0,0 +1,438 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: firewall +short_description: Create and manage firewalls on the Hetzner Cloud. + +description: + - Create, update and manage firewalls on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@lkaemmerling) + +options: + id: + description: + - The ID of the Hetzner Cloud Firewall to manage. + - Only required if no firewall O(name) is given. + type: int + name: + description: + - The Name of the Hetzner Cloud Firewall to manage. + - Only required if no firewall O(id) is given, or the firewall does not exist. + type: str + labels: + description: + - User-defined labels (key-value pairs). + type: dict + rules: + description: + - List of rules the firewall contain. + type: list + elements: dict + suboptions: + description: + description: + - User defined description of this rule. + type: str + direction: + description: + - The direction of the firewall rule. + type: str + choices: [in, out] + protocol: + description: + - The protocol of the firewall rule. + type: str + choices: [icmp, tcp, udp, esp, gre] + port: + description: + - The port or port range allowed by this rule. + - A port range can be specified by separating two ports with a dash, e.g 1024-5000. + - Only used if O(rules[].protocol=tcp) or O(rules[].protocol=udp). + type: str + source_ips: + description: + - List of CIDRs that are allowed within this rule. + - Use 0.0.0.0/0 to allow all IPv4 addresses and ::/0 to allow all IPv6 addresses. + - Only used if O(rules[].direction=in). + type: list + elements: str + default: [] + destination_ips: + description: + - List of CIDRs that are allowed within this rule. + - Use 0.0.0.0/0 to allow all IPv4 addresses and ::/0 to allow all IPv6 addresses. + - Only used if O(rules[].direction=out). + type: list + elements: str + default: [] + force: + description: + - Force the deletion of the Firewall when still in use. + type: bool + default: false + state: + description: + - State of the firewall. + default: present + choices: [absent, present] + type: str + +extends_documentation_fragment: + - hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic firewall + hetzner.hcloud.firewall: + name: my-firewall + state: present + +- name: Create a firewall with rules + hetzner.hcloud.firewall: + name: my-firewall + rules: + - description: allow icmp from everywhere + direction: in + protocol: icmp + source_ips: + - 0.0.0.0/0 + - ::/0 + state: present + +- name: Create a firewall with labels + hetzner.hcloud.firewall: + name: my-firewall + labels: + key: value + mylabel: 123 + state: present + +- name: Ensure the firewall is absent (remove if needed) + hetzner.hcloud.firewall: + name: my-firewall + state: absent +""" + +RETURN = """ +hcloud_firewall: + description: The firewall instance. + returned: always + type: dict + contains: + id: + description: Numeric identifier of the firewall. + returned: always + type: int + sample: 1937415 + name: + description: Name of the firewall. + returned: always + type: str + sample: my-firewall + labels: + description: User-defined labels (key-value pairs). + returned: always + type: dict + rules: + description: List of rules the firewall contain. + returned: always + type: list + elements: dict + contains: + description: + description: User defined description of this rule. + type: str + returned: always + sample: allow http from anywhere + direction: + description: The direction of the firewall rule. + type: str + returned: always + sample: in + protocol: + description: The protocol of the firewall rule. + type: str + returned: always + sample: tcp + port: + description: The port or port range allowed by this rule. + type: str + returned: if RV(hcloud_firewall.rules[].protocol=tcp) or RV(hcloud_firewall.rules[].protocol=udp) + sample: "80" + source_ips: + description: List of source CIDRs that are allowed within this rule. + type: list + elements: str + returned: always + sample: ["0.0.0.0/0", "::/0"] + destination_ips: + description: List of destination CIDRs that are allowed within this rule. + type: list + elements: str + returned: always + sample: [] + applied_to: + description: List of Resources the Firewall is applied to. + returned: always + type: list + elements: dict + contains: + type: + description: Type of the resource. + type: str + choices: [server, label_selector] + sample: label_selector + server: + description: ID of the server. + type: int + sample: 12345 + label_selector: + description: Label selector value. + type: str + sample: env=prod + applied_to_resources: + description: List of Resources the Firewall label selector is applied to. + returned: if RV(hcloud_firewall.applied_to[].type=label_selector) + type: list + elements: dict + contains: + type: + description: Type of resource referenced. + type: str + choices: [server] + sample: server + server: + description: ID of the Server. + type: int + sample: 12345 +""" + +import time + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import APIException, HCloudException +from ..module_utils.vendor.hcloud.firewalls import ( + BoundFirewall, + FirewallResource, + FirewallRule, +) + + +class AnsibleHCloudFirewall(AnsibleHCloud): + represent = "hcloud_firewall" + + hcloud_firewall: BoundFirewall | None = None + + def _prepare_result(self): + return { + "id": to_native(self.hcloud_firewall.id), + "name": to_native(self.hcloud_firewall.name), + "rules": [self._prepare_result_rule(rule) for rule in self.hcloud_firewall.rules], + "labels": self.hcloud_firewall.labels, + "applied_to": [self._prepare_result_applied_to(resource) for resource in self.hcloud_firewall.applied_to], + } + + def _prepare_result_rule(self, rule: FirewallRule): + return { + "direction": to_native(rule.direction), + "protocol": to_native(rule.protocol), + "port": to_native(rule.port) if rule.port is not None else None, + "source_ips": [to_native(cidr) for cidr in rule.source_ips], + "destination_ips": [to_native(cidr) for cidr in rule.destination_ips], + "description": to_native(rule.description) if rule.description is not None else None, + } + + def _prepare_result_applied_to(self, resource: FirewallResource): + result = { + "type": to_native(resource.type), + "server": to_native(resource.server.id) if resource.server is not None else None, + "label_selector": ( + to_native(resource.label_selector.selector) if resource.label_selector is not None else None + ), + } + if resource.applied_to_resources is not None: + result["applied_to_resources"] = [ + { + "type": to_native(item.type), + "server": to_native(item.server.id) if item.server is not None else None, + } + for item in resource.applied_to_resources + ] + return result + + def _get_firewall(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_firewall = self.client.firewalls.get_by_id(self.module.params.get("id")) + elif self.module.params.get("name") is not None: + self.hcloud_firewall = self.client.firewalls.get_by_name(self.module.params.get("name")) + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_firewall(self): + self.module.fail_on_missing_params(required_params=["name"]) + params = { + "name": self.module.params.get("name"), + "labels": self.module.params.get("labels"), + } + rules = self.module.params.get("rules") + if rules is not None: + params["rules"] = [ + FirewallRule( + direction=rule["direction"], + protocol=rule["protocol"], + source_ips=rule["source_ips"] if rule["source_ips"] is not None else [], + destination_ips=rule["destination_ips"] if rule["destination_ips"] is not None else [], + port=rule["port"], + description=rule["description"], + ) + for rule in rules + ] + + if not self.module.check_mode: + try: + self.client.firewalls.create(**params) + except HCloudException as exception: + self.fail_json_hcloud(exception, params=params) + + self._mark_as_changed() + self._get_firewall() + + def _update_firewall(self): + name = self.module.params.get("name") + if name is not None and self.hcloud_firewall.name != name: + self.module.fail_on_missing_params(required_params=["id"]) + if not self.module.check_mode: + self.hcloud_firewall.update(name=name) + self._mark_as_changed() + + labels = self.module.params.get("labels") + if labels is not None and self.hcloud_firewall.labels != labels: + if not self.module.check_mode: + self.hcloud_firewall.update(labels=labels) + self._mark_as_changed() + + rules = self.module.params.get("rules") + if rules is not None and rules != [self._prepare_result_rule(rule) for rule in self.hcloud_firewall.rules]: + if not self.module.check_mode: + new_rules = [ + FirewallRule( + direction=rule["direction"], + protocol=rule["protocol"], + source_ips=rule["source_ips"] if rule["source_ips"] is not None else [], + destination_ips=rule["destination_ips"] if rule["destination_ips"] is not None else [], + port=rule["port"], + description=rule["description"], + ) + for rule in rules + ] + self.hcloud_firewall.set_rules(new_rules) + self._mark_as_changed() + + self._get_firewall() + + def present_firewall(self): + self._get_firewall() + if self.hcloud_firewall is None: + self._create_firewall() + else: + self._update_firewall() + + def delete_firewall(self): + self._get_firewall() + if self.hcloud_firewall is not None: + if not self.module.check_mode: + if self.hcloud_firewall.applied_to: + if self.module.params.get("force"): + actions = self.hcloud_firewall.remove_from_resources(self.hcloud_firewall.applied_to) + for action in actions: + action.wait_until_finished() + else: + self.module.warn( + f"Firewall {self.hcloud_firewall.name} is currently used by " + "other resources. You need to unassign the resources before " + "deleting the Firewall or use force=true." + ) + + retry_count = 0 + while True: + try: + self.hcloud_firewall.delete() + break + except APIException as exception: + if "is still in use" in exception.message and retry_count < 10: + retry_count += 1 + time.sleep(0.5 * retry_count) + continue + self.fail_json_hcloud(exception) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + self._mark_as_changed() + self.hcloud_firewall = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + labels={"type": "dict"}, + rules=dict( + type="list", + elements="dict", + options=dict( + description={"type": "str"}, + direction={"type": "str", "choices": ["in", "out"]}, + protocol={"type": "str", "choices": ["icmp", "udp", "tcp", "esp", "gre"]}, + port={"type": "str"}, + source_ips={"type": "list", "elements": "str", "default": []}, + destination_ips={"type": "list", "elements": "str", "default": []}, + ), + required_together=[["direction", "protocol"]], + required_if=[ + ["protocol", "udp", ["port"]], + ["protocol", "tcp", ["port"]], + ], + ), + force={"type": "bool", "default": False}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name"]], + required_if=[["state", "present", ["name"]]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudFirewall.define_module() + + hcloud = AnsibleHCloudFirewall(module) + state = module.params.get("state") + if state == "absent": + hcloud.delete_firewall() + elif state == "present": + hcloud.present_firewall() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py new file mode 100644 index 000000000..7e7a623d0 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_info.py @@ -0,0 +1,246 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: firewall_info +short_description: Gather infos about the Hetzner Cloud Firewalls. + +description: + - Gather facts about your Hetzner Cloud Firewalls. + +author: + - Jonas Lammler (@jooola) + +options: + id: + description: + - The ID of the Firewall you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name for the Firewall you want to get. + type: str + label_selector: + description: + - The label selector for the Firewalls you want to get. + type: str + +extends_documentation_fragment: + - hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Gather hcloud Firewall infos + hetzner.hcloud.firewall_info: + register: output + +- name: Print the gathered infos + debug: + var: output +""" + +RETURN = """ +hcloud_firewall_info: + description: List of Firewalls. + returned: always + type: list + elements: dict + contains: + id: + description: Numeric identifier of the firewall. + returned: always + type: int + sample: 1937415 + name: + description: Name of the firewall. + returned: always + type: str + sample: my-firewall + labels: + description: User-defined labels (key-value pairs). + returned: always + type: dict + rules: + description: List of rules the firewall contain. + returned: always + type: list + elements: dict + contains: + description: + description: User defined description of this rule. + type: str + returned: always + sample: allow http from anywhere + direction: + description: The direction of the firewall rule. + type: str + returned: always + sample: in + protocol: + description: The protocol of the firewall rule. + type: str + returned: always + sample: tcp + port: + description: The port or port range allowed by this rule. + type: str + returned: if RV(hcloud_firewall_info[].rules[].protocol=tcp) or RV(hcloud_firewall_info[].rules[].protocol=udp) + sample: "80" + source_ips: + description: List of source CIDRs that are allowed within this rule. + type: list + elements: str + returned: always + sample: ["0.0.0.0/0", "::/0"] + destination_ips: + description: List of destination CIDRs that are allowed within this rule. + type: list + elements: str + returned: always + sample: [] + applied_to: + description: List of Resources the Firewall is applied to. + returned: always + type: list + elements: dict + contains: + type: + description: Type of the resource. + type: str + choices: [server, label_selector] + sample: label_selector + server: + description: ID of the server. + type: int + sample: 12345 + label_selector: + description: Label selector value. + type: str + sample: env=prod + applied_to_resources: + description: List of Resources the Firewall label selector is applied to. + returned: if RV(hcloud_firewall_info[].applied_to[].type=label_selector) + type: list + elements: dict + contains: + type: + description: Type of resource referenced. + type: str + choices: [server] + sample: server + server: + description: ID of the Server. + type: int + sample: 12345 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.firewalls import ( + BoundFirewall, + FirewallResource, + FirewallRule, +) + + +class AnsibleHCloudFirewallInfo(AnsibleHCloud): + represent = "hcloud_firewall_info" + + hcloud_firewall_info: list[BoundFirewall] | None = None + + def _prepare_result(self): + tmp = [] + + for firewall in self.hcloud_firewall_info: + if firewall is None: + continue + + tmp.append( + { + "id": to_native(firewall.id), + "name": to_native(firewall.name), + "labels": firewall.labels, + "rules": [self._prepare_result_rule(rule) for rule in firewall.rules], + "applied_to": [self._prepare_result_applied_to(resource) for resource in firewall.applied_to], + } + ) + + return tmp + + def _prepare_result_rule(self, rule: FirewallRule): + return { + "description": to_native(rule.description) if rule.description is not None else None, + "direction": to_native(rule.direction), + "protocol": to_native(rule.protocol), + "port": to_native(rule.port) if rule.port is not None else None, + "source_ips": [to_native(cidr) for cidr in rule.source_ips], + "destination_ips": [to_native(cidr) for cidr in rule.destination_ips], + } + + def _prepare_result_applied_to(self, resource: FirewallResource): + result = { + "type": to_native(resource.type), + "server": to_native(resource.server.id) if resource.server is not None else None, + "label_selector": ( + to_native(resource.label_selector.selector) if resource.label_selector is not None else None + ), + } + if resource.applied_to_resources is not None: + result["applied_to_resources"] = [ + { + "type": to_native(item.type), + "server": to_native(item.server.id) if item.server is not None else None, + } + for item in resource.applied_to_resources + ] + return result + + def get_firewalls(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_firewall_info = [self.client.firewalls.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_firewall_info = [self.client.firewalls.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("label_selector") is not None: + self.hcloud_firewall_info = self.client.firewalls.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_firewall_info = self.client.firewalls.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + label_selector={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudFirewallInfo.define_module() + hcloud = AnsibleHCloudFirewallInfo(module) + + hcloud.get_firewalls() + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py new file mode 100644 index 000000000..207f27092 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/firewall_resource.py @@ -0,0 +1,243 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: firewall_resource +short_description: Manage Resources a Hetzner Cloud Firewall is applied to. + +description: + - Add and Remove Resources a Hetzner Cloud Firewall is applied to. + +author: + - Jonas Lammler (@jooola) + +version_added: 2.5.0 +options: + firewall: + description: + - Name or ID of the Hetzner Cloud Firewall. + type: str + required: true + servers: + description: + - List of Server Name or ID. + type: list + elements: str + label_selectors: + description: + - List of Label Selector. + type: list + elements: str + state: + description: + - State of the firewall resources. + default: present + choices: [absent, present] + type: str + +extends_documentation_fragment: + - hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Apply a firewall to a list of servers + hetzner.hcloud.firewall_resource: + name: my-firewall + servers: + - my-server + - 3456789 + state: present + +- name: Remove a firewall from a list of servers + hetzner.hcloud.firewall_resource: + name: my-firewall + servers: + - my-server + - 3456789 + state: absent + +- name: Apply a firewall to resources using label selectors + hetzner.hcloud.firewall_resource: + name: my-firewall + label_selectors: + - env=prod + state: present + +- name: Remove a firewall from resources using label selectors + hetzner.hcloud.firewall_resource: + name: my-firewall + label_selectors: + - env=prod + state: absent +""" + +RETURN = """ +hcloud_firewall_resource: + description: The Resources a Hetzner Cloud Firewall is applied to. + returned: always + type: dict + contains: + firewall: + description: + - Name of the Hetzner Cloud Firewall. + type: str + sample: my-firewall + servers: + description: + - List of Server Name. + type: list + elements: str + sample: [my-server1, my-server2] + label_selectors: + description: + - List of Label Selector. + type: list + elements: str + sample: [env=prod] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.firewalls import ( + BoundFirewall, + FirewallResource, + FirewallResourceLabelSelector, +) +from ..module_utils.vendor.hcloud.servers import BoundServer + + +class AnsibleHCloudFirewallResource(AnsibleHCloud): + represent = "hcloud_firewall_resource" + + hcloud_firewall_resource: BoundFirewall | None = None + + def _prepare_result(self): + servers = [] + label_selectors = [] + for resource in self.hcloud_firewall_resource.applied_to: + if resource.type == FirewallResource.TYPE_SERVER: + servers.append(to_native(resource.server.name)) + elif resource.type == FirewallResource.TYPE_LABEL_SELECTOR: + label_selectors.append(to_native(resource.label_selector.selector)) + + return { + "firewall": to_native(self.hcloud_firewall_resource.name), + "servers": servers, + "label_selectors": label_selectors, + } + + def _get_firewall(self): + try: + self.hcloud_firewall_resource = self._client_get_by_name_or_id( + "firewalls", + self.module.params.get("firewall"), + ) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _diff_firewall_resources(self, operator) -> list[FirewallResource]: + before = self._prepare_result() + + resources: list[FirewallResource] = [] + + servers: list[str] | None = self.module.params.get("servers") + if servers: + for server_param in servers: + try: + server: BoundServer = self._client_get_by_name_or_id("servers", server_param) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + if operator(server.name, before["servers"]): + resources.append( + FirewallResource( + type=FirewallResource.TYPE_SERVER, + server=server, + ) + ) + + label_selectors = self.module.params.get("label_selectors") + if label_selectors: + for label_selector in label_selectors: + if operator(label_selector, before["label_selectors"]): + resources.append( + FirewallResource( + type=FirewallResource.TYPE_LABEL_SELECTOR, + label_selector=FirewallResourceLabelSelector(selector=label_selector), + ) + ) + + return resources + + def present_firewall_resources(self): + self._get_firewall() + resources = self._diff_firewall_resources( + lambda to_add, before: to_add not in before, + ) + if resources: + if not self.module.check_mode: + actions = self.hcloud_firewall_resource.apply_to_resources(resources=resources) + for action in actions: + action.wait_until_finished() + + self.hcloud_firewall_resource.reload() + + self._mark_as_changed() + + def absent_firewall_resources(self): + self._get_firewall() + resources = self._diff_firewall_resources( + lambda to_remove, before: to_remove in before, + ) + if resources: + if not self.module.check_mode: + actions = self.hcloud_firewall_resource.remove_from_resources(resources=resources) + for action in actions: + action.wait_until_finished() + + self.hcloud_firewall_resource.reload() + + self._mark_as_changed() + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec={ + "firewall": {"type": "str", "required": True}, + "servers": {"type": "list", "elements": "str"}, + "label_selectors": {"type": "list", "elements": "str"}, + "state": { + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + }, + required_one_of=[["servers", "label_selectors"]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudFirewallResource.define_module() + + hcloud = AnsibleHCloudFirewallResource(module) + state = module.params.get("state") + if state == "absent": + hcloud.absent_firewall_resources() + elif state == "present": + hcloud.present_firewall_resources() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py new file mode 100644 index 000000000..e037dd7a1 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip.py @@ -0,0 +1,340 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: floating_ip + +short_description: Create and manage cloud Floating IPs on the Hetzner Cloud. + + +description: + - Create, update and manage cloud Floating IPs on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@lkaemmerling) +version_added: 0.1.0 +options: + id: + description: + - The ID of the Hetzner Cloud Floating IPs to manage. + - Only required if no Floating IP I(name) is given. + type: int + name: + description: + - The Name of the Hetzner Cloud Floating IPs to manage. + - Only required if no Floating IP I(id) is given or a Floating IP does not exist. + type: str + description: + description: + - The Description of the Hetzner Cloud Floating IPs. + type: str + home_location: + description: + - Home Location of the Hetzner Cloud Floating IP. + - Required if no I(server) is given and Floating IP does not exist. + type: str + server: + description: + - Server Name the Floating IP should be assigned to. + - Required if no I(home_location) is given and Floating IP does not exist. + type: str + type: + description: + - Type of the Floating IP. + - Required if Floating IP does not exist + choices: [ ipv4, ipv6 ] + type: str + force: + description: + - Force the assignment or deletion of the Floating IP. + type: bool + delete_protection: + description: + - Protect the Floating IP for deletion. + type: bool + labels: + description: + - User-defined labels (key-value pairs). + type: dict + state: + description: + - State of the Floating IP. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic IPv4 Floating IP + hetzner.hcloud.floating_ip: + name: my-floating-ip + home_location: fsn1 + type: ipv4 + state: present +- name: Create a basic IPv6 Floating IP + hetzner.hcloud.floating_ip: + name: my-floating-ip + home_location: fsn1 + type: ipv6 + state: present +- name: Assign a Floating IP to a server + hetzner.hcloud.floating_ip: + name: my-floating-ip + server: 1234 + state: present +- name: Assign a Floating IP to another server + hetzner.hcloud.floating_ip: + name: my-floating-ip + server: 1234 + force: true + state: present +- name: Floating IP should be absent + hetzner.hcloud.floating_ip: + name: my-floating-ip + state: absent +""" + +RETURN = """ +hcloud_floating_ip: + description: The Floating IP instance + returned: Always + type: complex + contains: + id: + description: ID of the Floating IP + type: int + returned: Always + sample: 12345 + name: + description: Name of the Floating IP + type: str + returned: Always + sample: my-floating-ip + description: + description: Description of the Floating IP + type: str + returned: Always + sample: my-floating-ip + ip: + description: IP Address of the Floating IP + type: str + returned: Always + sample: 116.203.104.109 + type: + description: Type of the Floating IP + type: str + returned: Always + sample: ipv4 + home_location: + description: Name of the home location of the Floating IP + type: str + returned: Always + sample: fsn1 + server: + description: Name of the server the Floating IP is assigned to. + type: str + returned: Always + sample: "my-server" + delete_protection: + description: True if Floating IP is protected for deletion + type: bool + returned: always + sample: false + version_added: "0.1.0" + labels: + description: User-defined labels (key-value pairs) + type: dict + returned: Always + sample: + key: value + mylabel: 123 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.floating_ips import BoundFloatingIP + + +class AnsibleHCloudFloatingIP(AnsibleHCloud): + represent = "hcloud_floating_ip" + + hcloud_floating_ip: BoundFloatingIP | None = None + + def _prepare_result(self): + server = None + + if self.hcloud_floating_ip.server is not None: + server = to_native(self.hcloud_floating_ip.server.name) + return { + "id": to_native(self.hcloud_floating_ip.id), + "name": to_native(self.hcloud_floating_ip.name), + "description": to_native(self.hcloud_floating_ip.description), + "ip": to_native(self.hcloud_floating_ip.ip), + "type": to_native(self.hcloud_floating_ip.type), + "home_location": to_native(self.hcloud_floating_ip.home_location.name), + "labels": self.hcloud_floating_ip.labels, + "server": server, + "delete_protection": self.hcloud_floating_ip.protection["delete"], + } + + def _get_floating_ip(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_floating_ip = self.client.floating_ips.get_by_id(self.module.params.get("id")) + else: + self.hcloud_floating_ip = self.client.floating_ips.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_floating_ip(self): + self.module.fail_on_missing_params(required_params=["type"]) + try: + params = { + "description": self.module.params.get("description"), + "type": self.module.params.get("type"), + "name": self.module.params.get("name"), + } + if self.module.params.get("home_location") is not None: + params["home_location"] = self.client.locations.get_by_name(self.module.params.get("home_location")) + elif self.module.params.get("server") is not None: + params["server"] = self.client.servers.get_by_name(self.module.params.get("server")) + else: + self.module.fail_json(msg="one of the following is required: home_location, server") + + if self.module.params.get("labels") is not None: + params["labels"] = self.module.params.get("labels") + if not self.module.check_mode: + resp = self.client.floating_ips.create(**params) + self.hcloud_floating_ip = resp.floating_ip + + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None: + self.hcloud_floating_ip.change_protection(delete=delete_protection).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_floating_ip() + + def _update_floating_ip(self): + try: + labels = self.module.params.get("labels") + if labels is not None and labels != self.hcloud_floating_ip.labels: + if not self.module.check_mode: + self.hcloud_floating_ip.update(labels=labels) + self._mark_as_changed() + + description = self.module.params.get("description") + if description is not None and description != self.hcloud_floating_ip.description: + if not self.module.check_mode: + self.hcloud_floating_ip.update(description=description) + self._mark_as_changed() + + server = self.module.params.get("server") + if server is not None and self.hcloud_floating_ip.server is not None: + if self.module.params.get("force") and server != self.hcloud_floating_ip.server.name: + if not self.module.check_mode: + self.hcloud_floating_ip.assign(self.client.servers.get_by_name(server)) + self._mark_as_changed() + elif server != self.hcloud_floating_ip.server.name: + self.module.warn( + "Floating IP is already assigned to another server " + f"{self.hcloud_floating_ip.server.name}. You need to " + "unassign the Floating IP or use force=true." + ) + self._mark_as_changed() + elif server is not None and self.hcloud_floating_ip.server is None: + if not self.module.check_mode: + self.hcloud_floating_ip.assign(self.client.servers.get_by_name(server)) + self._mark_as_changed() + elif server is None and self.hcloud_floating_ip.server is not None: + if not self.module.check_mode: + self.hcloud_floating_ip.unassign() + self._mark_as_changed() + + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None and delete_protection != self.hcloud_floating_ip.protection["delete"]: + if not self.module.check_mode: + self.hcloud_floating_ip.change_protection(delete=delete_protection).wait_until_finished() + self._mark_as_changed() + + self._get_floating_ip() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def present_floating_ip(self): + self._get_floating_ip() + if self.hcloud_floating_ip is None: + self._create_floating_ip() + else: + self._update_floating_ip() + + def delete_floating_ip(self): + try: + self._get_floating_ip() + if self.hcloud_floating_ip is not None: + if self.module.params.get("force") or self.hcloud_floating_ip.server is None: + if not self.module.check_mode: + self.client.floating_ips.delete(self.hcloud_floating_ip) + else: + self.module.warn( + "Floating IP is currently assigned to server " + f"{self.hcloud_floating_ip.server.name}. You need to " + "unassign the Floating IP or use force=true." + ) + self._mark_as_changed() + self.hcloud_floating_ip = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + description={"type": "str"}, + server={"type": "str"}, + home_location={"type": "str"}, + force={"type": "bool"}, + type={"choices": ["ipv4", "ipv6"]}, + labels={"type": "dict"}, + delete_protection={"type": "bool"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name"]], + mutually_exclusive=[["home_location", "server"]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudFloatingIP.define_module() + + hcloud = AnsibleHCloudFloatingIP(module) + state = module.params["state"] + if state == "absent": + hcloud.delete_floating_ip() + elif state == "present": + hcloud.present_floating_ip() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py new file mode 100644 index 000000000..663d29622 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/floating_ip_info.py @@ -0,0 +1,180 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: floating_ip_info + +short_description: Gather infos about the Hetzner Cloud Floating IPs. + +description: + - Gather facts about your Hetzner Cloud Floating IPs. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the Floating IP you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name for the Floating IP you want to get. + type: str + label_selector: + description: + - The label selector for the Floating IP you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud Floating ip infos + hetzner.hcloud.floating_ip_info: + register: output +- name: Print the gathered infos + debug: + var: output +""" + +RETURN = """ +hcloud_floating_ip_info: + description: The Floating ip infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the Floating IP + returned: always + type: int + sample: 1937415 + name: + description: Name of the Floating IP + returned: Always + type: str + sample: my-floating-ip + version_added: "0.1.0" + description: + description: Description of the Floating IP + returned: always + type: str + sample: Falkenstein DC 8 + ip: + description: IP address of the Floating IP + returned: always + type: str + sample: 131.232.99.1 + type: + description: Type of the Floating IP + returned: always + type: str + sample: ipv4 + server: + description: Name of the server where the Floating IP is assigned to. + returned: always + type: str + sample: my-server + home_location: + description: Location the Floating IP was created in + returned: always + type: str + sample: fsn1 + delete_protection: + description: True if the Floating IP is protected for deletion + returned: always + type: bool + version_added: "0.1.0" + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.floating_ips import BoundFloatingIP + + +class AnsibleHCloudFloatingIPInfo(AnsibleHCloud): + represent = "hcloud_floating_ip_info" + + hcloud_floating_ip_info: list[BoundFloatingIP] | None = None + + def _prepare_result(self): + tmp = [] + + for floating_ip in self.hcloud_floating_ip_info: + if floating_ip is not None: + server_name = None + if floating_ip.server is not None: + server_name = floating_ip.server.name + tmp.append( + { + "id": to_native(floating_ip.id), + "name": to_native(floating_ip.name), + "description": to_native(floating_ip.description), + "ip": to_native(floating_ip.ip), + "type": to_native(floating_ip.type), + "server": to_native(server_name), + "home_location": to_native(floating_ip.home_location.name), + "labels": floating_ip.labels, + "delete_protection": floating_ip.protection["delete"], + } + ) + + return tmp + + def get_floating_ips(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_floating_ip_info = [self.client.floating_ips.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_floating_ip_info = [self.client.floating_ips.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("label_selector") is not None: + self.hcloud_floating_ip_info = self.client.floating_ips.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_floating_ip_info = self.client.floating_ips.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + label_selector={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudFloatingIPInfo.define_module() + hcloud = AnsibleHCloudFloatingIPInfo(module) + + hcloud.get_floating_ips() + result = hcloud.get_result() + + ansible_info = {"hcloud_floating_ip_info": result["hcloud_floating_ip_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate.py deleted file mode 100644 index 0f6dcf0f2..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate.py +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_certificate - -short_description: Create and manage certificates on the Hetzner Cloud. - - -description: - - Create, update and manage certificates on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@lkaemmerling) - -options: - id: - description: - - The ID of the Hetzner Cloud certificate to manage. - - Only required if no certificate I(name) is given - type: int - name: - description: - - The Name of the Hetzner Cloud certificate to manage. - - Only required if no certificate I(id) is given or a certificate does not exist. - type: str - labels: - description: - - User-defined labels (key-value pairs) - type: dict - certificate: - description: - - Certificate and chain in PEM format, in order so that each record directly certifies the one preceding. - - Required if certificate does not exist. - type: str - private_key: - description: - - Certificate key in PEM format. - - Required if certificate does not exist. - type: str - domain_names: - description: - - Certificate key in PEM format. - - Required if certificate does not exist. - type: list - default: [ ] - elements: str - type: - description: - - Choose between uploading a Certificate in PEM format or requesting a managed Let's Encrypt Certificate. - default: uploaded - choices: [ uploaded, managed ] - type: str - state: - description: - - State of the certificate. - default: present - choices: [ absent, present ] - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic certificate - hcloud_certificate: - name: my-certificate - certificate: "ssh-rsa AAAjjk76kgf...Xt" - private_key: "ssh-rsa AAAjjk76kgf...Xt" - state: present - -- name: Create a certificate with labels - hcloud_certificate: - name: my-certificate - certificate: "ssh-rsa AAAjjk76kgf...Xt" - private_key: "ssh-rsa AAAjjk76kgf...Xt" - labels: - key: value - mylabel: 123 - state: present - -- name: Ensure the certificate is absent (remove if needed) - hcloud_certificate: - name: my-certificate - state: absent -""" - -RETURN = """ -hcloud_certificate: - description: The certificate instance - returned: Always - type: complex - contains: - id: - description: Numeric identifier of the certificate - returned: always - type: int - sample: 1937415 - name: - description: Name of the certificate - returned: always - type: str - sample: my website cert - fingerprint: - description: Fingerprint of the certificate - returned: always - type: str - sample: "03:c7:55:9b:2a:d1:04:17:09:f6:d0:7f:18:34:63:d4:3e:5f" - certificate: - description: Certificate and chain in PEM format - returned: always - type: str - sample: "-----BEGIN CERTIFICATE-----..." - domain_names: - description: List of Domains and Subdomains covered by the Certificate - returned: always - type: dict - not_valid_before: - description: Point in time when the Certificate becomes valid (in ISO-8601 format) - returned: always - type: str - not_valid_after: - description: Point in time when the Certificate stops being valid (in ISO-8601 format) - returned: always - type: str - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudCertificate(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_certificate") - self.hcloud_certificate = None - - def _prepare_result(self): - return { - "id": to_native(self.hcloud_certificate.id), - "name": to_native(self.hcloud_certificate.name), - "type": to_native(self.hcloud_certificate.type), - "fingerprint": to_native(self.hcloud_certificate.fingerprint), - "certificate": to_native(self.hcloud_certificate.certificate), - "not_valid_before": to_native(self.hcloud_certificate.not_valid_before), - "not_valid_after": to_native(self.hcloud_certificate.not_valid_after), - "domain_names": [to_native(domain) for domain in self.hcloud_certificate.domain_names], - "labels": self.hcloud_certificate.labels - } - - def _get_certificate(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_certificate = self.client.certificates.get_by_id( - self.module.params.get("id") - ) - elif self.module.params.get("name") is not None: - self.hcloud_certificate = self.client.certificates.get_by_name( - self.module.params.get("name") - ) - - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_certificate(self): - self.module.fail_on_missing_params( - required_params=["name"] - ) - - params = { - "name": self.module.params.get("name"), - "labels": self.module.params.get("labels") - } - if self.module.params.get('type') == 'uploaded': - self.module.fail_on_missing_params( - required_params=["certificate", "private_key"] - ) - params["certificate"] = self.module.params.get("certificate") - params["private_key"] = self.module.params.get("private_key") - if not self.module.check_mode: - try: - self.client.certificates.create(**params) - except Exception as e: - self.module.fail_json(msg=e.message) - else: - self.module.fail_on_missing_params( - required_params=["domain_names"] - ) - params["domain_names"] = self.module.params.get("domain_names") - if not self.module.check_mode: - try: - resp = self.client.certificates.create_managed(**params) - resp.action.wait_until_finished(max_retries=1000) - except Exception as e: - self.module.fail_json(msg=e.message) - - self._mark_as_changed() - self._get_certificate() - - def _update_certificate(self): - try: - name = self.module.params.get("name") - if name is not None and self.hcloud_certificate.name != name: - self.module.fail_on_missing_params( - required_params=["id"] - ) - if not self.module.check_mode: - self.hcloud_certificate.update(name=name) - self._mark_as_changed() - - labels = self.module.params.get("labels") - if labels is not None and self.hcloud_certificate.labels != labels: - if not self.module.check_mode: - self.hcloud_certificate.update(labels=labels) - self._mark_as_changed() - except Exception as e: - self.module.fail_json(msg=e.message) - self._get_certificate() - - def present_certificate(self): - self._get_certificate() - if self.hcloud_certificate is None: - self._create_certificate() - else: - self._update_certificate() - - def delete_certificate(self): - self._get_certificate() - if self.hcloud_certificate is not None: - if not self.module.check_mode: - try: - self.client.certificates.delete(self.hcloud_certificate) - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self.hcloud_certificate = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - type={ - "choices": ["uploaded", "managed"], - "default": "uploaded", - }, - domain_names={"type": "list", "elements": "str", "default": []}, - certificate={"type": "str"}, - private_key={"type": "str", "no_log": True}, - labels={"type": "dict"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name']], - required_if=[['state', 'present', ['name']]], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudCertificate.define_module() - - hcloud = AnsibleHcloudCertificate(module) - state = module.params.get("state") - if state == "absent": - hcloud.delete_certificate() - elif state == "present": - hcloud.present_certificate() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate_info.py deleted file mode 100644 index 855706f1f..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_certificate_info.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_certificate_info -short_description: Gather infos about your Hetzner Cloud certificates. -description: - - Gather facts about your Hetzner Cloud certificates. -author: - - Lukas Kaemmerling (@LKaemmerling) -options: - id: - description: - - The ID of the certificate you want to get. - type: int - name: - description: - - The name of the certificate you want to get. - type: str - label_selector: - description: - - The label selector for the certificate you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud certificate infos - hcloud_certificate_info: - register: output -- name: Print the gathered infos - debug: - var: output.hcloud_certificate_info -""" - -RETURN = """ -hcloud_certificate_info: - description: The certificate instances - returned: Always - type: complex - contains: - id: - description: Numeric identifier of the certificate - returned: always - type: int - sample: 1937415 - name: - description: Name of the certificate - returned: always - type: str - sample: my website cert - fingerprint: - description: Fingerprint of the certificate - returned: always - type: str - sample: "03:c7:55:9b:2a:d1:04:17:09:f6:d0:7f:18:34:63:d4:3e:5f" - certificate: - description: Certificate and chain in PEM format - returned: always - type: str - sample: "-----BEGIN CERTIFICATE-----..." - domain_names: - description: List of Domains and Subdomains covered by the Certificate - returned: always - type: dict - not_valid_before: - description: Point in time when the Certificate becomes valid (in ISO-8601 format) - returned: always - type: str - not_valid_after: - description: Point in time when the Certificate stops being valid (in ISO-8601 format) - returned: always - type: str - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudCertificateInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_certificate_info") - self.hcloud_certificate_info = None - - def _prepare_result(self): - certificates = [] - - for certificate in self.hcloud_certificate_info: - if certificate: - certificates.append({ - "id": to_native(certificate.id), - "name": to_native(certificate.name), - "fingerprint": to_native(certificate.fingerprint), - "certificate": to_native(certificate.certificate), - "not_valid_before": to_native(certificate.not_valid_before), - "not_valid_after": to_native(certificate.not_valid_after), - "domain_names": [to_native(domain) for domain in certificate.domain_names], - "labels": certificate.labels - }) - return certificates - - def get_certificates(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_certificate_info = [self.client.certificates.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_certificate_info = [self.client.certificates.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_certificate_info = self.client.certificates.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_certificate_info = self.client.certificates.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudCertificateInfo.define_module() - - hcloud = AnsibleHcloudCertificateInfo(module) - hcloud.get_certificates() - result = hcloud.get_result() - - ansible_info = { - 'hcloud_certificate_info': result['hcloud_certificate_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_facts.py deleted file mode 100644 index 8cebabf8c..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_facts.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_datacenter_info - -short_description: Gather info about the Hetzner Cloud datacenters. - -description: - - Gather info about your Hetzner Cloud datacenters. - - This module was called C(hcloud_datacenter_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_datacenter_facts). - Note that the M(hetzner.hcloud.hcloud_datacenter_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_datacenter_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the datacenter you want to get. - type: int - name: - description: - - The name of the datacenter you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud datacenter info - hcloud_datacenter_info: - register: output -- name: Print the gathered info - debug: - var: output -""" - -RETURN = """ -hcloud_datacenter_info: - description: - - The datacenter info as list - - This module was called C(hcloud_datacenter_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_datacenter_facts). - Note that the M(hetzner.hcloud.hcloud_datacenter_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_datacenter_info)! - returned: always - type: complex - contains: - id: - description: Numeric identifier of the datacenter - returned: always - type: int - sample: 1937415 - name: - description: Name of the datacenter - returned: always - type: str - sample: fsn1-dc8 - description: - description: Detail description of the datacenter - returned: always - type: str - sample: Falkenstein DC 8 - location: - description: Name of the location where the datacenter resides in - returned: always - type: str - sample: fsn1 - city: - description: City of the location - returned: always - type: str - sample: fsn1 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudDatacenterInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_datacenter_info") - self.hcloud_datacenter_info = None - - def _prepare_result(self): - tmp = [] - - for datacenter in self.hcloud_datacenter_info: - if datacenter is not None: - tmp.append({ - "id": to_native(datacenter.id), - "name": to_native(datacenter.name), - "description": to_native(datacenter.description), - "location": to_native(datacenter.location.name) - }) - - return tmp - - def get_datacenters(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_datacenter_info = [self.client.datacenters.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_datacenter_info = [self.client.datacenters.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_datacenter_info = self.client.datacenters.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudDatacenterInfo.define_module() - - is_old_facts = module._name == 'hcloud_datacenter_facts' - if is_old_facts: - module.deprecate("The 'hcloud_datacenter_facts' module has been renamed to 'hcloud_datacenter_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - hcloud = AnsibleHcloudDatacenterInfo(module) - - hcloud.get_datacenters() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_datacenter_facts': result['hcloud_datacenter_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_datacenter_info': result['hcloud_datacenter_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_info.py deleted file mode 100644 index 8cebabf8c..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_datacenter_info.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_datacenter_info - -short_description: Gather info about the Hetzner Cloud datacenters. - -description: - - Gather info about your Hetzner Cloud datacenters. - - This module was called C(hcloud_datacenter_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_datacenter_facts). - Note that the M(hetzner.hcloud.hcloud_datacenter_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_datacenter_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the datacenter you want to get. - type: int - name: - description: - - The name of the datacenter you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud datacenter info - hcloud_datacenter_info: - register: output -- name: Print the gathered info - debug: - var: output -""" - -RETURN = """ -hcloud_datacenter_info: - description: - - The datacenter info as list - - This module was called C(hcloud_datacenter_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_datacenter_facts). - Note that the M(hetzner.hcloud.hcloud_datacenter_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_datacenter_info)! - returned: always - type: complex - contains: - id: - description: Numeric identifier of the datacenter - returned: always - type: int - sample: 1937415 - name: - description: Name of the datacenter - returned: always - type: str - sample: fsn1-dc8 - description: - description: Detail description of the datacenter - returned: always - type: str - sample: Falkenstein DC 8 - location: - description: Name of the location where the datacenter resides in - returned: always - type: str - sample: fsn1 - city: - description: City of the location - returned: always - type: str - sample: fsn1 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudDatacenterInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_datacenter_info") - self.hcloud_datacenter_info = None - - def _prepare_result(self): - tmp = [] - - for datacenter in self.hcloud_datacenter_info: - if datacenter is not None: - tmp.append({ - "id": to_native(datacenter.id), - "name": to_native(datacenter.name), - "description": to_native(datacenter.description), - "location": to_native(datacenter.location.name) - }) - - return tmp - - def get_datacenters(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_datacenter_info = [self.client.datacenters.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_datacenter_info = [self.client.datacenters.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_datacenter_info = self.client.datacenters.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudDatacenterInfo.define_module() - - is_old_facts = module._name == 'hcloud_datacenter_facts' - if is_old_facts: - module.deprecate("The 'hcloud_datacenter_facts' module has been renamed to 'hcloud_datacenter_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - hcloud = AnsibleHcloudDatacenterInfo(module) - - hcloud.get_datacenters() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_datacenter_facts': result['hcloud_datacenter_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_datacenter_info': result['hcloud_datacenter_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_firewall.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_firewall.py deleted file mode 100644 index 34608977e..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_firewall.py +++ /dev/null @@ -1,359 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_firewall - -short_description: Create and manage firewalls on the Hetzner Cloud. - - -description: - - Create, update and manage firewalls on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@lkaemmerling) - -options: - id: - description: - - The ID of the Hetzner Cloud firewall to manage. - - Only required if no firewall I(name) is given - type: int - name: - description: - - The Name of the Hetzner Cloud firewall to manage. - - Only required if no firewall I(id) is given, or a firewall does not exist. - type: str - labels: - description: - - User-defined labels (key-value pairs) - type: dict - rules: - description: - - List of rules the firewall should contain. - type: list - elements: dict - suboptions: - direction: - description: - - The direction of the firewall rule. - type: str - choices: [ in, out ] - port: - description: - - The port of the firewall rule. - type: str - protocol: - description: - - The protocol of the firewall rule. - type: str - choices: [ icmp, tcp, udp, esp, gre ] - source_ips: - description: - - List of CIDRs that are allowed within this rule - type: list - elements: str - default: [ ] - destination_ips: - description: - - List of CIDRs that are allowed within this rule - type: list - elements: str - default: [ ] - description: - description: - - User defined description of this rule. - type: str - state: - description: - - State of the firewall. - default: present - choices: [ absent, present ] - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud -''' - -EXAMPLES = """ -- name: Create a basic firewall - hcloud_firewall: - name: my-firewall - state: present - -- name: Create a firewall with rules - hcloud_firewall: - name: my-firewall - rules: - - direction: in - protocol: icmp - source_ips: - - 0.0.0.0/0 - - ::/0 - description: allow icmp in - state: present - -- name: Create a firewall with labels - hcloud_firewall: - name: my-firewall - labels: - key: value - mylabel: 123 - state: present - -- name: Ensure the firewall is absent (remove if needed) - hcloud_firewall: - name: my-firewall - state: absent -""" - -RETURN = """ -hcloud_firewall: - description: The firewall instance - returned: Always - type: complex - contains: - id: - description: Numeric identifier of the firewall - returned: always - type: int - sample: 1937415 - name: - description: Name of the firewall - returned: always - type: str - sample: my firewall - rules: - description: List of Rules within this Firewall - returned: always - type: complex - contains: - direction: - description: Direction of the Firewall Rule - type: str - returned: always - sample: in - protocol: - description: Protocol of the Firewall Rule - type: str - returned: always - sample: icmp - port: - description: Port of the Firewall Rule, None/Null if protocol is icmp - type: str - returned: always - sample: in - source_ips: - description: Source IPs of the Firewall - type: list - elements: str - returned: always - destination_ips: - description: Source IPs of the Firewall - type: list - elements: str - returned: always - description: - description: User defined description of the Firewall Rule - type: str - returned: always - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud -import time - -try: - from hcloud.firewalls.domain import FirewallRule - from hcloud import APIException -except ImportError: - APIException = None - FirewallRule = None - - -class AnsibleHcloudFirewall(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_firewall") - self.hcloud_firewall = None - - def _prepare_result(self): - return { - "id": to_native(self.hcloud_firewall.id), - "name": to_native(self.hcloud_firewall.name), - "rules": [self._prepare_result_rule(rule) for rule in self.hcloud_firewall.rules], - "labels": self.hcloud_firewall.labels - } - - def _prepare_result_rule(self, rule): - return { - "direction": rule.direction, - "protocol": to_native(rule.protocol), - "port": to_native(rule.port) if rule.port is not None else None, - "source_ips": [to_native(cidr) for cidr in rule.source_ips], - "destination_ips": [to_native(cidr) for cidr in rule.destination_ips], - "description": to_native(rule.description) if rule.description is not None else None, - } - - def _get_firewall(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_firewall = self.client.firewalls.get_by_id( - self.module.params.get("id") - ) - elif self.module.params.get("name") is not None: - self.hcloud_firewall = self.client.firewalls.get_by_name( - self.module.params.get("name") - ) - - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_firewall(self): - self.module.fail_on_missing_params( - required_params=["name"] - ) - params = { - "name": self.module.params.get("name"), - "labels": self.module.params.get("labels") - } - rules = self.module.params.get("rules") - if rules is not None: - params["rules"] = [ - FirewallRule( - direction=rule["direction"], - protocol=rule["protocol"], - source_ips=rule["source_ips"] if rule["source_ips"] is not None else [], - destination_ips=rule["destination_ips"] if rule["destination_ips"] is not None else [], - port=rule["port"], - description=rule["description"], - ) - for rule in rules - ] - if not self.module.check_mode: - try: - self.client.firewalls.create(**params) - except Exception as e: - self.module.fail_json(msg=e.message, **params) - self._mark_as_changed() - self._get_firewall() - - def _update_firewall(self): - name = self.module.params.get("name") - if name is not None and self.hcloud_firewall.name != name: - self.module.fail_on_missing_params( - required_params=["id"] - ) - if not self.module.check_mode: - self.hcloud_firewall.update(name=name) - self._mark_as_changed() - - labels = self.module.params.get("labels") - if labels is not None and self.hcloud_firewall.labels != labels: - if not self.module.check_mode: - self.hcloud_firewall.update(labels=labels) - self._mark_as_changed() - - rules = self.module.params.get("rules") - if rules is not None and rules != [self._prepare_result_rule(rule) for rule in self.hcloud_firewall.rules]: - if not self.module.check_mode: - new_rules = [ - FirewallRule( - direction=rule["direction"], - protocol=rule["protocol"], - source_ips=rule["source_ips"] if rule["source_ips"] is not None else [], - destination_ips=rule["destination_ips"] if rule["destination_ips"] is not None else [], - port=rule["port"], - description=rule["description"], - ) - for rule in rules - ] - self.hcloud_firewall.set_rules(new_rules) - self._mark_as_changed() - self._get_firewall() - - def present_firewall(self): - self._get_firewall() - if self.hcloud_firewall is None: - self._create_firewall() - else: - self._update_firewall() - - def delete_firewall(self): - self._get_firewall() - if self.hcloud_firewall is not None: - if not self.module.check_mode: - retry_count = 0 - while retry_count < 10: - try: - self.client.firewalls.delete(self.hcloud_firewall) - break - except APIException as e: - if "is still in use" in e.message: - retry_count = retry_count + 1 - time.sleep(0.5 * retry_count) - else: - self.module.fail_json(msg=e.message) - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self.hcloud_firewall = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - rules=dict( - type="list", - elements="dict", - options=dict( - direction={"type": "str", "choices": ["in", "out"]}, - protocol={"type": "str", "choices": ["icmp", "udp", "tcp", "esp", "gre"]}, - port={"type": "str"}, - source_ips={"type": "list", "elements": "str", "default": []}, - destination_ips={"type": "list", "elements": "str", "default": []}, - description={"type": "str"}, - ), - required_together=[["direction", "protocol"]], - ), - labels={"type": "dict"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name']], - required_if=[['state', 'present', ['name']]], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudFirewall.define_module() - - hcloud = AnsibleHcloudFirewall(module) - state = module.params.get("state") - if state == "absent": - hcloud.delete_firewall() - elif state == "present": - hcloud.present_firewall() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip.py deleted file mode 100644 index 1ee61ea13..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip.py +++ /dev/null @@ -1,355 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_floating_ip - -short_description: Create and manage cloud Floating IPs on the Hetzner Cloud. - - -description: - - Create, update and manage cloud Floating IPs on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@lkaemmerling) -version_added: 0.1.0 -options: - id: - description: - - The ID of the Hetzner Cloud Floating IPs to manage. - - Only required if no Floating IP I(name) is given. - type: int - name: - description: - - The Name of the Hetzner Cloud Floating IPs to manage. - - Only required if no Floating IP I(id) is given or a Floating IP does not exist. - type: str - description: - description: - - The Description of the Hetzner Cloud Floating IPs. - type: str - home_location: - description: - - Home Location of the Hetzner Cloud Floating IP. - - Required if no I(server) is given and Floating IP does not exist. - type: str - server: - description: - - Server Name the Floating IP should be assigned to. - - Required if no I(home_location) is given and Floating IP does not exist. - type: str - type: - description: - - Type of the Floating IP. - - Required if Floating IP does not exist - choices: [ ipv4, ipv6 ] - type: str - force: - description: - - Force the assignment or deletion of the Floating IP. - type: bool - delete_protection: - description: - - Protect the Floating IP for deletion. - type: bool - labels: - description: - - User-defined labels (key-value pairs). - type: dict - state: - description: - - State of the Floating IP. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.6.0 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic IPv4 Floating IP - hcloud_floating_ip: - name: my-floating-ip - home_location: fsn1 - type: ipv4 - state: present -- name: Create a basic IPv6 Floating IP - hcloud_floating_ip: - name: my-floating-ip - home_location: fsn1 - type: ipv6 - state: present -- name: Assign a Floating IP to a server - hcloud_floating_ip: - name: my-floating-ip - server: 1234 - state: present -- name: Assign a Floating IP to another server - hcloud_floating_ip: - name: my-floating-ip - server: 1234 - force: yes - state: present -- name: Floating IP should be absent - hcloud_floating_ip: - name: my-floating-ip - state: absent -""" - -RETURN = """ -hcloud_floating_ip: - description: The Floating IP instance - returned: Always - type: complex - contains: - id: - description: ID of the Floating IP - type: int - returned: Always - sample: 12345 - name: - description: Name of the Floating IP - type: str - returned: Always - sample: my-floating-ip - description: - description: Description of the Floating IP - type: str - returned: Always - sample: my-floating-ip - ip: - description: IP Address of the Floating IP - type: str - returned: Always - sample: 116.203.104.109 - type: - description: Type of the Floating IP - type: str - returned: Always - sample: ipv4 - home_location: - description: Name of the home location of the Floating IP - type: str - returned: Always - sample: fsn1 - server: - description: Name of the server the Floating IP is assigned to. - type: str - returned: Always - sample: "my-server" - delete_protection: - description: True if Floating IP is protected for deletion - type: bool - returned: always - sample: false - version_added: "0.1.0" - labels: - description: User-defined labels (key-value pairs) - type: dict - returned: Always - sample: - key: value - mylabel: 123 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudFloatingIP(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_floating_ip") - self.hcloud_floating_ip = None - - def _prepare_result(self): - server = None - - if self.hcloud_floating_ip.server is not None: - server = to_native(self.hcloud_floating_ip.server.name) - return { - "id": to_native(self.hcloud_floating_ip.id), - "name": to_native(self.hcloud_floating_ip.name), - "description": to_native(self.hcloud_floating_ip.description), - "ip": to_native(self.hcloud_floating_ip.ip), - "type": to_native(self.hcloud_floating_ip.type), - "home_location": to_native(self.hcloud_floating_ip.home_location.name), - "labels": self.hcloud_floating_ip.labels, - "server": server, - "delete_protection": self.hcloud_floating_ip.protection["delete"], - } - - def _get_floating_ip(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_floating_ip = self.client.floating_ips.get_by_id( - self.module.params.get("id") - ) - else: - self.hcloud_floating_ip = self.client.floating_ips.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_floating_ip(self): - self.module.fail_on_missing_params( - required_params=["type"] - ) - try: - params = { - "description": self.module.params.get("description"), - "type": self.module.params.get("type"), - "name": self.module.params.get("name"), - } - if self.module.params.get("home_location") is not None: - params["home_location"] = self.client.locations.get_by_name( - self.module.params.get("home_location") - ) - elif self.module.params.get("server") is not None: - params["server"] = self.client.servers.get_by_name( - self.module.params.get("server") - ) - else: - self.module.fail_json(msg="one of the following is required: home_location, server") - - if self.module.params.get("labels") is not None: - params["labels"] = self.module.params.get("labels") - if not self.module.check_mode: - resp = self.client.floating_ips.create(**params) - self.hcloud_floating_ip = resp.floating_ip - - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None: - self.hcloud_floating_ip.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self._get_floating_ip() - - def _update_floating_ip(self): - try: - labels = self.module.params.get("labels") - if labels is not None and labels != self.hcloud_floating_ip.labels: - if not self.module.check_mode: - self.hcloud_floating_ip.update(labels=labels) - self._mark_as_changed() - - description = self.module.params.get("description") - if description is not None and description != self.hcloud_floating_ip.description: - if not self.module.check_mode: - self.hcloud_floating_ip.update(description=description) - self._mark_as_changed() - - server = self.module.params.get("server") - if server is not None and self.hcloud_floating_ip.server is not None: - if self.module.params.get("force") and server != self.hcloud_floating_ip.server.name: - if not self.module.check_mode: - self.hcloud_floating_ip.assign( - self.client.servers.get_by_name(server) - ) - self._mark_as_changed() - elif server != self.hcloud_floating_ip.server.name: - self.module.warn( - "Floating IP is already assigned to another server %s. You need to unassign the Floating IP or use force=yes." - % self.hcloud_floating_ip.server.name - ) - self._mark_as_changed() - elif server is not None and self.hcloud_floating_ip.server is None: - if not self.module.check_mode: - self.hcloud_floating_ip.assign( - self.client.servers.get_by_name(server) - ) - self._mark_as_changed() - elif server is None and self.hcloud_floating_ip.server is not None: - if not self.module.check_mode: - self.hcloud_floating_ip.unassign() - self._mark_as_changed() - - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None and delete_protection != self.hcloud_floating_ip.protection["delete"]: - if not self.module.check_mode: - self.hcloud_floating_ip.change_protection(delete=delete_protection).wait_until_finished() - self._mark_as_changed() - - self._get_floating_ip() - except Exception as e: - self.module.fail_json(msg=e.message) - - def present_floating_ip(self): - self._get_floating_ip() - if self.hcloud_floating_ip is None: - self._create_floating_ip() - else: - self._update_floating_ip() - - def delete_floating_ip(self): - try: - self._get_floating_ip() - if self.hcloud_floating_ip is not None: - if self.module.params.get("force") or self.hcloud_floating_ip.server is None: - if not self.module.check_mode: - self.client.floating_ips.delete(self.hcloud_floating_ip) - else: - self.module.warn( - "Floating IP is currently assigned to server %s. You need to unassign the Floating IP or use force=yes." - % self.hcloud_floating_ip.server.name - ) - self._mark_as_changed() - self.hcloud_floating_ip = None - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - description={"type": "str"}, - server={"type": "str"}, - home_location={"type": "str"}, - force={"type": "bool"}, - type={"choices": ["ipv4", "ipv6"]}, - labels={"type": "dict"}, - delete_protection={"type": "bool"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name']], - mutually_exclusive=[['home_location', 'server']], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudFloatingIP.define_module() - - hcloud = AnsibleHcloudFloatingIP(module) - state = module.params["state"] - if state == "absent": - hcloud.delete_floating_ip() - elif state == "present": - hcloud.present_floating_ip() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_facts.py deleted file mode 100644 index 2ec359600..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_facts.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_floating_ip_info - -short_description: Gather infos about the Hetzner Cloud Floating IPs. - -description: - - Gather facts about your Hetzner Cloud Floating IPs. - - This module was called C(hcloud_floating_ip_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_floating_ip_facts). - Note that the M(hetzner.hcloud.hcloud_floating_ip_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_floating_ip_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the Floating IP you want to get. - type: int - label_selector: - description: - - The label selector for the Floating IP you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud Floating ip infos - hcloud_floating_ip_info: - register: output -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_floating_ip_info: - description: The Floating ip infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the Floating IP - returned: always - type: int - sample: 1937415 - name: - description: Name of the Floating IP - returned: Always - type: str - sample: my-floating-ip - version_added: "0.1.0" - description: - description: Description of the Floating IP - returned: always - type: str - sample: Falkenstein DC 8 - ip: - description: IP address of the Floating IP - returned: always - type: str - sample: 131.232.99.1 - type: - description: Type of the Floating IP - returned: always - type: str - sample: ipv4 - server: - description: Name of the server where the Floating IP is assigned to. - returned: always - type: str - sample: my-server - home_location: - description: Location the Floating IP was created in - returned: always - type: str - sample: fsn1 - delete_protection: - description: True if the Floating IP is protected for deletion - returned: always - type: bool - version_added: "0.1.0" - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudFloatingIPInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_floating_ip_info") - self.hcloud_floating_ip_info = None - - def _prepare_result(self): - tmp = [] - - for floating_ip in self.hcloud_floating_ip_info: - if floating_ip is not None: - server_name = None - if floating_ip.server is not None: - server_name = floating_ip.server.name - tmp.append({ - "id": to_native(floating_ip.id), - "name": to_native(floating_ip.name), - "description": to_native(floating_ip.description), - "ip": to_native(floating_ip.ip), - "type": to_native(floating_ip.type), - "server": to_native(server_name), - "home_location": to_native(floating_ip.home_location.name), - "labels": floating_ip.labels, - "delete_protection": floating_ip.protection["delete"], - }) - - return tmp - - def get_floating_ips(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_floating_ip_info = [self.client.floating_ips.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_floating_ip_info = self.client.floating_ips.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_floating_ip_info = self.client.floating_ips.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudFloatingIPInfo.define_module() - - is_old_facts = module._name == 'hcloud_floating_ip_facts' - if is_old_facts: - module.deprecate("The 'hcloud_floating_ip_facts' module has been renamed to 'hcloud_floating_ip_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudFloatingIPInfo(module) - - hcloud.get_floating_ips() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_floating_ip_facts': result['hcloud_floating_ip_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_floating_ip_info': result['hcloud_floating_ip_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_info.py deleted file mode 100644 index 2ec359600..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_floating_ip_info.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_floating_ip_info - -short_description: Gather infos about the Hetzner Cloud Floating IPs. - -description: - - Gather facts about your Hetzner Cloud Floating IPs. - - This module was called C(hcloud_floating_ip_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_floating_ip_facts). - Note that the M(hetzner.hcloud.hcloud_floating_ip_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_floating_ip_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the Floating IP you want to get. - type: int - label_selector: - description: - - The label selector for the Floating IP you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud Floating ip infos - hcloud_floating_ip_info: - register: output -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_floating_ip_info: - description: The Floating ip infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the Floating IP - returned: always - type: int - sample: 1937415 - name: - description: Name of the Floating IP - returned: Always - type: str - sample: my-floating-ip - version_added: "0.1.0" - description: - description: Description of the Floating IP - returned: always - type: str - sample: Falkenstein DC 8 - ip: - description: IP address of the Floating IP - returned: always - type: str - sample: 131.232.99.1 - type: - description: Type of the Floating IP - returned: always - type: str - sample: ipv4 - server: - description: Name of the server where the Floating IP is assigned to. - returned: always - type: str - sample: my-server - home_location: - description: Location the Floating IP was created in - returned: always - type: str - sample: fsn1 - delete_protection: - description: True if the Floating IP is protected for deletion - returned: always - type: bool - version_added: "0.1.0" - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudFloatingIPInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_floating_ip_info") - self.hcloud_floating_ip_info = None - - def _prepare_result(self): - tmp = [] - - for floating_ip in self.hcloud_floating_ip_info: - if floating_ip is not None: - server_name = None - if floating_ip.server is not None: - server_name = floating_ip.server.name - tmp.append({ - "id": to_native(floating_ip.id), - "name": to_native(floating_ip.name), - "description": to_native(floating_ip.description), - "ip": to_native(floating_ip.ip), - "type": to_native(floating_ip.type), - "server": to_native(server_name), - "home_location": to_native(floating_ip.home_location.name), - "labels": floating_ip.labels, - "delete_protection": floating_ip.protection["delete"], - }) - - return tmp - - def get_floating_ips(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_floating_ip_info = [self.client.floating_ips.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_floating_ip_info = self.client.floating_ips.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_floating_ip_info = self.client.floating_ips.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudFloatingIPInfo.define_module() - - is_old_facts = module._name == 'hcloud_floating_ip_facts' - if is_old_facts: - module.deprecate("The 'hcloud_floating_ip_facts' module has been renamed to 'hcloud_floating_ip_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudFloatingIPInfo(module) - - hcloud.get_floating_ips() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_floating_ip_facts': result['hcloud_floating_ip_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_floating_ip_info': result['hcloud_floating_ip_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_facts.py deleted file mode 100644 index 8acd8846a..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_facts.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_image_info - -short_description: Gather infos about your Hetzner Cloud images. - - -description: - - Gather infos about your Hetzner Cloud images. - - This module was called C(hcloud_location_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_location_facts). - Note that the M(hetzner.hcloud.hcloud_image_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_image_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the image you want to get. - type: int - name: - description: - - The name of the image you want to get. - type: str - label_selector: - description: - - The label selector for the images you want to get. - type: str - type: - description: - - The type for the images you want to get. - default: system - choices: [ system, snapshot, backup ] - type: str - architecture: - description: - - The architecture for the images you want to get. - type: str - choices: [ x86, arm ] -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud image infos - hcloud_image_info: - register: output - -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_image_info: - description: The image infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the image - returned: always - type: int - sample: 1937415 - type: - description: Type of the image - returned: always - type: str - sample: system - status: - description: Status of the image - returned: always - type: str - sample: available - name: - description: Name of the image - returned: always - type: str - sample: ubuntu-18.04 - description: - description: Detail description of the image - returned: always - type: str - sample: Ubuntu 18.04 Standard 64 bit - os_flavor: - description: OS flavor of the image - returned: always - type: str - sample: ubuntu - os_version: - description: OS version of the image - returned: always - type: str - sample: 18.04 - architecture: - description: Image is compatible with this architecture - returned: always - type: str - sample: x86 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudImageInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_image_info") - self.hcloud_image_info = None - - def _prepare_result(self): - tmp = [] - - for image in self.hcloud_image_info: - if image is not None: - tmp.append({ - "id": to_native(image.id), - "status": to_native(image.status), - "type": to_native(image.type), - "name": to_native(image.name), - "description": to_native(image.description), - "os_flavor": to_native(image.os_flavor), - "os_version": to_native(image.os_version), - "architecture": to_native(image.architecture), - "labels": image.labels, - }) - return tmp - - def get_images(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_image_info = [self.client.images.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None and self.module.params.get("architecture") is not None: - self.hcloud_image_info = [self.client.images.get_by_name_and_architecture( - self.module.params.get("name"), - self.module.params.get("architecture") - )] - elif self.module.params.get("name") is not None: - self.hcloud_image_info = [self.client.images.get_by_name( - self.module.params.get("name") - )] - else: - params = {} - label_selector = self.module.params.get("label_selector") - if label_selector: - params["label_selector"] = label_selector - - image_type = self.module.params.get("type") - if image_type: - params["type"] = image_type - - architecture = self.module.params.get("architecture") - if architecture: - params["architecture"] = architecture - - self.hcloud_image_info = self.client.images.get_all(**params) - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - type={"choices": ["system", "snapshot", "backup"], "default": "system", "type": "str"}, - architecture={"choices": ["x86", "arm"], "type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudImageInfo.define_module() - - is_old_facts = module._name == 'hcloud_image_facts' - if is_old_facts: - module.deprecate("The 'hcloud_image_facts' module has been renamed to 'hcloud_image_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudImageInfo(module) - hcloud.get_images() - result = hcloud.get_result() - - if is_old_facts: - ansible_info = { - 'hcloud_imagen_facts': result['hcloud_image_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_image_info': result['hcloud_image_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_info.py deleted file mode 100644 index 8acd8846a..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_image_info.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_image_info - -short_description: Gather infos about your Hetzner Cloud images. - - -description: - - Gather infos about your Hetzner Cloud images. - - This module was called C(hcloud_location_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_location_facts). - Note that the M(hetzner.hcloud.hcloud_image_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_image_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the image you want to get. - type: int - name: - description: - - The name of the image you want to get. - type: str - label_selector: - description: - - The label selector for the images you want to get. - type: str - type: - description: - - The type for the images you want to get. - default: system - choices: [ system, snapshot, backup ] - type: str - architecture: - description: - - The architecture for the images you want to get. - type: str - choices: [ x86, arm ] -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud image infos - hcloud_image_info: - register: output - -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_image_info: - description: The image infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the image - returned: always - type: int - sample: 1937415 - type: - description: Type of the image - returned: always - type: str - sample: system - status: - description: Status of the image - returned: always - type: str - sample: available - name: - description: Name of the image - returned: always - type: str - sample: ubuntu-18.04 - description: - description: Detail description of the image - returned: always - type: str - sample: Ubuntu 18.04 Standard 64 bit - os_flavor: - description: OS flavor of the image - returned: always - type: str - sample: ubuntu - os_version: - description: OS version of the image - returned: always - type: str - sample: 18.04 - architecture: - description: Image is compatible with this architecture - returned: always - type: str - sample: x86 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudImageInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_image_info") - self.hcloud_image_info = None - - def _prepare_result(self): - tmp = [] - - for image in self.hcloud_image_info: - if image is not None: - tmp.append({ - "id": to_native(image.id), - "status": to_native(image.status), - "type": to_native(image.type), - "name": to_native(image.name), - "description": to_native(image.description), - "os_flavor": to_native(image.os_flavor), - "os_version": to_native(image.os_version), - "architecture": to_native(image.architecture), - "labels": image.labels, - }) - return tmp - - def get_images(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_image_info = [self.client.images.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None and self.module.params.get("architecture") is not None: - self.hcloud_image_info = [self.client.images.get_by_name_and_architecture( - self.module.params.get("name"), - self.module.params.get("architecture") - )] - elif self.module.params.get("name") is not None: - self.hcloud_image_info = [self.client.images.get_by_name( - self.module.params.get("name") - )] - else: - params = {} - label_selector = self.module.params.get("label_selector") - if label_selector: - params["label_selector"] = label_selector - - image_type = self.module.params.get("type") - if image_type: - params["type"] = image_type - - architecture = self.module.params.get("architecture") - if architecture: - params["architecture"] = architecture - - self.hcloud_image_info = self.client.images.get_all(**params) - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - type={"choices": ["system", "snapshot", "backup"], "default": "system", "type": "str"}, - architecture={"choices": ["x86", "arm"], "type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudImageInfo.define_module() - - is_old_facts = module._name == 'hcloud_image_facts' - if is_old_facts: - module.deprecate("The 'hcloud_image_facts' module has been renamed to 'hcloud_image_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudImageInfo(module) - hcloud.get_images() - result = hcloud.get_result() - - if is_old_facts: - ansible_info = { - 'hcloud_imagen_facts': result['hcloud_image_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_image_info': result['hcloud_image_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py deleted file mode 100644 index 9c6c2bbaf..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer.py +++ /dev/null @@ -1,318 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_load_balancer - -short_description: Create and manage cloud Load Balancers on the Hetzner Cloud. - - -description: - - Create, update and manage cloud Load Balancers on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@LKaemmerling) -version_added: 0.1.0 -options: - id: - description: - - The ID of the Hetzner Cloud Load Balancer to manage. - - Only required if no Load Balancer I(name) is given - type: int - name: - description: - - The Name of the Hetzner Cloud Load Balancer to manage. - - Only required if no Load Balancer I(id) is given or a Load Balancer does not exist. - type: str - load_balancer_type: - description: - - The Load Balancer Type of the Hetzner Cloud Load Balancer to manage. - - Required if Load Balancer does not exist. - type: str - location: - description: - - Location of Load Balancer. - - Required if no I(network_zone) is given and Load Balancer does not exist. - type: str - network_zone: - description: - - Network Zone of Load Balancer. - - Required of no I(location) is given and Load Balancer does not exist. - type: str - labels: - description: - - User-defined labels (key-value pairs). - type: dict - disable_public_interface: - description: - - Disables the public interface. - type: bool - default: False - delete_protection: - description: - - Protect the Load Balancer for deletion. - type: bool - state: - description: - - State of the Load Balancer. - default: present - choices: [ absent, present ] - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -requirements: - - hcloud-python >= 1.8.0 -''' - -EXAMPLES = """ -- name: Create a basic Load Balancer - hcloud_load_balancer: - name: my-Load Balancer - load_balancer_type: lb11 - location: fsn1 - state: present - -- name: Ensure the Load Balancer is absent (remove if needed) - hcloud_load_balancer: - name: my-Load Balancer - state: absent - -""" - -RETURN = """ -hcloud_load_balancer: - description: The Load Balancer instance - returned: Always - type: complex - contains: - id: - description: Numeric identifier of the Load Balancer - returned: always - type: int - sample: 1937415 - name: - description: Name of the Load Balancer - returned: always - type: str - sample: my-Load-Balancer - status: - description: Status of the Load Balancer - returned: always - type: str - sample: running - load_balancer_type: - description: Name of the Load Balancer type of the Load Balancer - returned: always - type: str - sample: cx11 - ipv4_address: - description: Public IPv4 address of the Load Balancer - returned: always - type: str - sample: 116.203.104.109 - ipv6_address: - description: Public IPv6 address of the Load Balancer - returned: always - type: str - sample: 2a01:4f8:1c1c:c140::1 - location: - description: Name of the location of the Load Balancer - returned: always - type: str - sample: fsn1 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict - delete_protection: - description: True if Load Balancer is protected for deletion - type: bool - returned: always - sample: false - disable_public_interface: - description: True if Load Balancer public interface is disabled - type: bool - returned: always - sample: false -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudLoadBalancer(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer") - self.hcloud_load_balancer = None - - def _prepare_result(self): - private_ipv4_address = None if len(self.hcloud_load_balancer.private_net) == 0 else to_native( - self.hcloud_load_balancer.private_net[0].ip) - return { - "id": to_native(self.hcloud_load_balancer.id), - "name": to_native(self.hcloud_load_balancer.name), - "ipv4_address": to_native(self.hcloud_load_balancer.public_net.ipv4.ip), - "ipv6_address": to_native(self.hcloud_load_balancer.public_net.ipv6.ip), - "private_ipv4_address": private_ipv4_address, - "load_balancer_type": to_native(self.hcloud_load_balancer.load_balancer_type.name), - "location": to_native(self.hcloud_load_balancer.location.name), - "labels": self.hcloud_load_balancer.labels, - "delete_protection": self.hcloud_load_balancer.protection["delete"], - "disable_public_interface": False if self.hcloud_load_balancer.public_net.enabled else True, - } - - def _get_load_balancer(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_load_balancer = self.client.load_balancers.get_by_id( - self.module.params.get("id") - ) - else: - self.hcloud_load_balancer = self.client.load_balancers.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_load_balancer(self): - - self.module.fail_on_missing_params( - required_params=["name", "load_balancer_type"] - ) - try: - params = { - "name": self.module.params.get("name"), - "load_balancer_type": self.client.load_balancer_types.get_by_name( - self.module.params.get("load_balancer_type") - ), - "labels": self.module.params.get("labels"), - } - - if self.module.params.get("location") is None and self.module.params.get("network_zone") is None: - self.module.fail_json(msg="one of the following is required: location, network_zone") - elif self.module.params.get("location") is not None and self.module.params.get("network_zone") is None: - params["location"] = self.client.locations.get_by_name( - self.module.params.get("location") - ) - elif self.module.params.get("location") is None and self.module.params.get("network_zone") is not None: - params["network_zone"] = self.module.params.get("network_zone") - - if not self.module.check_mode: - resp = self.client.load_balancers.create(**params) - resp.action.wait_until_finished(max_retries=1000) - - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None: - self._get_load_balancer() - self.hcloud_load_balancer.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self._get_load_balancer() - - def _update_load_balancer(self): - try: - labels = self.module.params.get("labels") - if labels is not None and labels != self.hcloud_load_balancer.labels: - if not self.module.check_mode: - self.hcloud_load_balancer.update(labels=labels) - self._mark_as_changed() - - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None and delete_protection != self.hcloud_load_balancer.protection["delete"]: - if not self.module.check_mode: - self.hcloud_load_balancer.change_protection(delete=delete_protection).wait_until_finished() - self._mark_as_changed() - self._get_load_balancer() - - disable_public_interface = self.module.params.get("disable_public_interface") - if disable_public_interface is not None and disable_public_interface != (not self.hcloud_load_balancer.public_net.enabled): - if not self.module.check_mode: - if disable_public_interface is True: - self.hcloud_load_balancer.disable_public_interface().wait_until_finished() - else: - self.hcloud_load_balancer.enable_public_interface().wait_until_finished() - self._mark_as_changed() - - load_balancer_type = self.module.params.get("load_balancer_type") - if load_balancer_type is not None and self.hcloud_load_balancer.load_balancer_type.name != load_balancer_type: - new_load_balancer_type = self.client.load_balancer_types.get_by_name(load_balancer_type) - if not new_load_balancer_type: - self.module.fail_json(msg="unknown load balancer type") - if not self.module.check_mode: - self.hcloud_load_balancer.change_type( - load_balancer_type=new_load_balancer_type, - ).wait_until_finished(max_retries=1000) - - self._mark_as_changed() - self._get_load_balancer() - except Exception as e: - self.module.fail_json(msg=e.message) - - def present_load_balancer(self): - self._get_load_balancer() - if self.hcloud_load_balancer is None: - self._create_load_balancer() - else: - self._update_load_balancer() - - def delete_load_balancer(self): - try: - self._get_load_balancer() - if self.hcloud_load_balancer is not None: - if not self.module.check_mode: - self.client.load_balancers.delete(self.hcloud_load_balancer) - self._mark_as_changed() - self.hcloud_load_balancer = None - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - load_balancer_type={"type": "str"}, - location={"type": "str"}, - network_zone={"type": "str"}, - labels={"type": "dict"}, - delete_protection={"type": "bool"}, - disable_public_interface={"type": "bool", "default": False}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name']], - mutually_exclusive=[["location", "network_zone"]], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudLoadBalancer.define_module() - - hcloud = AnsibleHcloudLoadBalancer(module) - state = module.params.get("state") - if state == "absent": - hcloud.delete_load_balancer() - elif state == "present": - hcloud.present_load_balancer() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_info.py deleted file mode 100644 index 159dad258..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_info.py +++ /dev/null @@ -1,398 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_load_balancer_info - -short_description: Gather infos about your Hetzner Cloud Load Balancers. - - -description: - - Gather infos about your Hetzner Cloud Load Balancers.. - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the Load Balancers you want to get. - type: int - name: - description: - - The name of the Load Balancers you want to get. - type: str - label_selector: - description: - - The label selector for the Load Balancers you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud load_balancer infos - hcloud_load_balancer_info: - register: output - -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_load_balancer_info: - description: The load_balancer infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the Load Balancer - returned: always - type: int - sample: 1937415 - name: - description: Name of the Load Balancer - returned: always - type: str - sample: my-Load-Balancer - status: - description: Status of the Load Balancer - returned: always - type: str - sample: running - load_balancer_type: - description: Name of the Load Balancer type of the Load Balancer - returned: always - type: str - sample: cx11 - ipv4_address: - description: Public IPv4 address of the Load Balancer - returned: always - type: str - sample: 116.203.104.109 - ipv6_address: - description: Public IPv6 address of the Load Balancer - returned: always - type: str - sample: 2a01:4f8:1c1c:c140::1 - location: - description: Name of the location of the Load Balancer - returned: always - type: str - sample: fsn1 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict - delete_protection: - description: True if Load Balancer is protected for deletion - type: bool - returned: always - sample: false - disable_public_interface: - description: True if Load Balancer public interface is disabled - type: bool - returned: always - sample: false - targets: - description: The targets of the Load Balancer - returned: always - type: complex - contains: - type: - description: Type of the Load Balancer Target - type: str - returned: always - sample: server - load_balancer: - description: Name of the Load Balancer - type: str - returned: always - sample: my-LoadBalancer - server: - description: Name of the Server - type: str - returned: if I(type) is server - sample: my-server - label_selector: - description: Label Selector - type: str - returned: if I(type) is label_selector - sample: application=backend - ip: - description: IP of the dedicated server - type: str - returned: if I(type) is ip - sample: 127.0.0.1 - use_private_ip: - description: - - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. - - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.hcloud.hcloud_load_balancer_network) - type: bool - sample: true - returned: always - services: - description: all services from this Load Balancer - returned: Always - type: complex - contains: - listen_port: - description: The port the service listens on, i.e. the port users can connect to. - returned: always - type: int - sample: 443 - protocol: - description: Protocol of the service - returned: always - type: str - sample: http - destination_port: - description: - - The port traffic is forwarded to, i.e. the port the targets are listening and accepting connections on. - returned: always - type: int - sample: 80 - proxyprotocol: - description: - - Enable the PROXY protocol. - returned: always - type: bool - sample: false - http: - description: Configuration for HTTP and HTTPS services - returned: always - type: complex - contains: - cookie_name: - description: Name of the cookie which will be set when you enable sticky sessions - returned: always - type: str - sample: HCLBSTICKY - cookie_lifetime: - description: Lifetime of the cookie which will be set when you enable sticky sessions, in seconds - returned: always - type: int - sample: 3600 - certificates: - description: List of Names or IDs of certificates - returned: always - type: list - elements: str - sticky_sessions: - description: Enable or disable sticky_sessions - returned: always - type: bool - sample: true - redirect_http: - description: Redirect Traffic from Port 80 to Port 443, only available if protocol is https - returned: always - type: bool - sample: false - health_check: - description: Configuration for health checks - returned: always - type: complex - contains: - protocol: - description: Protocol the health checks will be performed over - returned: always - type: str - sample: http - port: - description: Port the health check will be performed on - returned: always - type: int - sample: 80 - interval: - description: Interval of health checks, in seconds - returned: always - type: int - sample: 15 - timeout: - description: Timeout of health checks, in seconds - returned: always - type: int - sample: 10 - retries: - description: Number of retries until a target is marked as unhealthy - returned: always - type: int - sample: 3 - http: - description: Additional Configuration of health checks with protocol http/https - returned: always - type: complex - contains: - domain: - description: Domain we will set within the HTTP HOST header - returned: always - type: str - sample: example.com - path: - description: Path we will try to access - returned: always - type: str - sample: / - response: - description: Response we expect, if response is not within the health check response the target is unhealthy - returned: always - type: str - status_codes: - description: List of HTTP status codes we expect to get when we perform the health check. - returned: always - type: list - elements: str - sample: ["2??","3??"] - tls: - description: Verify the TLS certificate, only available if health check protocol is https - returned: always - type: bool - sample: false -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudLoadBalancerInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_info") - self.hcloud_load_balancer_info = None - - def _prepare_result(self): - tmp = [] - - for load_balancer in self.hcloud_load_balancer_info: - if load_balancer is not None: - services = [self._prepare_service_result(service) for service in load_balancer.services] - targets = [self._prepare_target_result(target) for target in load_balancer.targets] - - private_ipv4_address = None if len(load_balancer.private_net) == 0 else to_native( - load_balancer.private_net[0].ip) - tmp.append({ - "id": to_native(load_balancer.id), - "name": to_native(load_balancer.name), - "ipv4_address": to_native(load_balancer.public_net.ipv4.ip), - "ipv6_address": to_native(load_balancer.public_net.ipv6.ip), - "private_ipv4_address": private_ipv4_address, - "load_balancer_type": to_native(load_balancer.load_balancer_type.name), - "location": to_native(load_balancer.location.name), - "labels": load_balancer.labels, - "delete_protection": load_balancer.protection["delete"], - "disable_public_interface": False if load_balancer.public_net.enabled else True, - "targets": targets, - "services": services - }) - return tmp - - @staticmethod - def _prepare_service_result(service): - http = None - if service.protocol != "tcp": - http = { - "cookie_name": to_native(service.http.cookie_name), - "cookie_lifetime": service.http.cookie_name, - "redirect_http": service.http.redirect_http, - "sticky_sessions": service.http.sticky_sessions, - "certificates": [to_native(certificate.name) for certificate in - service.http.certificates], - } - health_check = { - "protocol": to_native(service.health_check.protocol), - "port": service.health_check.port, - "interval": service.health_check.interval, - "timeout": service.health_check.timeout, - "retries": service.health_check.retries, - } - if service.health_check.protocol != "tcp": - health_check["http"] = { - "domain": to_native(service.health_check.http.domain), - "path": to_native(service.health_check.http.path), - "response": to_native(service.health_check.http.response), - "certificates": [to_native(status_code) for status_code in - service.health_check.http.status_codes], - "tls": service.health_check.http.tls, - } - return { - "protocol": to_native(service.protocol), - "listen_port": service.listen_port, - "destination_port": service.destination_port, - "proxyprotocol": service.proxyprotocol, - "http": http, - "health_check": health_check, - } - - @staticmethod - def _prepare_target_result(target): - result = { - "type": to_native(target.type), - "use_private_ip": target.use_private_ip - } - if target.type == "server": - result["server"] = to_native(target.server.name) - elif target.type == "label_selector": - result["label_selector"] = to_native(target.label_selector.selector) - elif target.type == "ip": - result["ip"] = to_native(target.ip.ip) - return result - - def get_load_balancers(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_load_balancer_info = [self.client.load_balancers.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_load_balancer_info = [self.client.load_balancers.get_by_name( - self.module.params.get("name") - )] - else: - params = {} - label_selector = self.module.params.get("label_selector") - if label_selector: - params["label_selector"] = label_selector - - self.hcloud_load_balancer_info = self.client.load_balancers.get_all(**params) - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudLoadBalancerInfo.define_module() - - hcloud = AnsibleHcloudLoadBalancerInfo(module) - hcloud.get_load_balancers() - result = hcloud.get_result() - - ansible_info = { - 'hcloud_load_balancer_info': result['hcloud_load_balancer_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_network.py deleted file mode 100644 index 63a7c5471..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_network.py +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_load_balancer_network - -short_description: Manage the relationship between Hetzner Cloud Networks and Load Balancers - - -description: - - Create and delete the relationship Hetzner Cloud Networks and Load Balancers - -author: - - Lukas Kaemmerling (@lkaemmerling) -version_added: 0.1.0 -options: - network: - description: - - The name of the Hetzner Cloud Networks. - type: str - required: true - load_balancer: - description: - - The name of the Hetzner Cloud Load Balancer. - type: str - required: true - ip: - description: - - The IP the Load Balancer should have. - type: str - state: - description: - - State of the load_balancer_network. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.8.1 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic Load Balancer network - hcloud_load_balancer_network: - network: my-network - load_balancer: my-LoadBalancer - state: present - -- name: Create a Load Balancer network and specify the ip address - hcloud_load_balancer_network: - network: my-network - load_balancer: my-LoadBalancer - ip: 10.0.0.1 - state: present - -- name: Ensure the Load Balancer network is absent (remove if needed) - hcloud_load_balancer_network: - network: my-network - load_balancer: my-LoadBalancer - state: absent -""" - -RETURN = """ -hcloud_load_balancer_network: - description: The relationship between a Load Balancer and a network - returned: always - type: complex - contains: - network: - description: Name of the Network - type: str - returned: always - sample: my-network - load_balancer: - description: Name of the Load Balancer - type: str - returned: always - sample: my-LoadBalancer - ip: - description: IP of the Load Balancer within the Network ip range - type: str - returned: always - sample: 10.0.0.8 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudLoadBalancerNetwork(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_network") - self.hcloud_network = None - self.hcloud_load_balancer = None - self.hcloud_load_balancer_network = None - - def _prepare_result(self): - return { - "network": to_native(self.hcloud_network.name), - "load_balancer": to_native(self.hcloud_load_balancer.name), - "ip": to_native(self.hcloud_load_balancer_network.ip), - } - - def _get_load_balancer_and_network(self): - try: - network = self.module.params.get("network") - self.hcloud_network = self.client.networks.get_by_name(network) - if not self.hcloud_network: - self.module.fail_json(msg="Network does not exist: %s" % network) - - load_balancer_name = self.module.params.get("load_balancer") - self.hcloud_load_balancer = self.client.load_balancers.get_by_name( - load_balancer_name - ) - if not self.hcloud_load_balancer: - self.module.fail_json(msg="Load balancer does not exist: %s" % load_balancer_name) - - self.hcloud_load_balancer_network = None - except Exception as e: - self.module.fail_json(msg=e.message) - - def _get_load_balancer_network(self): - for privateNet in self.hcloud_load_balancer.private_net: - if privateNet.network.id == self.hcloud_network.id: - self.hcloud_load_balancer_network = privateNet - - def _create_load_balancer_network(self): - params = { - "network": self.hcloud_network - } - - if self.module.params.get("ip") is not None: - params["ip"] = self.module.params.get("ip") - - if not self.module.check_mode: - try: - self.hcloud_load_balancer.attach_to_network(**params).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - - self._mark_as_changed() - self._get_load_balancer_and_network() - self._get_load_balancer_network() - - def present_load_balancer_network(self): - self._get_load_balancer_and_network() - self._get_load_balancer_network() - if self.hcloud_load_balancer_network is None: - self._create_load_balancer_network() - - def delete_load_balancer_network(self): - self._get_load_balancer_and_network() - self._get_load_balancer_network() - if self.hcloud_load_balancer_network is not None and self.hcloud_load_balancer is not None: - if not self.module.check_mode: - try: - self.hcloud_load_balancer.detach_from_network( - self.hcloud_load_balancer_network.network).wait_until_finished() - self._mark_as_changed() - except Exception as e: - self.module.fail_json(msg=e.message) - - self.hcloud_load_balancer_network = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - network={"type": "str", "required": True}, - load_balancer={"type": "str", "required": True}, - ip={"type": "str"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudLoadBalancerNetwork.define_module() - - hcloud = AnsibleHcloudLoadBalancerNetwork(module) - state = module.params["state"] - if state == "absent": - hcloud.delete_load_balancer_network() - elif state == "present": - hcloud.present_load_balancer_network() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_service.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_service.py deleted file mode 100644 index b5edcc6b5..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_service.py +++ /dev/null @@ -1,620 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_load_balancer_service - -short_description: Create and manage the services of cloud Load Balancers on the Hetzner Cloud. - - -description: - - Create, update and manage the services of cloud Load Balancers on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@LKaemmerling) -version_added: 0.1.0 -options: - load_balancer: - description: - - The Name of the Hetzner Cloud Load Balancer the service belongs to - type: str - required: true - listen_port: - description: - - The port the service listens on, i.e. the port users can connect to. - type: int - required: true - destination_port: - description: - - The port traffic is forwarded to, i.e. the port the targets are listening and accepting connections on. - - Required if services does not exist and protocol is tcp. - type: int - protocol: - description: - - Protocol of the service. - - Required if Load Balancer does not exist. - type: str - choices: [ http, https, tcp ] - proxyprotocol: - description: - - Enable the PROXY protocol. - type: bool - default: False - http: - description: - - Configuration for HTTP and HTTPS services - type: dict - suboptions: - cookie_name: - description: - - Name of the cookie which will be set when you enable sticky sessions - type: str - cookie_lifetime: - description: - - Lifetime of the cookie which will be set when you enable sticky sessions, in seconds - type: int - certificates: - description: - - List of Names or IDs of certificates - type: list - elements: str - sticky_sessions: - description: - - Enable or disable sticky_sessions - type: bool - default: False - redirect_http: - description: - - Redirect Traffic from Port 80 to Port 443, only available if protocol is https - type: bool - default: False - health_check: - description: - - Configuration for health checks - type: dict - suboptions: - protocol: - description: - - Protocol the health checks will be performed over - type: str - choices: [ http, https, tcp ] - port: - description: - - Port the health check will be performed on - type: int - interval: - description: - - Interval of health checks, in seconds - type: int - timeout: - description: - - Timeout of health checks, in seconds - type: int - retries: - description: - - Number of retries until a target is marked as unhealthy - type: int - http: - description: - - Additional Configuration of health checks with protocol http/https - type: dict - suboptions: - domain: - description: - - Domain we will set within the HTTP HOST header - type: str - path: - description: - - Path we will try to access - type: str - response: - description: - - Response we expect, if response is not within the health check response the target is unhealthy - type: str - status_codes: - description: - - List of HTTP status codes we expect to get when we perform the health check. - type: list - elements: str - tls: - description: - - Verify the TLS certificate, only available if health check protocol is https - type: bool - default: False - state: - description: - - State of the Load Balancer. - default: present - choices: [ absent, present ] - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -requirements: - - hcloud-python >= 1.8.1 -''' - -EXAMPLES = """ -- name: Create a basic Load Balancer service with Port 80 - hcloud_load_balancer_service: - load_balancer: my-load-balancer - protocol: http - listen_port: 80 - state: present - -- name: Ensure the Load Balancer is absent (remove if needed) - hcloud_load_balancer_service: - load_balancer: my-Load Balancer - protocol: http - listen_port: 80 - state: absent -""" - -RETURN = """ -hcloud_load_balancer_service: - description: The Load Balancer service instance - returned: Always - type: complex - contains: - load_balancer: - description: The name of the Load Balancer where the service belongs to - returned: always - type: str - sample: my-load-balancer - listen_port: - description: The port the service listens on, i.e. the port users can connect to. - returned: always - type: int - sample: 443 - protocol: - description: Protocol of the service - returned: always - type: str - sample: http - destination_port: - description: - - The port traffic is forwarded to, i.e. the port the targets are listening and accepting connections on. - returned: always - type: int - sample: 80 - proxyprotocol: - description: - - Enable the PROXY protocol. - returned: always - type: bool - sample: false - http: - description: Configuration for HTTP and HTTPS services - returned: always - type: complex - contains: - cookie_name: - description: Name of the cookie which will be set when you enable sticky sessions - returned: always - type: str - sample: HCLBSTICKY - cookie_lifetime: - description: Lifetime of the cookie which will be set when you enable sticky sessions, in seconds - returned: always - type: int - sample: 3600 - certificates: - description: List of Names or IDs of certificates - returned: always - type: list - elements: str - sticky_sessions: - description: Enable or disable sticky_sessions - returned: always - type: bool - sample: true - redirect_http: - description: Redirect Traffic from Port 80 to Port 443, only available if protocol is https - returned: always - type: bool - sample: false - health_check: - description: Configuration for health checks - returned: always - type: complex - contains: - protocol: - description: Protocol the health checks will be performed over - returned: always - type: str - sample: http - port: - description: Port the health check will be performed on - returned: always - type: int - sample: 80 - interval: - description: Interval of health checks, in seconds - returned: always - type: int - sample: 15 - timeout: - description: Timeout of health checks, in seconds - returned: always - type: int - sample: 10 - retries: - description: Number of retries until a target is marked as unhealthy - returned: always - type: int - sample: 3 - http: - description: Additional Configuration of health checks with protocol http/https - returned: always - type: complex - contains: - domain: - description: Domain we will set within the HTTP HOST header - returned: always - type: str - sample: example.com - path: - description: Path we will try to access - returned: always - type: str - sample: / - response: - description: Response we expect, if response is not within the health check response the target is unhealthy - returned: always - type: str - status_codes: - description: List of HTTP status codes we expect to get when we perform the health check. - returned: always - type: list - elements: str - sample: ["2??","3??"] - tls: - description: Verify the TLS certificate, only available if health check protocol is https - returned: always - type: bool - sample: false -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - -try: - from hcloud.load_balancers.domain import LoadBalancerService, LoadBalancerServiceHttp, \ - LoadBalancerHealthCheck, LoadBalancerHealtCheckHttp - from hcloud import APIException -except ImportError: - APIException = None - - -class AnsibleHcloudLoadBalancerService(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_service") - self.hcloud_load_balancer = None - self.hcloud_load_balancer_service = None - - def _prepare_result(self): - http = None - if self.hcloud_load_balancer_service.protocol != "tcp": - http = { - "cookie_name": to_native(self.hcloud_load_balancer_service.http.cookie_name), - "cookie_lifetime": self.hcloud_load_balancer_service.http.cookie_name, - "redirect_http": self.hcloud_load_balancer_service.http.redirect_http, - "sticky_sessions": self.hcloud_load_balancer_service.http.sticky_sessions, - "certificates": [to_native(certificate.name) for certificate in - self.hcloud_load_balancer_service.http.certificates], - } - health_check = { - "protocol": to_native(self.hcloud_load_balancer_service.health_check.protocol), - "port": self.hcloud_load_balancer_service.health_check.port, - "interval": self.hcloud_load_balancer_service.health_check.interval, - "timeout": self.hcloud_load_balancer_service.health_check.timeout, - "retries": self.hcloud_load_balancer_service.health_check.retries, - } - if self.hcloud_load_balancer_service.health_check.protocol != "tcp": - health_check["http"] = { - "domain": to_native(self.hcloud_load_balancer_service.health_check.http.domain), - "path": to_native(self.hcloud_load_balancer_service.health_check.http.path), - "response": to_native(self.hcloud_load_balancer_service.health_check.http.response), - "certificates": [to_native(status_code) for status_code in - self.hcloud_load_balancer_service.health_check.http.status_codes], - "tls": self.hcloud_load_balancer_service.health_check.http.tls, - } - return { - "load_balancer": to_native(self.hcloud_load_balancer.name), - "protocol": to_native(self.hcloud_load_balancer_service.protocol), - "listen_port": self.hcloud_load_balancer_service.listen_port, - "destination_port": self.hcloud_load_balancer_service.destination_port, - "proxyprotocol": self.hcloud_load_balancer_service.proxyprotocol, - "http": http, - "health_check": health_check, - } - - def _get_load_balancer(self): - try: - load_balancer_name = self.module.params.get("load_balancer") - self.hcloud_load_balancer = self.client.load_balancers.get_by_name( - load_balancer_name - ) - if not self.hcloud_load_balancer: - self.module.fail_json(msg="Load balancer does not exist: %s" % load_balancer_name) - - self._get_load_balancer_service() - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_load_balancer_service(self): - - self.module.fail_on_missing_params( - required_params=["protocol"] - ) - if self.module.params.get("protocol") == "tcp": - self.module.fail_on_missing_params( - required_params=["destination_port"] - ) - - params = { - "protocol": self.module.params.get("protocol"), - "listen_port": self.module.params.get("listen_port"), - "proxyprotocol": self.module.params.get("proxyprotocol") - } - - if self.module.params.get("destination_port"): - params["destination_port"] = self.module.params.get("destination_port") - - if self.module.params.get("http"): - params["http"] = self.__get_service_http(http_arg=self.module.params.get("http")) - - if self.module.params.get("health_check"): - params["health_check"] = self.__get_service_health_checks( - health_check=self.module.params.get("health_check")) - - if not self.module.check_mode: - try: - self.hcloud_load_balancer.add_service(LoadBalancerService(**params)).wait_until_finished( - max_retries=1000) - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self._get_load_balancer() - self._get_load_balancer_service() - - def __get_service_http(self, http_arg): - service_http = LoadBalancerServiceHttp(certificates=[]) - if http_arg.get("cookie_name") is not None: - service_http.cookie_name = http_arg.get("cookie_name") - if http_arg.get("cookie_lifetime") is not None: - service_http.cookie_lifetime = http_arg.get("cookie_lifetime") - if http_arg.get("sticky_sessions") is not None: - service_http.sticky_sessions = http_arg.get("sticky_sessions") - if http_arg.get("redirect_http") is not None: - service_http.redirect_http = http_arg.get("redirect_http") - if http_arg.get("certificates") is not None: - certificates = http_arg.get("certificates") - if certificates is not None: - for certificate in certificates: - hcloud_cert = None - try: - try: - hcloud_cert = self.client.certificates.get_by_name( - certificate - ) - except Exception: - hcloud_cert = self.client.certificates.get_by_id( - certificate - ) - except Exception as e: - self.module.fail_json(msg=e.message) - service_http.certificates.append(hcloud_cert) - - return service_http - - def __get_service_health_checks(self, health_check): - service_health_check = LoadBalancerHealthCheck() - if health_check.get("protocol") is not None: - service_health_check.protocol = health_check.get("protocol") - if health_check.get("port") is not None: - service_health_check.port = health_check.get("port") - if health_check.get("interval") is not None: - service_health_check.interval = health_check.get("interval") - if health_check.get("timeout") is not None: - service_health_check.timeout = health_check.get("timeout") - if health_check.get("retries") is not None: - service_health_check.retries = health_check.get("retries") - if health_check.get("http") is not None: - health_check_http = health_check.get("http") - service_health_check.http = LoadBalancerHealtCheckHttp() - if health_check_http.get("domain") is not None: - service_health_check.http.domain = health_check_http.get("domain") - if health_check_http.get("path") is not None: - service_health_check.http.path = health_check_http.get("path") - if health_check_http.get("response") is not None: - service_health_check.http.response = health_check_http.get("response") - if health_check_http.get("status_codes") is not None: - service_health_check.http.status_codes = health_check_http.get("status_codes") - if health_check_http.get("tls") is not None: - service_health_check.http.tls = health_check_http.get("tls") - - return service_health_check - - def _update_load_balancer_service(self): - changed = False - try: - params = { - "listen_port": self.module.params.get("listen_port"), - } - - if self.module.params.get("destination_port") is not None: - if self.hcloud_load_balancer_service.destination_port != self.module.params.get("destination_port"): - params["destination_port"] = self.module.params.get("destination_port") - changed = True - - if self.module.params.get("protocol") is not None: - if self.hcloud_load_balancer_service.protocol != self.module.params.get("protocol"): - params["protocol"] = self.module.params.get("protocol") - changed = True - - if self.module.params.get("proxyprotocol") is not None: - if self.hcloud_load_balancer_service.proxyprotocol != self.module.params.get("proxyprotocol"): - params["proxyprotocol"] = self.module.params.get("proxyprotocol") - changed = True - - if self.module.params.get("http") is not None: - params["http"] = self.__get_service_http(http_arg=self.module.params.get("http")) - changed = True - - if self.module.params.get("health_check") is not None: - params["health_check"] = self.__get_service_health_checks( - health_check=self.module.params.get("health_check")) - changed = True - - if not self.module.check_mode: - self.hcloud_load_balancer.update_service(LoadBalancerService(**params)).wait_until_finished( - max_retries=1000) - except Exception as e: - self.module.fail_json(msg=e.message) - self._get_load_balancer() - - if changed: - self._mark_as_changed() - - def _get_load_balancer_service(self): - for service in self.hcloud_load_balancer.services: - if self.module.params.get("listen_port") == service.listen_port: - self.hcloud_load_balancer_service = service - - def present_load_balancer_service(self): - self._get_load_balancer() - if self.hcloud_load_balancer_service is None: - self._create_load_balancer_service() - else: - self._update_load_balancer_service() - - def delete_load_balancer_service(self): - try: - self._get_load_balancer() - if self.hcloud_load_balancer_service is not None: - if not self.module.check_mode: - try: - self.hcloud_load_balancer.delete_service(self.hcloud_load_balancer_service).wait_until_finished( - max_retries=1000) - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self.hcloud_load_balancer_service = None - except APIException as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - load_balancer={"type": "str", "required": True}, - listen_port={"type": "int", "required": True}, - destination_port={"type": "int"}, - protocol={ - "type": "str", - "choices": ["http", "https", "tcp"], - }, - proxyprotocol={"type": "bool", "default": False}, - http={ - "type": "dict", - "options": dict( - cookie_name={ - "type": "str" - }, - cookie_lifetime={ - "type": "int" - }, - sticky_sessions={ - "type": "bool", - "default": False - }, - redirect_http={ - "type": "bool", - "default": False - }, - certificates={ - "type": "list", - "elements": "str" - }, - - ) - }, - health_check={ - "type": "dict", - "options": dict( - protocol={ - "type": "str", - "choices": ["http", "https", "tcp"], - }, - port={ - "type": "int" - }, - interval={ - "type": "int" - }, - timeout={ - "type": "int" - }, - retries={ - "type": "int" - }, - http={ - "type": "dict", - "options": dict( - domain={ - "type": "str" - }, - path={ - "type": "str" - }, - response={ - "type": "str" - }, - status_codes={ - "type": "list", - "elements": "str" - }, - tls={ - "type": "bool", - "default": False - }, - ) - } - ) - - }, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudLoadBalancerService.define_module() - - hcloud = AnsibleHcloudLoadBalancerService(module) - state = module.params.get("state") - if state == "absent": - hcloud.delete_load_balancer_service() - elif state == "present": - hcloud.present_load_balancer_service() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_target.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_target.py deleted file mode 100644 index 760884466..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_target.py +++ /dev/null @@ -1,321 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_load_balancer_target - -short_description: Manage Hetzner Cloud Load Balancer targets - - -description: - - Create and delete Hetzner Cloud Load Balancer targets - -author: - - Lukas Kaemmerling (@lkaemmerling) -version_added: 0.1.0 -options: - type: - description: - - The type of the target. - type: str - choices: [ server, label_selector, ip ] - required: true - load_balancer: - description: - - The name of the Hetzner Cloud Load Balancer. - type: str - required: true - server: - description: - - The name of the Hetzner Cloud Server. - - Required if I(type) is server - type: str - label_selector: - description: - - A Label Selector that will be used to determine the targets dynamically - - Required if I(type) is label_selector - type: str - ip: - description: - - An IP from a Hetzner Dedicated Server, needs to belongs to the same user as the project. - - Required if I(type) is ip - type: str - use_private_ip: - description: - - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. - - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.hcloud.hcloud_load_balancer_network) - type: bool - default: False - state: - description: - - State of the load_balancer_network. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.8.1 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a server Load Balancer target - hetzner.hcloud.hcloud_load_balancer_target: - type: server - load_balancer: my-LoadBalancer - server: my-server - state: present - -- name: Create a label_selector Load Balancer target - hetzner.hcloud.hcloud_load_balancer_target: - type: label_selector - load_balancer: my-LoadBalancer - label_selector: application=backend - state: present - -- name: Create an IP Load Balancer target - hetzner.hcloud.hcloud_load_balancer_target: - type: ip - load_balancer: my-LoadBalancer - ip: 127.0.0.1 - state: present - -- name: Ensure the Load Balancer target is absent (remove if needed) - hetzner.hcloud.hcloud_load_balancer_target: - type: server - load_balancer: my-LoadBalancer - server: my-server - state: absent -""" - -RETURN = """ -hcloud_load_balancer_target: - description: The relationship between a Load Balancer and a network - returned: always - type: complex - contains: - type: - description: Type of the Load Balancer Target - type: str - returned: always - sample: server - load_balancer: - description: Name of the Load Balancer - type: str - returned: always - sample: my-LoadBalancer - server: - description: Name of the Server - type: str - returned: if I(type) is server - sample: my-server - label_selector: - description: Label Selector - type: str - returned: if I(type) is label_selector - sample: application=backend - ip: - description: IP of the dedicated server - type: str - returned: if I(type) is ip - sample: 127.0.0.1 - use_private_ip: - description: - - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. - - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.hcloud.hcloud_load_balancer_network) - type: bool - sample: true - returned: always -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - -try: - from hcloud.load_balancers.domain import LoadBalancerTarget, LoadBalancerTargetLabelSelector, LoadBalancerTargetIP -except ImportError: - LoadBalancerTarget = None - LoadBalancerTargetLabelSelector = None - LoadBalancerTargetIP = None - - -class AnsibleHcloudLoadBalancerTarget(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_target") - self.hcloud_load_balancer = None - self.hcloud_load_balancer_target = None - self.hcloud_server = None - - def _prepare_result(self): - result = { - "type": to_native(self.hcloud_load_balancer_target.type), - "load_balancer": to_native(self.hcloud_load_balancer.name), - "use_private_ip": self.hcloud_load_balancer_target.use_private_ip - } - - if self.hcloud_load_balancer_target.type == "server": - result["server"] = to_native(self.hcloud_load_balancer_target.server.name) - elif self.hcloud_load_balancer_target.type == "label_selector": - result["label_selector"] = to_native(self.hcloud_load_balancer_target.label_selector.selector) - elif self.hcloud_load_balancer_target.type == "ip": - result["ip"] = to_native(self.hcloud_load_balancer_target.ip.ip) - return result - - def _get_load_balancer_and_target(self): - try: - load_balancer_name = self.module.params.get("load_balancer") - self.hcloud_load_balancer = self.client.load_balancers.get_by_name( - load_balancer_name - ) - if not self.hcloud_load_balancer: - self.module.fail_json(msg="Load balancer does not exist: %s" % load_balancer_name) - - if self.module.params.get("type") == "server": - server_name = self.module.params.get("server") - self.hcloud_server = self.client.servers.get_by_name(server_name) - if not self.hcloud_server: - self.module.fail_json(msg="Server not found: %s" % server_name) - - self.hcloud_load_balancer_target = None - except Exception as e: - self.module.fail_json(msg=e.message) - - def _get_load_balancer_target(self): - for target in self.hcloud_load_balancer.targets: - if self.module.params.get("type") == "server" and target.type == "server": - if target.server.id == self.hcloud_server.id: - self.hcloud_load_balancer_target = target - elif self.module.params.get("type") == "label_selector" and target.type == "label_selector": - if target.label_selector.selector == self.module.params.get("label_selector"): - self.hcloud_load_balancer_target = target - elif self.module.params.get("type") == "ip" and target.type == "ip": - if target.ip.ip == self.module.params.get("ip"): - self.hcloud_load_balancer_target = target - - def _create_load_balancer_target(self): - params = { - "target": None - } - - if self.module.params.get("type") == "server": - self.module.fail_on_missing_params( - required_params=["server"] - ) - params["target"] = LoadBalancerTarget(type=self.module.params.get("type"), server=self.hcloud_server, - use_private_ip=self.module.params.get("use_private_ip")) - elif self.module.params.get("type") == "label_selector": - self.module.fail_on_missing_params( - required_params=["label_selector"] - ) - params["target"] = LoadBalancerTarget(type=self.module.params.get("type"), - label_selector=LoadBalancerTargetLabelSelector( - selector=self.module.params.get("label_selector")), - use_private_ip=self.module.params.get("use_private_ip")) - elif self.module.params.get("type") == "ip": - self.module.fail_on_missing_params( - required_params=["ip"] - ) - params["target"] = LoadBalancerTarget(type=self.module.params.get("type"), - ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), - use_private_ip=False) - - if not self.module.check_mode: - try: - self.hcloud_load_balancer.add_target(**params).wait_until_finished() - except Exception as e: - if e.code == "locked" or e.code == "conflict": - self._create_load_balancer_target() - else: - self.module.fail_json(msg=e.message) - - self._mark_as_changed() - self._get_load_balancer_and_target() - self._get_load_balancer_target() - - def present_load_balancer_target(self): - self._get_load_balancer_and_target() - self._get_load_balancer_target() - if self.hcloud_load_balancer_target is None: - self._create_load_balancer_target() - - def delete_load_balancer_target(self): - self._get_load_balancer_and_target() - self._get_load_balancer_target() - if self.hcloud_load_balancer_target is not None and self.hcloud_load_balancer is not None: - if not self.module.check_mode: - target = None - if self.module.params.get("type") == "server": - self.module.fail_on_missing_params( - required_params=["server"] - ) - target = LoadBalancerTarget(type=self.module.params.get("type"), - server=self.hcloud_server) - elif self.module.params.get("type") == "label_selector": - self.module.fail_on_missing_params( - required_params=["label_selector"] - ) - target = LoadBalancerTarget(type=self.module.params.get("type"), - label_selector=LoadBalancerTargetLabelSelector( - selector=self.module.params.get("label_selector")), - use_private_ip=self.module.params.get("use_private_ip")) - elif self.module.params.get("type") == "ip": - self.module.fail_on_missing_params( - required_params=["ip"] - ) - target = LoadBalancerTarget(type=self.module.params.get("type"), - ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), - use_private_ip=False) - try: - self.hcloud_load_balancer.remove_target(target).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self.hcloud_load_balancer_target = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - type={"type": "str", "required": True, "choices": ["server", "label_selector", "ip"]}, - load_balancer={"type": "str", "required": True}, - server={"type": "str"}, - label_selector={"type": "str"}, - ip={"type": "str"}, - use_private_ip={"type": "bool", "default": False}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudLoadBalancerTarget.define_module() - - hcloud = AnsibleHcloudLoadBalancerTarget(module) - state = module.params["state"] - if state == "absent": - hcloud.delete_load_balancer_target() - elif state == "present": - hcloud.present_load_balancer_target() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_type_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_type_info.py deleted file mode 100644 index a481ea9c9..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_load_balancer_type_info.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_load_balancer_type_info - -short_description: Gather infos about the Hetzner Cloud Load Balancer types. - - -description: - - Gather infos about your Hetzner Cloud Load Balancer types. - -author: - - Lukas Kaemmerling (@LKaemmerling) -version_added: 0.1.0 -options: - id: - description: - - The ID of the Load Balancer type you want to get. - type: int - name: - description: - - The name of the Load Balancer type you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud Load Balancer type infos - hcloud_load_balancer_type_info: - register: output - -- name: Print the gathered infos - debug: - var: output.hcloud_load_balancer_type_info -""" - -RETURN = """ -hcloud_load_balancer_type_info: - description: The Load Balancer type infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the Load Balancer type - returned: always - type: int - sample: 1937415 - name: - description: Name of the Load Balancer type - returned: always - type: str - sample: lb11 - description: - description: Description of the Load Balancer type - returned: always - type: str - sample: LB11 - max_connections: - description: Number of maximum simultaneous open connections - returned: always - type: int - sample: 1 - max_services: - description: Number of services a Load Balancer of this type can have - returned: always - type: int - sample: 1 - max_targets: - description: Number of targets a single Load Balancer can have - returned: always - type: int - sample: 25 - max_assigned_certificates: - description: Number of SSL Certificates that can be assigned to a single Load Balancer - returned: always - type: int - sample: 5 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudLoadBalancerTypeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_load_balancer_type_info") - self.hcloud_load_balancer_type_info = None - - def _prepare_result(self): - tmp = [] - - for load_balancer_type in self.hcloud_load_balancer_type_info: - if load_balancer_type is not None: - tmp.append({ - "id": to_native(load_balancer_type.id), - "name": to_native(load_balancer_type.name), - "description": to_native(load_balancer_type.description), - "max_connections": load_balancer_type.max_connections, - "max_services": load_balancer_type.max_services, - "max_targets": load_balancer_type.max_targets, - "max_assigned_certificates": load_balancer_type.max_assigned_certificates - }) - return tmp - - def get_load_balancer_types(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_load_balancer_type_info = [self.client.load_balancer_types.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_load_balancer_type_info = [self.client.load_balancer_types.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_load_balancer_type_info = self.client.load_balancer_types.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudLoadBalancerTypeInfo.define_module() - - hcloud = AnsibleHcloudLoadBalancerTypeInfo(module) - hcloud.get_load_balancer_types() - result = hcloud.get_result() - ansible_info = { - 'hcloud_load_balancer_type_info': result['hcloud_load_balancer_type_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_facts.py deleted file mode 100644 index 623c6ab68..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_facts.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_location_info - -short_description: Gather infos about your Hetzner Cloud locations. - - -description: - - Gather infos about your Hetzner Cloud locations. - - This module was called C(hcloud_location_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_location_facts). - Note that the M(hetzner.hcloud.hcloud_location_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_location_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the location you want to get. - type: int - name: - description: - - The name of the location you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud location infos - hcloud_location_info: - register: output - -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_location_info: - description: The location infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the location - returned: always - type: int - sample: 1937415 - name: - description: Name of the location - returned: always - type: str - sample: fsn1 - description: - description: Detail description of the location - returned: always - type: str - sample: Falkenstein DC Park 1 - country: - description: Country code of the location - returned: always - type: str - sample: DE - city: - description: City of the location - returned: always - type: str - sample: Falkenstein -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudLocationInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_location_info") - self.hcloud_location_info = None - - def _prepare_result(self): - tmp = [] - - for location in self.hcloud_location_info: - if location is not None: - tmp.append({ - "id": to_native(location.id), - "name": to_native(location.name), - "description": to_native(location.description), - "city": to_native(location.city), - "country": to_native(location.country) - }) - return tmp - - def get_locations(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_location_info = [self.client.locations.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_location_info = [self.client.locations.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_location_info = self.client.locations.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudLocationInfo.define_module() - - is_old_facts = module._name == 'hcloud_location_facts' - if is_old_facts: - module.deprecate("The 'hcloud_location_info' module has been renamed to 'hcloud_location_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudLocationInfo(module) - hcloud.get_locations() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_location_facts': result['hcloud_location_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_location_info': result['hcloud_location_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_info.py deleted file mode 100644 index 623c6ab68..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_location_info.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_location_info - -short_description: Gather infos about your Hetzner Cloud locations. - - -description: - - Gather infos about your Hetzner Cloud locations. - - This module was called C(hcloud_location_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_location_facts). - Note that the M(hetzner.hcloud.hcloud_location_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_location_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the location you want to get. - type: int - name: - description: - - The name of the location you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud location infos - hcloud_location_info: - register: output - -- name: Print the gathered infos - debug: - var: output -""" - -RETURN = """ -hcloud_location_info: - description: The location infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the location - returned: always - type: int - sample: 1937415 - name: - description: Name of the location - returned: always - type: str - sample: fsn1 - description: - description: Detail description of the location - returned: always - type: str - sample: Falkenstein DC Park 1 - country: - description: Country code of the location - returned: always - type: str - sample: DE - city: - description: City of the location - returned: always - type: str - sample: Falkenstein -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudLocationInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_location_info") - self.hcloud_location_info = None - - def _prepare_result(self): - tmp = [] - - for location in self.hcloud_location_info: - if location is not None: - tmp.append({ - "id": to_native(location.id), - "name": to_native(location.name), - "description": to_native(location.description), - "city": to_native(location.city), - "country": to_native(location.country) - }) - return tmp - - def get_locations(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_location_info = [self.client.locations.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_location_info = [self.client.locations.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_location_info = self.client.locations.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudLocationInfo.define_module() - - is_old_facts = module._name == 'hcloud_location_facts' - if is_old_facts: - module.deprecate("The 'hcloud_location_info' module has been renamed to 'hcloud_location_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudLocationInfo(module) - hcloud.get_locations() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_location_facts': result['hcloud_location_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_location_info': result['hcloud_location_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network.py deleted file mode 100644 index 9c005d29f..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_network - -short_description: Create and manage cloud Networks on the Hetzner Cloud. - - -description: - - Create, update and manage cloud Networks on the Hetzner Cloud. - - You need at least hcloud-python 1.3.0. - -author: - - Lukas Kaemmerling (@lkaemmerling) - -options: - id: - description: - - The ID of the Hetzner Cloud Networks to manage. - - Only required if no Network I(name) is given. - type: int - name: - description: - - The Name of the Hetzner Cloud Network to manage. - - Only required if no Network I(id) is given or a Network does not exist. - type: str - ip_range: - description: - - IP range of the Network. - - Required if Network does not exist. - type: str - labels: - description: - - User-defined labels (key-value pairs). - type: dict - delete_protection: - description: - - Protect the Network for deletion. - type: bool - state: - description: - - State of the Network. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.3.0 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic network - hcloud_network: - name: my-network - ip_range: 10.0.0.0/8 - state: present - -- name: Ensure the Network is absent (remove if needed) - hcloud_network: - name: my-network - state: absent -""" - -RETURN = """ -hcloud_network: - description: The Network - returned: always - type: complex - contains: - id: - description: ID of the Network - type: int - returned: always - sample: 12345 - name: - description: Name of the Network - type: str - returned: always - sample: my-volume - ip_range: - description: IP range of the Network - type: str - returned: always - sample: 10.0.0.0/8 - delete_protection: - description: True if Network is protected for deletion - type: bool - returned: always - sample: false - version_added: "0.1.0" - labels: - description: User-defined labels (key-value pairs) - type: dict - returned: always - sample: - key: value - mylabel: 123 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudNetwork(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_network") - self.hcloud_network = None - - def _prepare_result(self): - return { - "id": to_native(self.hcloud_network.id), - "name": to_native(self.hcloud_network.name), - "ip_range": to_native(self.hcloud_network.ip_range), - "delete_protection": self.hcloud_network.protection["delete"], - "labels": self.hcloud_network.labels, - } - - def _get_network(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_network = self.client.networks.get_by_id( - self.module.params.get("id") - ) - else: - self.hcloud_network = self.client.networks.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_network(self): - - self.module.fail_on_missing_params( - required_params=["name", "ip_range"] - ) - params = { - "name": self.module.params.get("name"), - "ip_range": self.module.params.get("ip_range"), - "labels": self.module.params.get("labels"), - } - try: - if not self.module.check_mode: - self.client.networks.create(**params) - - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None: - self._get_network() - self.hcloud_network.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self._get_network() - - def _update_network(self): - try: - labels = self.module.params.get("labels") - if labels is not None and labels != self.hcloud_network.labels: - if not self.module.check_mode: - self.hcloud_network.update(labels=labels) - self._mark_as_changed() - - ip_range = self.module.params.get("ip_range") - if ip_range is not None and ip_range != self.hcloud_network.ip_range: - if not self.module.check_mode: - self.hcloud_network.change_ip_range(ip_range=ip_range).wait_until_finished() - self._mark_as_changed() - - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None and delete_protection != self.hcloud_network.protection["delete"]: - if not self.module.check_mode: - self.hcloud_network.change_protection(delete=delete_protection).wait_until_finished() - self._mark_as_changed() - except Exception as e: - self.module.fail_json(msg=e.message) - self._get_network() - - def present_network(self): - self._get_network() - if self.hcloud_network is None: - self._create_network() - else: - self._update_network() - - def delete_network(self): - try: - self._get_network() - if self.hcloud_network is not None: - if not self.module.check_mode: - self.client.networks.delete(self.hcloud_network) - self._mark_as_changed() - except Exception as e: - self.module.fail_json(msg=e.message) - self.hcloud_network = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - ip_range={"type": "str"}, - labels={"type": "dict"}, - delete_protection={"type": "bool"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name']], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudNetwork.define_module() - - hcloud = AnsibleHcloudNetwork(module) - state = module.params["state"] - if state == "absent": - hcloud.delete_network() - elif state == "present": - hcloud.present_network() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network_info.py deleted file mode 100644 index 382e447aa..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_network_info.py +++ /dev/null @@ -1,293 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_network_info - -short_description: Gather info about your Hetzner Cloud networks. - - -description: - - Gather info about your Hetzner Cloud networks. - -author: - - Christopher Schmitt (@cschmitt-hcloud) - -options: - id: - description: - - The ID of the network you want to get. - type: int - name: - description: - - The name of the network you want to get. - type: str - label_selector: - description: - - The label selector for the network you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud network info - local_action: - module: hcloud_network_info - -- name: Print the gathered info - debug: - var: hcloud_network_info -""" - -RETURN = """ -hcloud_network_info: - description: The network info as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the network - returned: always - type: int - sample: 1937415 - name: - description: Name of the network - returned: always - type: str - sample: awesome-network - ip_range: - description: IP range of the network - returned: always - type: str - sample: 10.0.0.0/16 - subnetworks: - description: Subnetworks belonging to the network - returned: always - type: complex - contains: - type: - description: Type of the subnetwork. - returned: always - type: str - sample: cloud - network_zone: - description: Network of the subnetwork. - returned: always - type: str - sample: eu-central - ip_range: - description: IP range of the subnetwork - returned: always - type: str - sample: 10.0.0.0/24 - gateway: - description: Gateway of this subnetwork - returned: always - type: str - sample: 10.0.0.1 - routes: - description: Routes belonging to the network - returned: always - type: complex - contains: - ip_range: - description: Destination network or host of this route. - returned: always - type: str - sample: 10.0.0.0/16 - gateway: - description: Gateway of this route - returned: always - type: str - sample: 10.0.0.1 - servers: - description: Servers attached to the network - returned: always - type: complex - contains: - id: - description: Numeric identifier of the server - returned: always - type: int - sample: 1937415 - name: - description: Name of the server - returned: always - type: str - sample: my-server - status: - description: Status of the server - returned: always - type: str - sample: running - server_type: - description: Name of the server type of the server - returned: always - type: str - sample: cx11 - ipv4_address: - description: Public IPv4 address of the server, None if not existing - returned: always - type: str - sample: 116.203.104.109 - ipv6: - description: IPv6 network of the server, None if not existing - returned: always - type: str - sample: 2a01:4f8:1c1c:c140::/64 - location: - description: Name of the location of the server - returned: always - type: str - sample: fsn1 - datacenter: - description: Name of the datacenter of the server - returned: always - type: str - sample: fsn1-dc14 - rescue_enabled: - description: True if rescue mode is enabled, Server will then boot into rescue system on next reboot - returned: always - type: bool - sample: false - backup_window: - description: Time window (UTC) in which the backup will run, or null if the backups are not enabled - returned: always - type: bool - sample: 22-02 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict - delete_protection: - description: True if the network is protected for deletion - returned: always - type: bool - version_added: "0.1.0" - labels: - description: Labels of the network - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudNetworkInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_network_info") - self.hcloud_network_info = None - - def _prepare_result(self): - tmp = [] - - for network in self.hcloud_network_info: - if network is not None: - subnets = [] - for subnet in network.subnets: - prepared_subnet = { - "type": subnet.type, - "ip_range": subnet.ip_range, - "network_zone": subnet.network_zone, - "gateway": subnet.gateway, - } - subnets.append(prepared_subnet) - routes = [] - for route in network.routes: - prepared_route = { - "destination": route.destination, - "gateway": route.gateway - } - routes.append(prepared_route) - - servers = [] - for server in network.servers: - image = None if server.image is None else to_native(server.image.name) - ipv4_address = None if server.public_net.ipv4 is None else to_native(server.public_net.ipv4.ip) - ipv6 = None if server.public_net.ipv6 is None else to_native(server.public_net.ipv6.ip) - prepared_server = { - "id": to_native(server.id), - "name": to_native(server.name), - "ipv4_address": ipv4_address, - "ipv6": ipv6, - "image": image, - "server_type": to_native(server.server_type.name), - "datacenter": to_native(server.datacenter.name), - "location": to_native(server.datacenter.location.name), - "rescue_enabled": server.rescue_enabled, - "backup_window": to_native(server.backup_window), - "labels": server.labels, - "status": to_native(server.status), - } - servers.append(prepared_server) - - tmp.append({ - "id": to_native(network.id), - "name": to_native(network.name), - "ip_range": to_native(network.ip_range), - "subnetworks": subnets, - "routes": routes, - "servers": servers, - "labels": network.labels, - "delete_protection": network.protection["delete"], - }) - return tmp - - def get_networks(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_network_info = [self.client.networks.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_network_info = [self.client.networks.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_network_info = self.client.networks.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_network_info = self.client.networks.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudNetworkInfo.define_module() - - hcloud = AnsibleHcloudNetworkInfo(module) - hcloud.get_networks() - result = hcloud.get_result() - info = { - 'hcloud_network_info': result['hcloud_network_info'] - } - module.exit_json(**info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_placement_group.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_placement_group.py deleted file mode 100644 index 522bb679d..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_placement_group.py +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = """ ---- -module: hcloud_placement_group - -short_description: Create and manage placement groups on the Hetzner Cloud. - - -description: - - Create, update and manage placement groups on the Hetzner Cloud. - -author: - - Adrian Huber (@Adi146) - -options: - id: - description: - - The ID of the Hetzner Cloud placement group to manage. - - Only required if no placement group I(name) is given - type: int - name: - description: - - The Name of the Hetzner Cloud placement group to manage. - - Only required if no placement group I(id) is given, or a placement group does not exist. - type: str - labels: - description: - - User-defined labels (key-value pairs) - type: dict - type: - description: - - The Type of the Hetzner Cloud placement group. - type: str - state: - description: - - State of the placement group. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.15.0 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud -""" - -EXAMPLES = """ -- name: Create a basic placement group - hcloud_placement_group: - name: my-placement-group - state: present - type: spread - -- name: Create a placement group with labels - hcloud_placement_group: - name: my-placement-group - type: spread - labels: - key: value - mylabel: 123 - state: present - -- name: Ensure the placement group is absent (remove if needed) - hcloud_placement_group: - name: my-placement-group - state: absent -""" - -RETURN = """ -hcloud_placement_group: - description: The placement group instance - returned: Always - type: complex - contains: - id: - description: Numeric identifier of the placement group - returned: always - type: int - sample: 1937415 - name: - description: Name of the placement group - returned: always - type: str - sample: my placement group - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict - type: - description: Type of the placement group - returned: always - type: str - sample: spread - servers: - description: Server IDs of the placement group - returned: always - type: list - elements: int - sample: - - 4711 - - 4712 -""" - -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - - -class AnsibleHcloudPlacementGroup(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_placement_group") - self.hcloud_placement_group = None - - def _prepare_result(self): - return { - "id": to_native(self.hcloud_placement_group.id), - "name": to_native(self.hcloud_placement_group.name), - "labels": self.hcloud_placement_group.labels, - "type": to_native(self.hcloud_placement_group.type), - "servers": self.hcloud_placement_group.servers, - } - - def _get_placement_group(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_placement_group = self.client.placement_groups.get_by_id( - self.module.params.get("id") - ) - elif self.module.params.get("name") is not None: - self.hcloud_placement_group = self.client.placement_groups.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_placement_group(self): - self.module.fail_on_missing_params( - required_params=["name"] - ) - params = { - "name": self.module.params.get("name"), - "type": self.module.params.get("type"), - "labels": self.module.params.get("labels"), - } - if not self.module.check_mode: - try: - self.client.placement_groups.create(**params) - except Exception as e: - self.module.fail_json(msg=e.message, **params) - self._mark_as_changed() - self._get_placement_group() - - def _update_placement_group(self): - name = self.module.params.get("name") - if name is not None and self.hcloud_placement_group.name != name: - self.module.fail_on_missing_params( - required_params=["id"] - ) - if not self.module.check_mode: - self.hcloud_placement_group.update(name=name) - self._mark_as_changed() - - labels = self.module.params.get("labels") - if labels is not None and self.hcloud_placement_group.labels != labels: - if not self.module.check_mode: - self.hcloud_placement_group.update(labels=labels) - self._mark_as_changed() - - self._get_placement_group() - - def present_placement_group(self): - self._get_placement_group() - if self.hcloud_placement_group is None: - self._create_placement_group() - else: - self._update_placement_group() - - def delete_placement_group(self): - self._get_placement_group() - if self.hcloud_placement_group is not None: - if not self.module.check_mode: - self.client.placement_groups.delete(self.hcloud_placement_group) - self._mark_as_changed() - self.hcloud_placement_group = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - labels={"type": "dict"}, - type={"type": "str"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name']], - required_if=[['state', 'present', ['name']]], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudPlacementGroup.define_module() - - hcloud = AnsibleHcloudPlacementGroup(module) - state = module.params.get("state") - if state == "absent": - hcloud.delete_placement_group() - elif state == "present": - hcloud.present_placement_group() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_primary_ip.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_primary_ip.py deleted file mode 100644 index c192d5fec..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_primary_ip.py +++ /dev/null @@ -1,271 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2022, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_primary_ip - -short_description: Create and manage cloud Primary IPs on the Hetzner Cloud. - - -description: - - Create, update and manage cloud Primary IPs on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@lkaemmerling) -version_added: 1.8.0 -options: - id: - description: - - The ID of the Hetzner Cloud Primary IPs to manage. - - Only required if no Primary IP I(name) is given. - type: int - name: - description: - - The Name of the Hetzner Cloud Primary IPs to manage. - - Only required if no Primary IP I(id) is given or a Primary IP does not exist. - type: str - datacenter: - description: - - Home Location of the Hetzner Cloud Primary IP. - - Required if no I(server) is given and Primary IP does not exist. - type: str - type: - description: - - Type of the Primary IP. - - Required if Primary IP does not exist - choices: [ ipv4, ipv6 ] - type: str - auto_delete: - description: - - Delete this Primary IP when the resource it is assigned to is deleted - type: bool - default: no - delete_protection: - description: - - Protect the Primary IP for deletion. - type: bool - labels: - description: - - User-defined labels (key-value pairs). - type: dict - state: - description: - - State of the Primary IP. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.9.0 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic IPv4 Primary IP - hcloud_primary_ip: - name: my-primary-ip - datacenter: fsn1-dc14 - type: ipv4 - state: present -- name: Create a basic IPv6 Primary IP - hcloud_primary_ip: - name: my-primary-ip - datacenter: fsn1-dc14 - type: ipv6 - state: present -- name: Primary IP should be absent - hcloud_primary_ip: - name: my-primary-ip - state: absent -""" - -RETURN = """ -hcloud_primary_ip: - description: The Primary IP instance - returned: Always - type: complex - contains: - id: - description: ID of the Primary IP - type: int - returned: Always - sample: 12345 - name: - description: Name of the Primary IP - type: str - returned: Always - sample: my-primary-ip - ip: - description: IP Address of the Primary IP - type: str - returned: Always - sample: 116.203.104.109 - type: - description: Type of the Primary IP - type: str - returned: Always - sample: ipv4 - datacenter: - description: Name of the datacenter of the Primary IP - type: str - returned: Always - sample: fsn1-dc14 - delete_protection: - description: True if Primary IP is protected for deletion - type: bool - returned: always - sample: false - labels: - description: User-defined labels (key-value pairs) - type: dict - returned: Always - sample: - key: value - mylabel: 123 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudPrimaryIP(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_primary_ip") - self.hcloud_primary_ip = None - - def _prepare_result(self): - return { - "id": to_native(self.hcloud_primary_ip.id), - "name": to_native(self.hcloud_primary_ip.name), - "ip": to_native(self.hcloud_primary_ip.ip), - "type": to_native(self.hcloud_primary_ip.type), - "datacenter": to_native(self.hcloud_primary_ip.datacenter.name), - "labels": self.hcloud_primary_ip.labels, - "delete_protection": self.hcloud_primary_ip.protection["delete"], - } - - def _get_primary_ip(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_primary_ip = self.client.primary_ips.get_by_id( - self.module.params.get("id") - ) - else: - self.hcloud_primary_ip = self.client.primary_ips.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_primary_ip(self): - self.module.fail_on_missing_params( - required_params=["type", "datacenter"] - ) - try: - params = { - "type": self.module.params.get("type"), - "name": self.module.params.get("name"), - "datacenter": self.client.datacenters.get_by_name( - self.module.params.get("datacenter") - ) - } - - if self.module.params.get("labels") is not None: - params["labels"] = self.module.params.get("labels") - if not self.module.check_mode: - resp = self.client.primary_ips.create(**params) - self.hcloud_primary_ip = resp.primary_ip - - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None: - self.hcloud_primary_ip.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e) - self._mark_as_changed() - self._get_primary_ip() - - def _update_primary_ip(self): - try: - labels = self.module.params.get("labels") - if labels is not None and labels != self.hcloud_primary_ip.labels: - if not self.module.check_mode: - self.hcloud_primary_ip.update(labels=labels) - self._mark_as_changed() - - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None and delete_protection != self.hcloud_primary_ip.protection["delete"]: - if not self.module.check_mode: - self.hcloud_primary_ip.change_protection(delete=delete_protection).wait_until_finished() - self._mark_as_changed() - - self._get_primary_ip() - except Exception as e: - self.module.fail_json(msg=e.message) - - def present_primary_ip(self): - self._get_primary_ip() - if self.hcloud_primary_ip is None: - self._create_primary_ip() - else: - self._update_primary_ip() - - def delete_primary_ip(self): - try: - self._get_primary_ip() - if self.hcloud_primary_ip is not None: - if not self.module.check_mode: - self.client.primary_ips.delete(self.hcloud_primary_ip) - self._mark_as_changed() - self.hcloud_primary_ip = None - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - datacenter={"type": "str"}, - auto_delete={"type": "bool", "default": False}, - type={"choices": ["ipv4", "ipv6"]}, - labels={"type": "dict"}, - delete_protection={"type": "bool"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name']], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudPrimaryIP.define_module() - - hcloud = AnsibleHcloudPrimaryIP(module) - state = module.params["state"] - if state == "absent": - hcloud.delete_primary_ip() - elif state == "present": - hcloud.present_primary_ip() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_rdns.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_rdns.py deleted file mode 100644 index 9f79fbe70..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_rdns.py +++ /dev/null @@ -1,360 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_rdns - -short_description: Create and manage reverse DNS entries on the Hetzner Cloud. - - -description: - - Create, update and delete reverse DNS entries on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@lkaemmerling) - -options: - server: - description: - - The name of the Hetzner Cloud server you want to add the reverse DNS entry to. - type: str - floating_ip: - description: - - The name of the Hetzner Cloud Floating IP you want to add the reverse DNS entry to. - type: str - primary_ip: - description: - - The name of the Hetzner Cloud Primary IP you want to add the reverse DNS entry to. - type: str - load_balancer: - description: - - The name of the Hetzner Cloud Load Balancer you want to add the reverse DNS entry to. - type: str - ip_address: - description: - - The IP address that should point to I(dns_ptr). - type: str - required: true - dns_ptr: - description: - - The DNS address the I(ip_address) should resolve to. - - Omit the param to reset the reverse DNS entry to the default value. - type: str - state: - description: - - State of the reverse DNS entry. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.3.0 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a reverse DNS entry for a server - hcloud_rdns: - server: my-server - ip_address: 123.123.123.123 - dns_ptr: example.com - state: present - -- name: Create a reverse DNS entry for a Floating IP - hcloud_rdns: - floating_ip: my-floating-ip - ip_address: 123.123.123.123 - dns_ptr: example.com - state: present - -- name: Create a reverse DNS entry for a Primary IP - hcloud_rdns: - primary_ip: my-primary-ip - ip_address: 123.123.123.123 - dns_ptr: example.com - state: present - -- name: Create a reverse DNS entry for a Load Balancer - hcloud_rdns: - load_balancer: my-load-balancer - ip_address: 123.123.123.123 - dns_ptr: example.com - state: present - -- name: Ensure the reverse DNS entry is absent (remove if needed) - hcloud_rdns: - server: my-server - ip_address: 123.123.123.123 - dns_ptr: example.com - state: absent -""" - -RETURN = """ -hcloud_rdns: - description: The reverse DNS entry - returned: always - type: complex - contains: - server: - description: Name of the server - type: str - returned: always - sample: my-server - floating_ip: - description: Name of the Floating IP - type: str - returned: always - sample: my-floating-ip - primary_ip: - description: Name of the Primary IP - type: str - returned: always - sample: my-primary-ip - load_balancer: - description: Name of the Load Balancer - type: str - returned: always - sample: my-load-balancer - ip_address: - description: The IP address that point to the DNS ptr - type: str - returned: always - sample: 123.123.123.123 - dns_ptr: - description: The DNS that resolves to the IP - type: str - returned: always - sample: example.com -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils - - -class AnsibleHcloudReverseDNS(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_rdns") - self.hcloud_resource = None - self.hcloud_rdns = None - - def _prepare_result(self): - result = { - "server": None, - "floating_ip": None, - "load_balancer": None, - "ip_address": to_native(self.hcloud_rdns["ip_address"]), - "dns_ptr": to_native(self.hcloud_rdns["dns_ptr"]), - } - - if self.module.params.get("server"): - result["server"] = to_native(self.hcloud_resource.name) - elif self.module.params.get("floating_ip"): - result["floating_ip"] = to_native(self.hcloud_resource.name) - elif self.module.params.get("load_balancer"): - result["load_balancer"] = to_native(self.hcloud_resource.name) - elif self.module.params.get("primary_ip"): - result["primary_ip"] = to_native(self.hcloud_resource.name) - return result - - def _get_resource(self): - try: - if self.module.params.get("server"): - self.hcloud_resource = self.client.servers.get_by_name( - self.module.params.get("server") - ) - if self.hcloud_resource is None: - self.module.fail_json(msg="The selected server does not exist") - elif self.module.params.get("floating_ip"): - self.hcloud_resource = self.client.floating_ips.get_by_name( - self.module.params.get("floating_ip") - ) - if self.hcloud_resource is None: - self.module.fail_json(msg="The selected Floating IP does not exist") - elif self.module.params.get("primary_ip"): - self.hcloud_resource = self.client.primary_ips.get_by_name( - self.module.params.get("primary_ip") - ) - if self.hcloud_resource is None: - self.module.fail_json(msg="The selected Floating IP does not exist") - elif self.module.params.get("load_balancer"): - self.hcloud_resource = self.client.load_balancers.get_by_name( - self.module.params.get("load_balancer") - ) - if self.hcloud_resource is None: - self.module.fail_json(msg="The selected Load Balancer does not exist") - except Exception as e: - self.module.fail_json(msg=e.message) - - def _get_rdns(self): - ip_address = self.module.params.get("ip_address") - if utils.validate_ip_address(ip_address): - if self.module.params.get("server"): - if self.hcloud_resource.public_net.ipv4.ip == ip_address: - self.hcloud_rdns = { - "ip_address": self.hcloud_resource.public_net.ipv4.ip, - "dns_ptr": self.hcloud_resource.public_net.ipv4.dns_ptr, - } - else: - self.module.fail_json(msg="The selected server does not have this IP address") - elif self.module.params.get("floating_ip"): - if self.hcloud_resource.ip == ip_address: - self.hcloud_rdns = { - "ip_address": self.hcloud_resource.ip, - "dns_ptr": self.hcloud_resource.dns_ptr[0]["dns_ptr"], - } - else: - self.module.fail_json(msg="The selected Floating IP does not have this IP address") - elif self.module.params.get("primary_ip"): - if self.hcloud_resource.ip == ip_address: - self.hcloud_rdns = { - "ip_address": self.hcloud_resource.ip, - "dns_ptr": self.hcloud_resource.dns_ptr[0]["dns_ptr"], - } - else: - self.module.fail_json(msg="The selected Primary IP does not have this IP address") - elif self.module.params.get("load_balancer"): - if self.hcloud_resource.public_net.ipv4.ip == ip_address: - self.hcloud_rdns = { - "ip_address": self.hcloud_resource.public_net.ipv4.ip, - "dns_ptr": self.hcloud_resource.public_net.ipv4.dns_ptr, - } - else: - self.module.fail_json(msg="The selected Load Balancer does not have this IP address") - - elif utils.validate_ip_v6_address(ip_address): - if self.module.params.get("server"): - for ipv6_address_dns_ptr in self.hcloud_resource.public_net.ipv6.dns_ptr: - if ipv6_address_dns_ptr["ip"] == ip_address: - self.hcloud_rdns = { - "ip_address": ipv6_address_dns_ptr["ip"], - "dns_ptr": ipv6_address_dns_ptr["dns_ptr"], - } - elif self.module.params.get("floating_ip"): - for ipv6_address_dns_ptr in self.hcloud_resource.dns_ptr: - if ipv6_address_dns_ptr["ip"] == ip_address: - self.hcloud_rdns = { - "ip_address": ipv6_address_dns_ptr["ip"], - "dns_ptr": ipv6_address_dns_ptr["dns_ptr"], - } - elif self.module.params.get("primary_ip"): - for ipv6_address_dns_ptr in self.hcloud_resource.dns_ptr: - if ipv6_address_dns_ptr["ip"] == ip_address: - self.hcloud_rdns = { - "ip_address": ipv6_address_dns_ptr["ip"], - "dns_ptr": ipv6_address_dns_ptr["dns_ptr"], - } - elif self.module.params.get("load_balancer"): - for ipv6_address_dns_ptr in self.hcloud_resource.public_net.ipv6.dns_ptr: - if ipv6_address_dns_ptr["ip"] == ip_address: - self.hcloud_rdns = { - "ip_address": ipv6_address_dns_ptr["ip"], - "dns_ptr": ipv6_address_dns_ptr["dns_ptr"], - } - else: - self.module.fail_json(msg="The given IP address is not valid") - - def _create_rdns(self): - self.module.fail_on_missing_params( - required_params=["dns_ptr"] - ) - params = { - "ip": self.module.params.get("ip_address"), - "dns_ptr": self.module.params.get("dns_ptr"), - } - - if not self.module.check_mode: - try: - self.hcloud_resource.change_dns_ptr(**params).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self._get_resource() - self._get_rdns() - - def _update_rdns(self): - dns_ptr = self.module.params.get("dns_ptr") - if dns_ptr != self.hcloud_rdns["dns_ptr"]: - params = { - "ip": self.module.params.get("ip_address"), - "dns_ptr": dns_ptr, - } - - if not self.module.check_mode: - try: - self.hcloud_resource.change_dns_ptr(**params).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self._get_resource() - self._get_rdns() - - def present_rdns(self): - self._get_resource() - self._get_rdns() - if self.hcloud_rdns is None: - self._create_rdns() - else: - self._update_rdns() - - def delete_rdns(self): - self._get_resource() - self._get_rdns() - if self.hcloud_rdns is not None: - if not self.module.check_mode: - try: - self.hcloud_resource.change_dns_ptr(ip=self.hcloud_rdns['ip_address'], dns_ptr=None) - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self.hcloud_rdns = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - server={"type": "str"}, - floating_ip={"type": "str"}, - load_balancer={"type": "str"}, - primary_ip={"type": "str"}, - ip_address={"type": "str", "required": True}, - dns_ptr={"type": "str"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['server', 'floating_ip', 'load_balancer', 'primary_ip']], - mutually_exclusive=[["server", "floating_ip", 'load_balancer', 'primary_ip']], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudReverseDNS.define_module() - - hcloud = AnsibleHcloudReverseDNS(module) - state = module.params["state"] - if state == "absent": - hcloud.delete_rdns() - elif state == "present": - hcloud.present_rdns() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_route.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_route.py deleted file mode 100644 index c75177953..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_route.py +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_route - -short_description: Create and delete cloud routes on the Hetzner Cloud. - - -description: - - Create, update and delete cloud routes on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@lkaemmerling) - -options: - network: - description: - - The name of the Hetzner Cloud Network. - type: str - required: true - destination: - description: - - Destination network or host of this route. - type: str - required: true - gateway: - description: - - Gateway for the route. - type: str - required: true - state: - description: - - State of the route. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.3.0 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic route - hcloud_route: - network: my-network - destination: 10.100.1.0/24 - gateway: 10.0.1.1 - state: present - -- name: Ensure the route is absent - hcloud_route: - network: my-network - destination: 10.100.1.0/24 - gateway: 10.0.1.1 - state: absent -""" - -RETURN = """ -hcloud_route: - description: One Route of a Network - returned: always - type: complex - contains: - network: - description: Name of the Network - type: str - returned: always - sample: my-network - destination: - description: Destination network or host of this route - type: str - returned: always - sample: 10.0.0.0/8 - gateway: - description: Gateway of the route - type: str - returned: always - sample: 10.0.0.1 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - -try: - from hcloud.networks.domain import NetworkRoute -except ImportError: - NetworkRoute = None - - -class AnsibleHcloudRoute(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_route") - self.hcloud_network = None - self.hcloud_route = None - - def _prepare_result(self): - return { - "network": to_native(self.hcloud_network.name), - "destination": to_native(self.hcloud_route.destination), - "gateway": self.hcloud_route.gateway, - } - - def _get_network(self): - try: - self.hcloud_network = self.client.networks.get_by_name(self.module.params.get("network")) - self.hcloud_route = None - except Exception as e: - self.module.fail_json(msg=e.message) - - def _get_route(self): - destination = self.module.params.get("destination") - gateway = self.module.params.get("gateway") - for route in self.hcloud_network.routes: - if route.destination == destination and route.gateway == gateway: - self.hcloud_route = route - - def _create_route(self): - route = NetworkRoute( - destination=self.module.params.get("destination"), - gateway=self.module.params.get('gateway') - ) - - if not self.module.check_mode: - try: - self.hcloud_network.add_route(route=route).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - - self._mark_as_changed() - self._get_network() - self._get_route() - - def present_route(self): - self._get_network() - self._get_route() - if self.hcloud_route is None: - self._create_route() - - def delete_route(self): - self._get_network() - self._get_route() - if self.hcloud_route is not None and self.hcloud_network is not None: - if not self.module.check_mode: - try: - self.hcloud_network.delete_route(self.hcloud_route).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self.hcloud_route = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - network={"type": "str", "required": True}, - gateway={"type": "str", "required": True}, - destination={"type": "str", "required": True}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudRoute.define_module() - - hcloud = AnsibleHcloudRoute(module) - state = module.params["state"] - if state == "absent": - hcloud.delete_route() - elif state == "present": - hcloud.present_route() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py deleted file mode 100644 index 3a77da695..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server.py +++ /dev/null @@ -1,928 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_server - -short_description: Create and manage cloud servers on the Hetzner Cloud. - - -description: - - Create, update and manage cloud servers on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the Hetzner Cloud server to manage. - - Only required if no server I(name) is given - type: int - name: - description: - - The Name of the Hetzner Cloud server to manage. - - Only required if no server I(id) is given or a server does not exist. - type: str - server_type: - description: - - The Server Type of the Hetzner Cloud server to manage. - - Required if server does not exist. - type: str - ssh_keys: - description: - - List of SSH key names - - The key names correspond to the SSH keys configured for your - Hetzner Cloud account access. - type: list - elements: str - volumes: - description: - - List of Volumes IDs that should be attached to the server on server creation. - type: list - elements: str - firewalls: - description: - - List of Firewall IDs that should be attached to the server on server creation. - type: list - elements: str - image: - description: - - Image the server should be created from. - - Required if server does not exist. - type: str - location: - description: - - Location of Server. - - Required if no I(datacenter) is given and server does not exist. - type: str - datacenter: - description: - - Datacenter of Server. - - Required of no I(location) is given and server does not exist. - type: str - backups: - description: - - Enable or disable Backups for the given Server. - type: bool - upgrade_disk: - description: - - Resize the disk size, when resizing a server. - - If you want to downgrade the server later, this value should be False. - type: bool - default: no - enable_ipv4: - description: - - Enables the public ipv4 address - type: bool - default: yes - enable_ipv6: - description: - - Enables the public ipv6 address - type: bool - default: yes - ipv4: - description: - - ID of the ipv4 Primary IP to use. If omitted and enable_ipv4 is true, a new ipv4 Primary IP will automatically be created - type: str - ipv6: - description: - - ID of the ipv6 Primary IP to use. If omitted and enable_ipv6 is true, a new ipv6 Primary IP will automatically be created. - type: str - private_networks: - description: - - List of private networks the server is attached to (name or ID) - - If None, private networks are left as they are (e.g. if previously added by hcloud_server_network), - if it has any other value (including []), only those networks are attached to the server. - type: list - elements: str - force_upgrade: - description: - - Deprecated - - Force the upgrade of the server. - - Power off the server if it is running on upgrade. - type: bool - default: no - force: - description: - - Force the update of the server. - - May power off the server if update. - type: bool - default: no - allow_deprecated_image: - description: - - Allows the creation of servers with deprecated images. - type: bool - default: no - user_data: - description: - - User Data to be passed to the server on creation. - - Only used if server does not exist. - type: str - rescue_mode: - description: - - Add the Hetzner rescue system type you want the server to be booted into. - type: str - labels: - description: - - User-defined labels (key-value pairs). - type: dict - delete_protection: - description: - - Protect the Server for deletion. - - Needs to be the same as I(rebuild_protection). - type: bool - rebuild_protection: - description: - - Protect the Server for rebuild. - - Needs to be the same as I(delete_protection). - type: bool - placement_group: - description: - - Placement Group of the server. - type: str - state: - description: - - State of the server. - default: present - choices: [ absent, present, restarted, started, stopped, rebuild ] - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic server - hcloud_server: - name: my-server - server_type: cx11 - image: ubuntu-18.04 - state: present - -- name: Create a basic server with ssh key - hcloud_server: - name: my-server - server_type: cx11 - image: ubuntu-18.04 - location: fsn1 - ssh_keys: - - me@myorganisation - state: present - -- name: Resize an existing server - hcloud_server: - name: my-server - server_type: cx21 - upgrade_disk: yes - state: present - -- name: Ensure the server is absent (remove if needed) - hcloud_server: - name: my-server - state: absent - -- name: Ensure the server is started - hcloud_server: - name: my-server - state: started - -- name: Ensure the server is stopped - hcloud_server: - name: my-server - state: stopped - -- name: Ensure the server is restarted - hcloud_server: - name: my-server - state: restarted - -- name: Ensure the server is will be booted in rescue mode and therefore restarted - hcloud_server: - name: my-server - rescue_mode: linux64 - state: restarted - -- name: Ensure the server is rebuild - hcloud_server: - name: my-server - image: ubuntu-18.04 - state: rebuild - -- name: Add server to placement group - hcloud_server: - name: my-server - placement_group: my-placement-group - force: True - state: present - -- name: Remove server from placement group - hcloud_server: - name: my-server - placement_group: null - state: present - -- name: Add server with private network only - hcloud_server: - name: my-server - enable_ipv4: false - enable_ipv6: false - private_networks: - - my-network - - 4711 - state: present -""" - -RETURN = """ -hcloud_server: - description: The server instance - returned: Always - type: complex - contains: - id: - description: Numeric identifier of the server - returned: always - type: int - sample: 1937415 - name: - description: Name of the server - returned: always - type: str - sample: my-server - status: - description: Status of the server - returned: always - type: str - sample: running - server_type: - description: Name of the server type of the server - returned: always - type: str - sample: cx11 - ipv4_address: - description: Public IPv4 address of the server - returned: always - type: str - sample: 116.203.104.109 - ipv6: - description: IPv6 network of the server - returned: always - type: str - sample: 2a01:4f8:1c1c:c140::/64 - private_networks: - description: List of private networks the server is attached to (name or ID) - returned: always - type: list - elements: str - sample: ['my-network', 'another-network', '4711'] - private_networks_info: - description: List of private networks the server is attached to (dict with name and ip) - returned: always - type: list - elements: dict - sample: [{'name': 'my-network', 'ip': '192.168.1.1'}, {'name': 'another-network', 'ip': '10.185.50.40'}] - location: - description: Name of the location of the server - returned: always - type: str - sample: fsn1 - placement_group: - description: Placement Group of the server - type: str - returned: always - sample: 4711 - version_added: "1.5.0" - datacenter: - description: Name of the datacenter of the server - returned: always - type: str - sample: fsn1-dc14 - rescue_enabled: - description: True if rescue mode is enabled, Server will then boot into rescue system on next reboot - returned: always - type: bool - sample: false - backup_window: - description: Time window (UTC) in which the backup will run, or null if the backups are not enabled - returned: always - type: bool - sample: 22-02 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict - delete_protection: - description: True if server is protected for deletion - type: bool - returned: always - sample: false - version_added: "0.1.0" - rebuild_protection: - description: True if server is protected for rebuild - type: bool - returned: always - sample: false - version_added: "0.1.0" -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud -from datetime import timedelta - -try: - from hcloud.volumes.domain import Volume - from hcloud.ssh_keys.domain import SSHKey - from hcloud.servers.domain import Server, ServerCreatePublicNetwork - from hcloud.firewalls.domain import FirewallResource -except ImportError: - Volume = None - SSHKey = None - Server = None - ServerCreatePublicNetwork = None - FirewallResource = None - - -class AnsibleHcloudServer(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server") - self.hcloud_server = None - - def _prepare_result(self): - image = None if self.hcloud_server.image is None else to_native(self.hcloud_server.image.name) - placement_group = None if self.hcloud_server.placement_group is None else to_native( - self.hcloud_server.placement_group.name) - ipv4_address = None if self.hcloud_server.public_net.ipv4 is None else to_native( - self.hcloud_server.public_net.ipv4.ip) - ipv6 = None if self.hcloud_server.public_net.ipv6 is None else to_native(self.hcloud_server.public_net.ipv6.ip) - backup_window = None if self.hcloud_server.backup_window is None else to_native(self.hcloud_server.backup_window) - return { - "id": to_native(self.hcloud_server.id), - "name": to_native(self.hcloud_server.name), - "ipv4_address": ipv4_address, - "ipv6": ipv6, - "private_networks": [to_native(net.network.name) for net in self.hcloud_server.private_net], - "private_networks_info": [{"name": to_native(net.network.name), "ip": net.ip} for net in self.hcloud_server.private_net], - "image": image, - "server_type": to_native(self.hcloud_server.server_type.name), - "datacenter": to_native(self.hcloud_server.datacenter.name), - "location": to_native(self.hcloud_server.datacenter.location.name), - "placement_group": placement_group, - "rescue_enabled": self.hcloud_server.rescue_enabled, - "backup_window": backup_window, - "labels": self.hcloud_server.labels, - "delete_protection": self.hcloud_server.protection["delete"], - "rebuild_protection": self.hcloud_server.protection["rebuild"], - "status": to_native(self.hcloud_server.status), - } - - def _get_server(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_server = self.client.servers.get_by_id( - self.module.params.get("id") - ) - else: - self.hcloud_server = self.client.servers.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_server(self): - self.module.fail_on_missing_params( - required_params=["name", "server_type", "image"] - ) - - server_type = self._get_server_type() - - params = { - "name": self.module.params.get("name"), - "server_type": server_type, - "user_data": self.module.params.get("user_data"), - "labels": self.module.params.get("labels"), - "image": self._get_image(server_type), - "placement_group": self._get_placement_group(), - "public_net": ServerCreatePublicNetwork( - enable_ipv4=self.module.params.get("enable_ipv4"), - enable_ipv6=self.module.params.get("enable_ipv6") - ) - } - - if self.module.params.get("ipv4") is not None: - p = self.client.primary_ips.get_by_name(self.module.params.get("ipv4")) - if not p: - p = self.client.primary_ips.get_by_id(self.module.params.get("ipv4")) - params["public_net"].ipv4 = p - - if self.module.params.get("ipv6") is not None: - p = self.client.primary_ips.get_by_name(self.module.params.get("ipv6")) - if not p: - p = self.client.primary_ips.get_by_id(self.module.params.get("ipv6")) - params["public_net"].ipv6 = p - - if self.module.params.get("private_networks") is not None: - _networks = [] - for network_name_or_id in self.module.params.get("private_networks"): - _networks.append( - self.client.networks.get_by_name(network_name_or_id) - or self.client.networks.get_by_id(network_name_or_id) - ) - params["networks"] = _networks - - if self.module.params.get("ssh_keys") is not None: - params["ssh_keys"] = [ - SSHKey(name=ssh_key_name) - for ssh_key_name in self.module.params.get("ssh_keys") - ] - - if self.module.params.get("volumes") is not None: - params["volumes"] = [ - Volume(id=volume_id) for volume_id in self.module.params.get("volumes") - ] - if self.module.params.get("firewalls") is not None: - params["firewalls"] = [] - for fw in self.module.params.get("firewalls"): - f = self.client.firewalls.get_by_name(fw) - if f is not None: - # When firewall name is not available look for id instead - params["firewalls"].append(f) - else: - params["firewalls"].append(self.client.firewalls.get_by_id(fw)) - - if self.module.params.get("location") is None and self.module.params.get("datacenter") is None: - # When not given, the API will choose the location. - params["location"] = None - params["datacenter"] = None - elif self.module.params.get("location") is not None and self.module.params.get("datacenter") is None: - params["location"] = self.client.locations.get_by_name( - self.module.params.get("location") - ) - elif self.module.params.get("location") is None and self.module.params.get("datacenter") is not None: - params["datacenter"] = self.client.datacenters.get_by_name( - self.module.params.get("datacenter") - ) - - if self.module.params.get("state") == "stopped": - params["start_after_create"] = False - if not self.module.check_mode: - try: - resp = self.client.servers.create(**params) - self.result["root_password"] = resp.root_password - resp.action.wait_until_finished(max_retries=1000) - [action.wait_until_finished() for action in resp.next_actions] - - rescue_mode = self.module.params.get("rescue_mode") - if rescue_mode: - self._get_server() - self._set_rescue_mode(rescue_mode) - - backups = self.module.params.get("backups") - if backups: - self._get_server() - self.hcloud_server.enable_backup().wait_until_finished() - - delete_protection = self.module.params.get("delete_protection") - rebuild_protection = self.module.params.get("rebuild_protection") - if delete_protection is not None and rebuild_protection is not None: - self._get_server() - self.hcloud_server.change_protection(delete=delete_protection, - rebuild=rebuild_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self._get_server() - - def _get_image(self, server_type): - image_resp = self.client.images.get_list(name=self.module.params.get("image"), architecture=server_type.architecture, include_deprecated=True) - images = getattr(image_resp, 'images') - image = None - if images is not None and len(images) > 0: - # If image name is not available look for id instead - image = images[0] - else: - try: - image = self.client.images.get_by_id(self.module.params.get("image")) - except Exception: - self.module.fail_json(msg="Image %s was not found" % self.module.params.get('image')) - if image.deprecated is not None: - available_until = image.deprecated + timedelta(days=90) - if self.module.params.get("allow_deprecated_image"): - self.module.warn( - "You try to use a deprecated image. The image %s will continue to be available until %s.") % ( - image.name, available_until.strftime('%Y-%m-%d')) - else: - self.module.fail_json( - msg=("You try to use a deprecated image. The image %s will continue to be available until %s." + - " If you want to use this image use allow_deprecated_image=yes." - ) % (image.name, available_until.strftime('%Y-%m-%d'))) - return image - - def _get_server_type(self): - server_type = self.client.server_types.get_by_name( - self.module.params.get("server_type") - ) - if server_type is None: - try: - server_type = self.client.server_types.get_by_id(self.module.params.get("server_type")) - except Exception: - self.module.fail_json(msg="server_type %s was not found" % self.module.params.get('server_type')) - - return server_type - - def _get_placement_group(self): - if self.module.params.get("placement_group") is None: - return None - - placement_group = self.client.placement_groups.get_by_name( - self.module.params.get("placement_group") - ) - if placement_group is None: - try: - placement_group = self.client.placement_groups.get_by_id(self.module.params.get("placement_group")) - except Exception: - self.module.fail_json( - msg="placement_group %s was not found" % self.module.params.get("placement_group")) - - return placement_group - - def _get_primary_ip(self, field): - if self.module.params.get(field) is None: - return None - - primary_ip = self.client.primary_ips.get_by_name( - self.module.params.get(field) - ) - if primary_ip is None: - try: - primary_ip = self.client.primary_ips.get_by_id(self.module.params.get(field)) - except Exception as e: - self.module.fail_json( - msg="primary_ip %s was not found" % self.module.params.get(field)) - - return primary_ip - - def _update_server(self): - if "force_upgrade" in self.module.params: - self.module.warn("force_upgrade is deprecated, use force instead") - - try: - previous_server_status = self.hcloud_server.status - - rescue_mode = self.module.params.get("rescue_mode") - if rescue_mode and self.hcloud_server.rescue_enabled is False: - if not self.module.check_mode: - self._set_rescue_mode(rescue_mode) - self._mark_as_changed() - elif not rescue_mode and self.hcloud_server.rescue_enabled is True: - if not self.module.check_mode: - self.hcloud_server.disable_rescue().wait_until_finished() - self._mark_as_changed() - - backups = self.module.params.get("backups") - if backups and self.hcloud_server.backup_window is None: - if not self.module.check_mode: - self.hcloud_server.enable_backup().wait_until_finished() - self._mark_as_changed() - elif backups is not None and not backups and self.hcloud_server.backup_window is not None: - if not self.module.check_mode: - self.hcloud_server.disable_backup().wait_until_finished() - self._mark_as_changed() - - labels = self.module.params.get("labels") - if labels is not None and labels != self.hcloud_server.labels: - if not self.module.check_mode: - self.hcloud_server.update(labels=labels) - self._mark_as_changed() - - wanted_firewalls = self.module.params.get("firewalls") - if wanted_firewalls is not None: - # Removing existing but not wanted firewalls - for current_firewall in self.hcloud_server.public_net.firewalls: - if current_firewall.firewall.name not in wanted_firewalls: - self._mark_as_changed() - if not self.module.check_mode: - r = FirewallResource(type="server", server=self.hcloud_server) - actions = self.client.firewalls.remove_from_resources(current_firewall.firewall, [r]) - for a in actions: - a.wait_until_finished() - - # Adding wanted firewalls that doesn't exist yet - for fname in wanted_firewalls: - found = False - for f in self.hcloud_server.public_net.firewalls: - if f.firewall.name == fname: - found = True - break - - if not found: - self._mark_as_changed() - if not self.module.check_mode: - fw = self.client.firewalls.get_by_name(fname) - if fw is None: - self.module.fail_json(msg="firewall %s was not found" % fname) - r = FirewallResource(type="server", server=self.hcloud_server) - actions = self.client.firewalls.apply_to_resources(fw, [r]) - for a in actions: - a.wait_until_finished() - - if "placement_group" in self.module.params: - if self.module.params["placement_group"] is None and self.hcloud_server.placement_group is not None: - if not self.module.check_mode: - self.hcloud_server.remove_from_placement_group().wait_until_finished() - self._mark_as_changed() - else: - placement_group = self._get_placement_group() - if ( - placement_group is not None and - ( - self.hcloud_server.placement_group is None or - self.hcloud_server.placement_group.id != placement_group.id - ) - ): - self.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.add_to_placement_group(placement_group).wait_until_finished() - self._mark_as_changed() - - if "ipv4" in self.module.params: - if ( - self.module.params["ipv4"] is None and - self.hcloud_server.public_net.primary_ipv4 is not None and - not self.module.params.get("enable_ipv4") - ): - self.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.public_net.primary_ipv4.unassign().wait_until_finished() - self._mark_as_changed() - else: - primary_ip = self._get_primary_ip("ipv4") - if ( - primary_ip is not None and - ( - self.hcloud_server.public_net.primary_ipv4 is None or - self.hcloud_server.public_net.primary_ipv4.id != primary_ip.id - ) - ): - self.stop_server_if_forced() - if not self.module.check_mode: - if self.hcloud_server.public_net.primary_ipv4: - self.hcloud_server.public_net.primary_ipv4.unassign().wait_until_finished() - primary_ip.assign(self.hcloud_server.id, "server").wait_until_finished() - self._mark_as_changed() - if "ipv6" in self.module.params: - if ( - (self.module.params["ipv6"] is None or self.module.params["ipv6"] == "") and - self.hcloud_server.public_net.primary_ipv6 is not None and - not self.module.params.get("enable_ipv6") - ): - self.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.public_net.primary_ipv6.unassign().wait_until_finished() - self._mark_as_changed() - else: - primary_ip = self._get_primary_ip("ipv6") - if ( - primary_ip is not None and - ( - self.hcloud_server.public_net.primary_ipv6 is None or - self.hcloud_server.public_net.primary_ipv6.id != primary_ip.id - ) - ): - self.stop_server_if_forced() - if not self.module.check_mode: - if self.hcloud_server.public_net.primary_ipv6 is not None: - self.hcloud_server.public_net.primary_ipv6.unassign().wait_until_finished() - primary_ip.assign(self.hcloud_server.id, "server").wait_until_finished() - self._mark_as_changed() - if "private_networks" in self.module.params and self.module.params["private_networks"] is not None: - if not bool(self.module.params["private_networks"]): - # This handles None, "" and [] - networks_target = {} - else: - _networks = {} - for network_name_or_id in self.module.params.get("private_networks"): - _found_network = self.client.networks.get_by_name(network_name_or_id) \ - or self.client.networks.get_by_id(network_name_or_id) - _networks.update( - {_found_network.id: _found_network} - ) - networks_target = _networks - networks_is = dict() - for p_network in self.hcloud_server.private_net: - networks_is.update({p_network.network.id: p_network.network}) - for network_id in set(list(networks_is) + list(networks_target)): - if network_id in networks_is and network_id not in networks_target: - self.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.detach_from_network(networks_is[network_id]).wait_until_finished() - self._mark_as_changed() - elif network_id in networks_target and network_id not in networks_is: - self.stop_server_if_forced() - if not self.module.check_mode: - self.hcloud_server.attach_to_network(networks_target[network_id]).wait_until_finished() - self._mark_as_changed() - - server_type = self.module.params.get("server_type") - if server_type is not None and self.hcloud_server.server_type.name != server_type: - self.stop_server_if_forced() - - timeout = 100 - if self.module.params.get("upgrade_disk"): - timeout = ( - 1000 - ) # When we upgrade the disk to the resize progress takes some more time. - if not self.module.check_mode: - self.hcloud_server.change_type( - server_type=self._get_server_type(), - upgrade_disk=self.module.params.get("upgrade_disk"), - ).wait_until_finished(timeout) - self._mark_as_changed() - - if ( - not self.module.check_mode and - ( - ( - self.module.params.get("state") == "present" and - previous_server_status == Server.STATUS_RUNNING - ) or - self.module.params.get("state") == "started" - ) - ): - self.start_server() - - delete_protection = self.module.params.get("delete_protection") - rebuild_protection = self.module.params.get("rebuild_protection") - if (delete_protection is not None and rebuild_protection is not None) and ( - delete_protection != self.hcloud_server.protection["delete"] or rebuild_protection != - self.hcloud_server.protection["rebuild"]): - if not self.module.check_mode: - self.hcloud_server.change_protection(delete=delete_protection, - rebuild=rebuild_protection).wait_until_finished() - self._mark_as_changed() - self._get_server() - except Exception as e: - self.module.fail_json(msg=e) - - def _set_rescue_mode(self, rescue_mode): - if self.module.params.get("ssh_keys"): - resp = self.hcloud_server.enable_rescue(type=rescue_mode, - ssh_keys=[self.client.ssh_keys.get_by_name(ssh_key_name).id - for - ssh_key_name in - self.module.params.get("ssh_keys")]) - else: - resp = self.hcloud_server.enable_rescue(type=rescue_mode) - resp.action.wait_until_finished() - self.result["root_password"] = resp.root_password - - def start_server(self): - try: - if self.hcloud_server: - if self.hcloud_server.status != Server.STATUS_RUNNING: - if not self.module.check_mode: - self.client.servers.power_on(self.hcloud_server).wait_until_finished() - self._mark_as_changed() - self._get_server() - except Exception as e: - self.module.fail_json(msg=e.message) - - def stop_server(self): - try: - if self.hcloud_server: - if self.hcloud_server.status != Server.STATUS_OFF: - if not self.module.check_mode: - self.client.servers.power_off(self.hcloud_server).wait_until_finished() - self._mark_as_changed() - self._get_server() - except Exception as e: - self.module.fail_json(msg=e.message) - - def stop_server_if_forced(self): - previous_server_status = self.hcloud_server.status - if previous_server_status == Server.STATUS_RUNNING and not self.module.check_mode: - if ( - self.module.params.get("force_upgrade") or - self.module.params.get("force") or - self.module.params.get("state") == "stopped" - ): - self.stop_server() # Only stopped server can be upgraded - return previous_server_status - else: - self.module.warn( - "You can not upgrade a running instance %s. You need to stop the instance or use force=yes." - % self.hcloud_server.name - ) - - return None - - def rebuild_server(self): - self.module.fail_on_missing_params( - required_params=["image"] - ) - try: - if not self.module.check_mode: - image = self._get_image(self.hcloud_server.server_type) - self.client.servers.rebuild(self.hcloud_server, image).wait_until_finished(1000) # When we rebuild the server progress takes some more time. - self._mark_as_changed() - - self._get_server() - except Exception as e: - self.module.fail_json(msg=e.message) - - def present_server(self): - self._get_server() - if self.hcloud_server is None: - self._create_server() - else: - self._update_server() - - def delete_server(self): - try: - self._get_server() - if self.hcloud_server is not None: - if not self.module.check_mode: - self.client.servers.delete(self.hcloud_server).wait_until_finished() - self._mark_as_changed() - self.hcloud_server = None - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - image={"type": "str"}, - server_type={"type": "str"}, - location={"type": "str"}, - datacenter={"type": "str"}, - user_data={"type": "str"}, - ssh_keys={"type": "list", "elements": "str", "no_log": False}, - volumes={"type": "list", "elements": "str"}, - firewalls={"type": "list", "elements": "str"}, - labels={"type": "dict"}, - backups={"type": "bool"}, - upgrade_disk={"type": "bool", "default": False}, - enable_ipv4={"type": "bool", "default": True}, - enable_ipv6={"type": "bool", "default": True}, - ipv4={"type": "str"}, - ipv6={"type": "str"}, - private_networks={"type": "list", "elements": "str", "default": None}, - force={"type": "bool", "default": False}, - force_upgrade={"type": "bool", "default": False}, - allow_deprecated_image={"type": "bool", "default": False}, - rescue_mode={"type": "str"}, - delete_protection={"type": "bool"}, - rebuild_protection={"type": "bool"}, - placement_group={"type": "str"}, - state={ - "choices": ["absent", "present", "restarted", "started", "stopped", "rebuild"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name']], - mutually_exclusive=[["location", "datacenter"]], - required_together=[["delete_protection", "rebuild_protection"]], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudServer.define_module() - - hcloud = AnsibleHcloudServer(module) - state = module.params.get("state") - if state == "absent": - hcloud.delete_server() - elif state == "present": - hcloud.present_server() - elif state == "started": - hcloud.present_server() - hcloud.start_server() - elif state == "stopped": - hcloud.present_server() - hcloud.stop_server() - elif state == "restarted": - hcloud.present_server() - hcloud.stop_server() - hcloud.start_server() - elif state == "rebuild": - hcloud.present_server() - hcloud.rebuild_server() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_facts.py deleted file mode 100644 index 102ceec0d..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_facts.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_server_info - -short_description: Gather infos about your Hetzner Cloud servers. - - -description: - - Gather infos about your Hetzner Cloud servers. - - This module was called C(hcloud_server_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_server_facts). - Note that the M(hetzner.hcloud.hcloud_server_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_server_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the server you want to get. - type: int - name: - description: - - The name of the server you want to get. - type: str - label_selector: - description: - - The label selector for the server you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud server infos - hcloud_server_info: - register: output - -- name: Print the gathered infos - debug: - var: output.hcloud_server_info -""" - -RETURN = """ -hcloud_server_info: - description: The server infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the server - returned: always - type: int - sample: 1937415 - name: - description: Name of the server - returned: always - type: str - sample: my-server - status: - description: Status of the server - returned: always - type: str - sample: running - server_type: - description: Name of the server type of the server - returned: always - type: str - sample: cx11 - ipv4_address: - description: Public IPv4 address of the server - returned: always - type: str - sample: 116.203.104.109 - ipv6: - description: IPv6 network of the server - returned: always - type: str - sample: 2a01:4f8:1c1c:c140::/64 - private_networks: - description: List of private networks the server is attached to (name) - returned: always - type: list - elements: str - sample: ['my-network', 'another-network'] - private_networks_info: - description: List of private networks the server is attached to (dict with name and ip) - returned: always - type: list - elements: dict - sample: [{'name': 'my-network', 'ip': '192.168.1.1'}, {'name': 'another-network', 'ip': '10.185.50.40'}] - location: - description: Name of the location of the server - returned: always - type: str - sample: fsn1 - placement_group: - description: Placement Group of the server - type: str - returned: always - sample: 4711 - version_added: "1.5.0" - datacenter: - description: Name of the datacenter of the server - returned: always - type: str - sample: fsn1-dc14 - rescue_enabled: - description: True if rescue mode is enabled, Server will then boot into rescue system on next reboot - returned: always - type: bool - sample: false - backup_window: - description: Time window (UTC) in which the backup will run, or null if the backups are not enabled - returned: always - type: bool - sample: 22-02 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict - delete_protection: - description: True if server is protected for deletion - type: bool - returned: always - sample: false - version_added: "0.1.0" - rebuild_protection: - description: True if server is protected for rebuild - type: bool - returned: always - sample: false - version_added: "0.1.0" -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudServerInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_info") - self.hcloud_server_info = None - - def _prepare_result(self): - tmp = [] - - for server in self.hcloud_server_info: - if server is not None: - image = None if server.image is None else to_native(server.image.name) - placement_group = None if server.placement_group is None else to_native(server.placement_group.name) - ipv4_address = None if server.public_net.ipv4 is None else to_native(server.public_net.ipv4.ip) - ipv6 = None if server.public_net.ipv6 is None else to_native(server.public_net.ipv6.ip) - backup_window = None if server.backup_window is None else to_native(server.backup_window) - tmp.append({ - "id": to_native(server.id), - "name": to_native(server.name), - "ipv4_address": ipv4_address, - "ipv6": ipv6, - "private_networks": [to_native(net.network.name) for net in server.private_net], - "private_networks_info": [{"name": to_native(net.network.name), "ip": net.ip} for net in server.private_net], - "image": image, - "server_type": to_native(server.server_type.name), - "datacenter": to_native(server.datacenter.name), - "location": to_native(server.datacenter.location.name), - "placement_group": placement_group, - "rescue_enabled": server.rescue_enabled, - "backup_window": backup_window, - "labels": server.labels, - "status": to_native(server.status), - "delete_protection": server.protection["delete"], - "rebuild_protection": server.protection["rebuild"], - }) - return tmp - - def get_servers(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_server_info = [self.client.servers.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_server_info = [self.client.servers.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_server_info = self.client.servers.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_server_info = self.client.servers.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudServerInfo.define_module() - - is_old_facts = module._name == 'hcloud_server_facts' - if is_old_facts: - module.deprecate("The 'hcloud_server_facts' module has been renamed to 'hcloud_server_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudServerInfo(module) - hcloud.get_servers() - result = hcloud.get_result() - - if is_old_facts: - ansible_info = { - 'hcloud_server_facts': result['hcloud_server_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_server_info': result['hcloud_server_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_info.py deleted file mode 100644 index 102ceec0d..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_info.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_server_info - -short_description: Gather infos about your Hetzner Cloud servers. - - -description: - - Gather infos about your Hetzner Cloud servers. - - This module was called C(hcloud_server_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_server_facts). - Note that the M(hetzner.hcloud.hcloud_server_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_server_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the server you want to get. - type: int - name: - description: - - The name of the server you want to get. - type: str - label_selector: - description: - - The label selector for the server you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud server infos - hcloud_server_info: - register: output - -- name: Print the gathered infos - debug: - var: output.hcloud_server_info -""" - -RETURN = """ -hcloud_server_info: - description: The server infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the server - returned: always - type: int - sample: 1937415 - name: - description: Name of the server - returned: always - type: str - sample: my-server - status: - description: Status of the server - returned: always - type: str - sample: running - server_type: - description: Name of the server type of the server - returned: always - type: str - sample: cx11 - ipv4_address: - description: Public IPv4 address of the server - returned: always - type: str - sample: 116.203.104.109 - ipv6: - description: IPv6 network of the server - returned: always - type: str - sample: 2a01:4f8:1c1c:c140::/64 - private_networks: - description: List of private networks the server is attached to (name) - returned: always - type: list - elements: str - sample: ['my-network', 'another-network'] - private_networks_info: - description: List of private networks the server is attached to (dict with name and ip) - returned: always - type: list - elements: dict - sample: [{'name': 'my-network', 'ip': '192.168.1.1'}, {'name': 'another-network', 'ip': '10.185.50.40'}] - location: - description: Name of the location of the server - returned: always - type: str - sample: fsn1 - placement_group: - description: Placement Group of the server - type: str - returned: always - sample: 4711 - version_added: "1.5.0" - datacenter: - description: Name of the datacenter of the server - returned: always - type: str - sample: fsn1-dc14 - rescue_enabled: - description: True if rescue mode is enabled, Server will then boot into rescue system on next reboot - returned: always - type: bool - sample: false - backup_window: - description: Time window (UTC) in which the backup will run, or null if the backups are not enabled - returned: always - type: bool - sample: 22-02 - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict - delete_protection: - description: True if server is protected for deletion - type: bool - returned: always - sample: false - version_added: "0.1.0" - rebuild_protection: - description: True if server is protected for rebuild - type: bool - returned: always - sample: false - version_added: "0.1.0" -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudServerInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_info") - self.hcloud_server_info = None - - def _prepare_result(self): - tmp = [] - - for server in self.hcloud_server_info: - if server is not None: - image = None if server.image is None else to_native(server.image.name) - placement_group = None if server.placement_group is None else to_native(server.placement_group.name) - ipv4_address = None if server.public_net.ipv4 is None else to_native(server.public_net.ipv4.ip) - ipv6 = None if server.public_net.ipv6 is None else to_native(server.public_net.ipv6.ip) - backup_window = None if server.backup_window is None else to_native(server.backup_window) - tmp.append({ - "id": to_native(server.id), - "name": to_native(server.name), - "ipv4_address": ipv4_address, - "ipv6": ipv6, - "private_networks": [to_native(net.network.name) for net in server.private_net], - "private_networks_info": [{"name": to_native(net.network.name), "ip": net.ip} for net in server.private_net], - "image": image, - "server_type": to_native(server.server_type.name), - "datacenter": to_native(server.datacenter.name), - "location": to_native(server.datacenter.location.name), - "placement_group": placement_group, - "rescue_enabled": server.rescue_enabled, - "backup_window": backup_window, - "labels": server.labels, - "status": to_native(server.status), - "delete_protection": server.protection["delete"], - "rebuild_protection": server.protection["rebuild"], - }) - return tmp - - def get_servers(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_server_info = [self.client.servers.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_server_info = [self.client.servers.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_server_info = self.client.servers.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_server_info = self.client.servers.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudServerInfo.define_module() - - is_old_facts = module._name == 'hcloud_server_facts' - if is_old_facts: - module.deprecate("The 'hcloud_server_facts' module has been renamed to 'hcloud_server_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudServerInfo(module) - hcloud.get_servers() - result = hcloud.get_result() - - if is_old_facts: - ansible_info = { - 'hcloud_server_facts': result['hcloud_server_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_server_info': result['hcloud_server_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_network.py deleted file mode 100644 index 79f6838fd..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_network.py +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_server_network - -short_description: Manage the relationship between Hetzner Cloud Networks and servers - - -description: - - Create and delete the relationship Hetzner Cloud Networks and servers - -author: - - Lukas Kaemmerling (@lkaemmerling) - -options: - network: - description: - - The name of the Hetzner Cloud Networks. - type: str - required: true - server: - description: - - The name of the Hetzner Cloud server. - type: str - required: true - ip: - description: - - The IP the server should have. - type: str - alias_ips: - description: - - Alias IPs the server has. - type: list - elements: str - state: - description: - - State of the server_network. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.3.0 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic server network - hcloud_server_network: - network: my-network - server: my-server - state: present - -- name: Create a server network and specify the ip address - hcloud_server_network: - network: my-network - server: my-server - ip: 10.0.0.1 - state: present - -- name: Create a server network and add alias ips - hcloud_server_network: - network: my-network - server: my-server - ip: 10.0.0.1 - alias_ips: - - 10.1.0.1 - - 10.2.0.1 - state: present - -- name: Ensure the server network is absent (remove if needed) - hcloud_server_network: - network: my-network - server: my-server - state: absent -""" - -RETURN = """ -hcloud_server_network: - description: The relationship between a server and a network - returned: always - type: complex - contains: - network: - description: Name of the Network - type: str - returned: always - sample: my-network - server: - description: Name of the server - type: str - returned: always - sample: my-server - ip: - description: IP of the server within the Network ip range - type: str - returned: always - sample: 10.0.0.8 - alias_ips: - description: Alias IPs of the server within the Network ip range - type: str - returned: always - sample: [10.1.0.1, ...] -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - -try: - from hcloud import APIException -except ImportError: - APIException = None - - -class AnsibleHcloudServerNetwork(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_network") - self.hcloud_network = None - self.hcloud_server = None - self.hcloud_server_network = None - - def _prepare_result(self): - return { - "network": to_native(self.hcloud_network.name), - "server": to_native(self.hcloud_server.name), - "ip": to_native(self.hcloud_server_network.ip), - "alias_ips": self.hcloud_server_network.alias_ips, - } - - def _get_server_and_network(self): - try: - self.hcloud_network = self.client.networks.get_by_name(self.module.params.get("network")) - self.hcloud_server = self.client.servers.get_by_name(self.module.params.get("server")) - self.hcloud_server_network = None - except Exception as e: - self.module.fail_json(msg=e.message) - - def _get_server_network(self): - for privateNet in self.hcloud_server.private_net: - if privateNet.network.id == self.hcloud_network.id: - self.hcloud_server_network = privateNet - - def _create_server_network(self): - params = { - "network": self.hcloud_network - } - - if self.module.params.get("ip") is not None: - params["ip"] = self.module.params.get("ip") - if self.module.params.get("alias_ips") is not None: - params["alias_ips"] = self.module.params.get("alias_ips") - - if not self.module.check_mode: - try: - self.hcloud_server.attach_to_network(**params).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - - self._mark_as_changed() - self._get_server_and_network() - self._get_server_network() - - def _update_server_network(self): - params = { - "network": self.hcloud_network - } - alias_ips = self.module.params.get("alias_ips") - if alias_ips is not None and sorted(self.hcloud_server_network.alias_ips) != sorted(alias_ips): - params["alias_ips"] = alias_ips - - if not self.module.check_mode: - try: - self.hcloud_server.change_alias_ips(**params).wait_until_finished() - except APIException as e: - self.module.fail_json(msg=e.message) - - self._mark_as_changed() - self._get_server_and_network() - self._get_server_network() - - def present_server_network(self): - self._get_server_and_network() - self._get_server_network() - if self.hcloud_server_network is None: - self._create_server_network() - else: - self._update_server_network() - - def delete_server_network(self): - self._get_server_and_network() - self._get_server_network() - if self.hcloud_server_network is not None and self.hcloud_server is not None: - if not self.module.check_mode: - try: - self.hcloud_server.detach_from_network(self.hcloud_server_network.network).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self.hcloud_server_network = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - network={"type": "str", "required": True}, - server={"type": "str", "required": True}, - ip={"type": "str"}, - alias_ips={"type": "list", "elements": "str"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudServerNetwork.define_module() - - hcloud = AnsibleHcloudServerNetwork(module) - state = module.params["state"] - if state == "absent": - hcloud.delete_server_network() - elif state == "present": - hcloud.present_server_network() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_facts.py deleted file mode 100644 index a84067c32..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_facts.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_server_type_info - -short_description: Gather infos about the Hetzner Cloud server types. - - -description: - - Gather infos about your Hetzner Cloud server types. - - This module was called C(hcloud_server_type_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_server_type_facts). - Note that the M(hetzner.hcloud.hcloud_server_type_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_server_type_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the server type you want to get. - type: int - name: - description: - - The name of the server type you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud server type infos - hcloud_server_type_info: - register: output - -- name: Print the gathered infos - debug: - var: output.hcloud_server_type_info -""" - -RETURN = """ -hcloud_server_type_info: - description: The server type infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the server type - returned: always - type: int - sample: 1937415 - name: - description: Name of the server type - returned: always - type: str - sample: fsn1 - description: - description: Detail description of the server type - returned: always - type: str - sample: Falkenstein DC Park 1 - cores: - description: Number of cpu cores a server of this type will have - returned: always - type: int - sample: 1 - memory: - description: Memory a server of this type will have in GB - returned: always - type: int - sample: 1 - disk: - description: Disk size a server of this type will have in GB - returned: always - type: int - sample: 25 - storage_type: - description: Type of server boot drive - returned: always - type: str - sample: local - cpu_type: - description: Type of cpu - returned: always - type: str - sample: shared - architecture: - description: Architecture of cpu - returned: always - type: str - sample: x86 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudServerTypeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_type_info") - self.hcloud_server_type_info = None - - def _prepare_result(self): - tmp = [] - - for server_type in self.hcloud_server_type_info: - if server_type is not None: - tmp.append({ - "id": to_native(server_type.id), - "name": to_native(server_type.name), - "description": to_native(server_type.description), - "cores": server_type.cores, - "memory": server_type.memory, - "disk": server_type.disk, - "storage_type": to_native(server_type.storage_type), - "cpu_type": to_native(server_type.cpu_type), - "architecture": to_native(server_type.architecture) - }) - return tmp - - def get_server_types(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_server_type_info = [self.client.server_types.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_server_type_info = [self.client.server_types.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_server_type_info = self.client.server_types.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudServerTypeInfo.define_module() - - is_old_facts = module._name == 'hcloud_server_type_facts' - if is_old_facts: - module.deprecate("The 'hcloud_server_type_info' module has been renamed to 'hcloud_server_type_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudServerTypeInfo(module) - hcloud.get_server_types() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_server_type_info': result['hcloud_server_type_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_server_type_info': result['hcloud_server_type_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_info.py deleted file mode 100644 index a84067c32..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_server_type_info.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_server_type_info - -short_description: Gather infos about the Hetzner Cloud server types. - - -description: - - Gather infos about your Hetzner Cloud server types. - - This module was called C(hcloud_server_type_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_server_type_facts). - Note that the M(hetzner.hcloud.hcloud_server_type_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_server_type_info)! - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the server type you want to get. - type: int - name: - description: - - The name of the server type you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud server type infos - hcloud_server_type_info: - register: output - -- name: Print the gathered infos - debug: - var: output.hcloud_server_type_info -""" - -RETURN = """ -hcloud_server_type_info: - description: The server type infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the server type - returned: always - type: int - sample: 1937415 - name: - description: Name of the server type - returned: always - type: str - sample: fsn1 - description: - description: Detail description of the server type - returned: always - type: str - sample: Falkenstein DC Park 1 - cores: - description: Number of cpu cores a server of this type will have - returned: always - type: int - sample: 1 - memory: - description: Memory a server of this type will have in GB - returned: always - type: int - sample: 1 - disk: - description: Disk size a server of this type will have in GB - returned: always - type: int - sample: 25 - storage_type: - description: Type of server boot drive - returned: always - type: str - sample: local - cpu_type: - description: Type of cpu - returned: always - type: str - sample: shared - architecture: - description: Architecture of cpu - returned: always - type: str - sample: x86 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudServerTypeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_server_type_info") - self.hcloud_server_type_info = None - - def _prepare_result(self): - tmp = [] - - for server_type in self.hcloud_server_type_info: - if server_type is not None: - tmp.append({ - "id": to_native(server_type.id), - "name": to_native(server_type.name), - "description": to_native(server_type.description), - "cores": server_type.cores, - "memory": server_type.memory, - "disk": server_type.disk, - "storage_type": to_native(server_type.storage_type), - "cpu_type": to_native(server_type.cpu_type), - "architecture": to_native(server_type.architecture) - }) - return tmp - - def get_server_types(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_server_type_info = [self.client.server_types.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_server_type_info = [self.client.server_types.get_by_name( - self.module.params.get("name") - )] - else: - self.hcloud_server_type_info = self.client.server_types.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudServerTypeInfo.define_module() - - is_old_facts = module._name == 'hcloud_server_type_facts' - if is_old_facts: - module.deprecate("The 'hcloud_server_type_info' module has been renamed to 'hcloud_server_type_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudServerTypeInfo(module) - hcloud.get_server_types() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_server_type_info': result['hcloud_server_type_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_server_type_info': result['hcloud_server_type_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key.py deleted file mode 100644 index 59a5197f5..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_ssh_key - -short_description: Create and manage ssh keys on the Hetzner Cloud. - - -description: - - Create, update and manage ssh keys on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the Hetzner Cloud ssh_key to manage. - - Only required if no ssh_key I(name) is given - type: int - name: - description: - - The Name of the Hetzner Cloud ssh_key to manage. - - Only required if no ssh_key I(id) is given or a ssh_key does not exist. - type: str - fingerprint: - description: - - The Fingerprint of the Hetzner Cloud ssh_key to manage. - - Only required if no ssh_key I(id) or I(name) is given. - type: str - labels: - description: - - User-defined labels (key-value pairs) - type: dict - public_key: - description: - - The Public Key to add. - - Required if ssh_key does not exist. - type: str - state: - description: - - State of the ssh_key. - default: present - choices: [ absent, present ] - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic ssh_key - hcloud_ssh_key: - name: my-ssh_key - public_key: "ssh-rsa AAAjjk76kgf...Xt" - state: present - -- name: Create a ssh_key with labels - hcloud_ssh_key: - name: my-ssh_key - public_key: "ssh-rsa AAAjjk76kgf...Xt" - labels: - key: value - mylabel: 123 - state: present - -- name: Ensure the ssh_key is absent (remove if needed) - hcloud_ssh_key: - name: my-ssh_key - state: absent -""" - -RETURN = """ -hcloud_ssh_key: - description: The ssh_key instance - returned: Always - type: complex - contains: - id: - description: ID of the ssh_key - type: int - returned: Always - sample: 12345 - name: - description: Name of the ssh_key - type: str - returned: Always - sample: my-ssh-key - fingerprint: - description: Fingerprint of the ssh_key - type: str - returned: Always - sample: b7:2f:30:a0:2f:6c:58:6c:21:04:58:61:ba:06:3b:2f - public_key: - description: Public key of the ssh_key - type: str - returned: Always - sample: "ssh-rsa AAAjjk76kgf...Xt" - labels: - description: User-defined labels (key-value pairs) - type: dict - returned: Always - sample: - key: value - mylabel: 123 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudSSHKey(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_ssh_key") - self.hcloud_ssh_key = None - - def _prepare_result(self): - return { - "id": to_native(self.hcloud_ssh_key.id), - "name": to_native(self.hcloud_ssh_key.name), - "fingerprint": to_native(self.hcloud_ssh_key.fingerprint), - "public_key": to_native(self.hcloud_ssh_key.public_key), - "labels": self.hcloud_ssh_key.labels, - } - - def _get_ssh_key(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_ssh_key = self.client.ssh_keys.get_by_id( - self.module.params.get("id") - ) - elif self.module.params.get("fingerprint") is not None: - self.hcloud_ssh_key = self.client.ssh_keys.get_by_fingerprint( - self.module.params.get("fingerprint") - ) - elif self.module.params.get("name") is not None: - self.hcloud_ssh_key = self.client.ssh_keys.get_by_name( - self.module.params.get("name") - ) - - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_ssh_key(self): - self.module.fail_on_missing_params( - required_params=["name", "public_key"] - ) - params = { - "name": self.module.params.get("name"), - "public_key": self.module.params.get("public_key"), - "labels": self.module.params.get("labels") - } - - if not self.module.check_mode: - try: - self.client.ssh_keys.create(**params) - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self._get_ssh_key() - - def _update_ssh_key(self): - name = self.module.params.get("name") - if name is not None and self.hcloud_ssh_key.name != name: - self.module.fail_on_missing_params( - required_params=["id"] - ) - if not self.module.check_mode: - self.hcloud_ssh_key.update(name=name) - self._mark_as_changed() - - labels = self.module.params.get("labels") - if labels is not None and self.hcloud_ssh_key.labels != labels: - if not self.module.check_mode: - self.hcloud_ssh_key.update(labels=labels) - self._mark_as_changed() - - self._get_ssh_key() - - def present_ssh_key(self): - self._get_ssh_key() - if self.hcloud_ssh_key is None: - self._create_ssh_key() - else: - self._update_ssh_key() - - def delete_ssh_key(self): - self._get_ssh_key() - if self.hcloud_ssh_key is not None: - if not self.module.check_mode: - try: - self.client.ssh_keys.delete(self.hcloud_ssh_key) - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self.hcloud_ssh_key = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - public_key={"type": "str"}, - fingerprint={"type": "str"}, - labels={"type": "dict"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name', 'fingerprint']], - required_if=[['state', 'present', ['name']]], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudSSHKey.define_module() - - hcloud = AnsibleHcloudSSHKey(module) - state = module.params.get("state") - if state == "absent": - hcloud.delete_ssh_key() - elif state == "present": - hcloud.present_ssh_key() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_facts.py deleted file mode 100644 index aab98ed60..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_facts.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_ssh_key_info -short_description: Gather infos about your Hetzner Cloud ssh_keys. -description: - - Gather facts about your Hetzner Cloud ssh_keys. - - This module was called C(hcloud_ssh_key_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_ssh_key_facts). - Note that the M(hetzner.hcloud.hcloud_ssh_key_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_ssh_key_info)! -author: - - Christopher Schmitt (@cschmitt-hcloud) -options: - id: - description: - - The ID of the ssh key you want to get. - type: int - name: - description: - - The name of the ssh key you want to get. - type: str - fingerprint: - description: - - The fingerprint of the ssh key you want to get. - type: str - label_selector: - description: - - The label selector for the ssh key you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud sshkey infos - hcloud_ssh_key_info: - register: output -- name: Print the gathered infos - debug: - var: output.hcloud_ssh_key_info -""" - -RETURN = """ -hcloud_ssh_key_info: - description: The ssh key instances - returned: Always - type: complex - contains: - id: - description: Numeric identifier of the ssh_key - returned: always - type: int - sample: 1937415 - name: - description: Name of the ssh_key - returned: always - type: str - sample: my-ssh-key - fingerprint: - description: Fingerprint of the ssh key - returned: always - type: str - sample: 0e:e0:bd:c7:2d:1f:69:49:94:44:91:f1:19:fd:35:f3 - public_key: - description: The actual public key - returned: always - type: str - sample: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGpl/tnk74nnQJxxLAtutUApUZMRJxryKh7VXkNbd4g9 john@example.com" - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudSSHKeyInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_ssh_key_info") - self.hcloud_ssh_key_info = None - - def _prepare_result(self): - ssh_keys = [] - - for ssh_key in self.hcloud_ssh_key_info: - if ssh_key: - ssh_keys.append({ - "id": to_native(ssh_key.id), - "name": to_native(ssh_key.name), - "fingerprint": to_native(ssh_key.fingerprint), - "public_key": to_native(ssh_key.public_key), - "labels": ssh_key.labels - }) - return ssh_keys - - def get_ssh_keys(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("fingerprint") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_fingerprint( - self.module.params.get("fingerprint") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_ssh_key_info = self.client.ssh_keys.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_ssh_key_info = self.client.ssh_keys.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - fingerprint={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudSSHKeyInfo.define_module() - - is_old_facts = module._name == 'hcloud_ssh_key_facts' - if is_old_facts: - module.deprecate("The 'hcloud_ssh_key_facts' module has been renamed to 'hcloud_ssh_key_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudSSHKeyInfo(module) - hcloud.get_ssh_keys() - result = hcloud.get_result() - - if is_old_facts: - ansible_info = { - 'hcloud_ssh_key_facts': result['hcloud_ssh_key_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_ssh_key_info': result['hcloud_ssh_key_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_info.py deleted file mode 100644 index aab98ed60..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_ssh_key_info.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_ssh_key_info -short_description: Gather infos about your Hetzner Cloud ssh_keys. -description: - - Gather facts about your Hetzner Cloud ssh_keys. - - This module was called C(hcloud_ssh_key_facts) before Ansible 2.9, returning C(ansible_facts) and C(hcloud_ssh_key_facts). - Note that the M(hetzner.hcloud.hcloud_ssh_key_info) module no longer returns C(ansible_facts) and the value was renamed to C(hcloud_ssh_key_info)! -author: - - Christopher Schmitt (@cschmitt-hcloud) -options: - id: - description: - - The ID of the ssh key you want to get. - type: int - name: - description: - - The name of the ssh key you want to get. - type: str - fingerprint: - description: - - The fingerprint of the ssh key you want to get. - type: str - label_selector: - description: - - The label selector for the ssh key you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud sshkey infos - hcloud_ssh_key_info: - register: output -- name: Print the gathered infos - debug: - var: output.hcloud_ssh_key_info -""" - -RETURN = """ -hcloud_ssh_key_info: - description: The ssh key instances - returned: Always - type: complex - contains: - id: - description: Numeric identifier of the ssh_key - returned: always - type: int - sample: 1937415 - name: - description: Name of the ssh_key - returned: always - type: str - sample: my-ssh-key - fingerprint: - description: Fingerprint of the ssh key - returned: always - type: str - sample: 0e:e0:bd:c7:2d:1f:69:49:94:44:91:f1:19:fd:35:f3 - public_key: - description: The actual public key - returned: always - type: str - sample: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGpl/tnk74nnQJxxLAtutUApUZMRJxryKh7VXkNbd4g9 john@example.com" - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudSSHKeyInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_ssh_key_info") - self.hcloud_ssh_key_info = None - - def _prepare_result(self): - ssh_keys = [] - - for ssh_key in self.hcloud_ssh_key_info: - if ssh_key: - ssh_keys.append({ - "id": to_native(ssh_key.id), - "name": to_native(ssh_key.name), - "fingerprint": to_native(ssh_key.fingerprint), - "public_key": to_native(ssh_key.public_key), - "labels": ssh_key.labels - }) - return ssh_keys - - def get_ssh_keys(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("fingerprint") is not None: - self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_fingerprint( - self.module.params.get("fingerprint") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_ssh_key_info = self.client.ssh_keys.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_ssh_key_info = self.client.ssh_keys.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - fingerprint={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudSSHKeyInfo.define_module() - - is_old_facts = module._name == 'hcloud_ssh_key_facts' - if is_old_facts: - module.deprecate("The 'hcloud_ssh_key_facts' module has been renamed to 'hcloud_ssh_key_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudSSHKeyInfo(module) - hcloud.get_ssh_keys() - result = hcloud.get_result() - - if is_old_facts: - ansible_info = { - 'hcloud_ssh_key_facts': result['hcloud_ssh_key_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_ssh_key_info': result['hcloud_ssh_key_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_subnetwork.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_subnetwork.py deleted file mode 100644 index c2ba66d80..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_subnetwork.py +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_subnetwork - -short_description: Manage cloud subnetworks on the Hetzner Cloud. - - -description: - - Create, update and delete cloud subnetworks on the Hetzner Cloud. - -author: - - Lukas Kaemmerling (@lkaemmerling) - -options: - network: - description: - - The ID or Name of the Hetzner Cloud Networks. - type: str - required: true - ip_range: - description: - - IP range of the subnetwork. - type: str - required: true - type: - description: - - Type of subnetwork. - type: str - choices: [ server, cloud, vswitch ] - required: true - network_zone: - description: - - Name of network zone. - type: str - required: true - vswitch_id: - description: - - ID of the vSwitch you want to couple with your Network. - - Required if type == vswitch - type: int - state: - description: - - State of the subnetwork. - default: present - choices: [ absent, present ] - type: str - -requirements: - - hcloud-python >= 1.10.0 - -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a basic subnetwork - hcloud_subnetwork: - network: my-network - ip_range: 10.0.0.0/16 - network_zone: eu-central - type: cloud - state: present - -- name: Create a basic subnetwork - hcloud_subnetwork: - network: my-vswitch-network - ip_range: 10.0.0.0/24 - network_zone: eu-central - type: vswitch - vswitch_id: 123 - state: present - -- name: Ensure the subnetwork is absent (remove if needed) - hcloud_subnetwork: - network: my-network - ip_range: 10.0.0.0/8 - network_zone: eu-central - type: cloud - state: absent -""" - -RETURN = """ -hcloud_subnetwork: - description: One Subnet of a Network - returned: always - type: complex - contains: - network: - description: Name of the Network - type: str - returned: always - sample: my-network - ip_range: - description: IP range of the Network - type: str - returned: always - sample: 10.0.0.0/8 - type: - description: Type of subnetwork - type: str - returned: always - sample: server - network_zone: - description: Name of network zone - type: str - returned: always - sample: eu-central - vswitch_id: - description: ID of the vswitch, null if not type vswitch - type: int - returned: always - sample: 123 - gateway: - description: Gateway of the subnetwork - type: str - returned: always - sample: 10.0.0.1 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - -try: - from hcloud.networks.domain import NetworkSubnet -except ImportError: - NetworkSubnet = None - - -class AnsibleHcloudSubnetwork(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_subnetwork") - self.hcloud_network = None - self.hcloud_subnetwork = None - - def _prepare_result(self): - return { - "network": to_native(self.hcloud_network.name), - "ip_range": to_native(self.hcloud_subnetwork.ip_range), - "type": to_native(self.hcloud_subnetwork.type), - "network_zone": to_native(self.hcloud_subnetwork.network_zone), - "gateway": self.hcloud_subnetwork.gateway, - "vswitch_id": self.hcloud_subnetwork.vswitch_id, - } - - def _get_network(self): - try: - self.hcloud_network = self.client.networks.get_by_name(self.module.params.get("network")) - self.hcloud_subnetwork = None - except Exception as e: - self.module.fail_json(msg=e.message) - - def _get_subnetwork(self): - subnet_ip_range = self.module.params.get("ip_range") - for subnetwork in self.hcloud_network.subnets: - if subnetwork.ip_range == subnet_ip_range: - self.hcloud_subnetwork = subnetwork - - def _create_subnetwork(self): - params = { - "ip_range": self.module.params.get("ip_range"), - "type": self.module.params.get('type'), - "network_zone": self.module.params.get('network_zone') - } - if self.module.params.get('type') == NetworkSubnet.TYPE_VSWITCH: - self.module.fail_on_missing_params( - required_params=["vswitch_id"] - ) - params["vswitch_id"] = self.module.params.get('vswitch_id') - - if not self.module.check_mode: - try: - self.hcloud_network.add_subnet(subnet=NetworkSubnet(**params)).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - - self._mark_as_changed() - self._get_network() - self._get_subnetwork() - - def present_subnetwork(self): - self._get_network() - self._get_subnetwork() - if self.hcloud_subnetwork is None: - self._create_subnetwork() - - def delete_subnetwork(self): - self._get_network() - self._get_subnetwork() - if self.hcloud_subnetwork is not None and self.hcloud_network is not None: - if not self.module.check_mode: - try: - self.hcloud_network.delete_subnet(self.hcloud_subnetwork).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self.hcloud_subnetwork = None - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - network={"type": "str", "required": True}, - network_zone={"type": "str", "required": True}, - type={ - "type": "str", - "required": True, - "choices": ["server", "cloud", "vswitch"] - }, - ip_range={"type": "str", "required": True}, - vswitch_id={"type": "int"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudSubnetwork.define_module() - - hcloud = AnsibleHcloudSubnetwork(module) - state = module.params["state"] - if state == "absent": - hcloud.delete_subnetwork() - elif state == "present": - hcloud.present_subnetwork() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume.py deleted file mode 100644 index 623a399b4..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume.py +++ /dev/null @@ -1,340 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_volume - -short_description: Create and manage block Volume on the Hetzner Cloud. - - -description: - - Create, update and attach/detach block Volume on the Hetzner Cloud. - -author: - - Christopher Schmitt (@cschmitt-hcloud) - -options: - id: - description: - - The ID of the Hetzner Cloud Block Volume to manage. - - Only required if no volume I(name) is given - type: int - name: - description: - - The Name of the Hetzner Cloud Block Volume to manage. - - Only required if no volume I(id) is given or a volume does not exist. - type: str - size: - description: - - The size of the Block Volume in GB. - - Required if volume does not yet exists. - type: int - automount: - description: - - Automatically mount the Volume. - type: bool - default: False - format: - description: - - Automatically Format the volume on creation - - Can only be used in case the Volume does not exist. - type: str - choices: [xfs, ext4] - location: - description: - - Location of the Hetzner Cloud Volume. - - Required if no I(server) is given and Volume does not exist. - type: str - server: - description: - - Server Name the Volume should be assigned to. - - Required if no I(location) is given and Volume does not exist. - type: str - delete_protection: - description: - - Protect the Volume for deletion. - type: bool - labels: - description: - - User-defined key-value pairs. - type: dict - state: - description: - - State of the Volume. - default: present - choices: [absent, present] - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Create a Volume - hcloud_volume: - name: my-volume - location: fsn1 - size: 100 - state: present -- name: Create a Volume and format it with ext4 - hcloud_volume: - name: my-volume - location: fsn - format: ext4 - size: 100 - state: present -- name: Mount a existing Volume and automount - hcloud_volume: - name: my-volume - server: my-server - automount: yes - state: present -- name: Mount a existing Volume and automount - hcloud_volume: - name: my-volume - server: my-server - automount: yes - state: present -- name: Ensure the Volume is absent (remove if needed) - hcloud_volume: - name: my-volume - state: absent -""" - -RETURN = """ -hcloud_volume: - description: The block Volume - returned: Always - type: complex - contains: - id: - description: ID of the Volume - type: int - returned: Always - sample: 12345 - name: - description: Name of the Volume - type: str - returned: Always - sample: my-volume - size: - description: Size in GB of the Volume - type: int - returned: Always - sample: 1337 - linux_device: - description: Path to the device that contains the Volume. - returned: always - type: str - sample: /dev/disk/by-id/scsi-0HC_Volume_12345 - version_added: "0.1.0" - location: - description: Location name where the Volume is located at - type: str - returned: Always - sample: "fsn1" - labels: - description: User-defined labels (key-value pairs) - type: dict - returned: Always - sample: - key: value - mylabel: 123 - server: - description: Server name where the Volume is attached to - type: str - returned: Always - sample: "my-server" - delete_protection: - description: True if Volume is protected for deletion - type: bool - returned: always - sample: false - version_added: "0.1.0" -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudVolume(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_volume") - self.hcloud_volume = None - - def _prepare_result(self): - server_name = None - if self.hcloud_volume.server is not None: - server_name = to_native(self.hcloud_volume.server.name) - - return { - "id": to_native(self.hcloud_volume.id), - "name": to_native(self.hcloud_volume.name), - "size": self.hcloud_volume.size, - "location": to_native(self.hcloud_volume.location.name), - "labels": self.hcloud_volume.labels, - "server": server_name, - "linux_device": to_native(self.hcloud_volume.linux_device), - "delete_protection": self.hcloud_volume.protection["delete"], - } - - def _get_volume(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_volume = self.client.volumes.get_by_id( - self.module.params.get("id") - ) - else: - self.hcloud_volume = self.client.volumes.get_by_name( - self.module.params.get("name") - ) - except Exception as e: - self.module.fail_json(msg=e.message) - - def _create_volume(self): - self.module.fail_on_missing_params( - required_params=["name", "size"] - ) - params = { - "name": self.module.params.get("name"), - "size": self.module.params.get("size"), - "automount": self.module.params.get("automount"), - "format": self.module.params.get("format"), - "labels": self.module.params.get("labels") - } - if self.module.params.get("server") is not None: - params['server'] = self.client.servers.get_by_name(self.module.params.get("server")) - elif self.module.params.get("location") is not None: - params['location'] = self.client.locations.get_by_name(self.module.params.get("location")) - else: - self.module.fail_json(msg="server or location is required") - - if not self.module.check_mode: - try: - resp = self.client.volumes.create(**params) - resp.action.wait_until_finished() - [action.wait_until_finished() for action in resp.next_actions] - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None: - self._get_volume() - self.hcloud_volume.change_protection(delete=delete_protection).wait_until_finished() - except Exception as e: - self.module.fail_json(msg=e.message) - self._mark_as_changed() - self._get_volume() - - def _update_volume(self): - try: - size = self.module.params.get("size") - if size: - if self.hcloud_volume.size < size: - if not self.module.check_mode: - self.hcloud_volume.resize(size).wait_until_finished() - self._mark_as_changed() - elif self.hcloud_volume.size > size: - self.module.warn("Shrinking of volumes is not supported") - - server_name = self.module.params.get("server") - if server_name: - server = self.client.servers.get_by_name(server_name) - if self.hcloud_volume.server is None or self.hcloud_volume.server.name != server.name: - if not self.module.check_mode: - automount = self.module.params.get("automount", False) - self.hcloud_volume.attach(server, automount=automount).wait_until_finished() - self._mark_as_changed() - else: - if self.hcloud_volume.server is not None: - if not self.module.check_mode: - self.hcloud_volume.detach().wait_until_finished() - self._mark_as_changed() - - labels = self.module.params.get("labels") - if labels is not None and labels != self.hcloud_volume.labels: - if not self.module.check_mode: - self.hcloud_volume.update(labels=labels) - self._mark_as_changed() - - delete_protection = self.module.params.get("delete_protection") - if delete_protection is not None and delete_protection != self.hcloud_volume.protection["delete"]: - if not self.module.check_mode: - self.hcloud_volume.change_protection(delete=delete_protection).wait_until_finished() - self._mark_as_changed() - - self._get_volume() - except Exception as e: - self.module.fail_json(msg=e.message) - - def present_volume(self): - self._get_volume() - if self.hcloud_volume is None: - self._create_volume() - else: - self._update_volume() - - def delete_volume(self): - try: - self._get_volume() - if self.hcloud_volume is not None: - if not self.module.check_mode: - if self.hcloud_volume.server is not None: - self.hcloud_volume.detach().wait_until_finished() - self.client.volumes.delete(self.hcloud_volume) - self._mark_as_changed() - self.hcloud_volume = None - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - size={"type": "int"}, - location={"type": "str"}, - server={"type": "str"}, - labels={"type": "dict"}, - automount={"type": "bool", "default": False}, - format={"type": "str", - "choices": ['xfs', 'ext4'], - }, - delete_protection={"type": "bool"}, - state={ - "choices": ["absent", "present"], - "default": "present", - }, - **Hcloud.base_module_arguments() - ), - required_one_of=[['id', 'name']], - mutually_exclusive=[["location", "server"]], - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudVolume.define_module() - - hcloud = AnsibleHcloudVolume(module) - state = module.params.get("state") - if state == "absent": - module.fail_on_missing_params( - required_params=["name"] - ) - hcloud.delete_volume() - else: - hcloud.present_volume() - - module.exit_json(**hcloud.get_result()) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_facts.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_facts.py deleted file mode 100644 index 9520bfa14..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_facts.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_volume_info - -short_description: Gather infos about your Hetzner Cloud Volumes. - -description: - - Gather infos about your Hetzner Cloud Volumes. - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the Volume you want to get. - type: int - name: - description: - - The name of the Volume you want to get. - type: str - label_selector: - description: - - The label selector for the Volume you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud Volume infos - hcloud_volume_info: - register: output -- name: Print the gathered infos - debug: - var: output.hcloud_volume_info -""" - -RETURN = """ -hcloud_volume_info: - description: The Volume infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the Volume - returned: always - type: int - sample: 1937415 - name: - description: Name of the Volume - returned: always - type: str - sample: my-volume - size: - description: Size of the Volume - returned: always - type: str - sample: 10 - linux_device: - description: Path to the device that contains the Volume. - returned: always - type: str - sample: /dev/disk/by-id/scsi-0HC_Volume_12345 - version_added: "0.1.0" - location: - description: Name of the location where the Volume resides in - returned: always - type: str - sample: fsn1 - server: - description: Name of the server where the Volume is attached to - returned: always - type: str - sample: my-server - delete_protection: - description: True if the Volume is protected for deletion - returned: always - type: bool - version_added: "0.1.0" - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudVolumeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_volume_info") - self.hcloud_volume_info = None - - def _prepare_result(self): - tmp = [] - - for volume in self.hcloud_volume_info: - if volume is not None: - server_name = None - if volume.server is not None: - server_name = to_native(volume.server.name) - tmp.append({ - "id": to_native(volume.id), - "name": to_native(volume.name), - "size": volume.size, - "location": to_native(volume.location.name), - "labels": volume.labels, - "server": server_name, - "linux_device": to_native(volume.linux_device), - "delete_protection": volume.protection["delete"], - }) - - return tmp - - def get_volumes(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_volume_info = [self.client.volumes.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_volume_info = [self.client.volumes.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_volume_info = self.client.volumes.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_volume_info = self.client.volumes.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudVolumeInfo.define_module() - - is_old_facts = module._name == 'hcloud_volume_facts' - if is_old_facts: - module.deprecate("The 'hcloud_volume_facts' module has been renamed to 'hcloud_volume_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudVolumeInfo(module) - - hcloud.get_volumes() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_volume_facts': result['hcloud_volume_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_volume_info': result['hcloud_volume_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_info.py deleted file mode 100644 index 9520bfa14..000000000 --- a/ansible_collections/hetzner/hcloud/plugins/modules/hcloud_volume_info.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -module: hcloud_volume_info - -short_description: Gather infos about your Hetzner Cloud Volumes. - -description: - - Gather infos about your Hetzner Cloud Volumes. - -author: - - Lukas Kaemmerling (@LKaemmerling) - -options: - id: - description: - - The ID of the Volume you want to get. - type: int - name: - description: - - The name of the Volume you want to get. - type: str - label_selector: - description: - - The label selector for the Volume you want to get. - type: str -extends_documentation_fragment: -- hetzner.hcloud.hcloud - -''' - -EXAMPLES = """ -- name: Gather hcloud Volume infos - hcloud_volume_info: - register: output -- name: Print the gathered infos - debug: - var: output.hcloud_volume_info -""" - -RETURN = """ -hcloud_volume_info: - description: The Volume infos as list - returned: always - type: complex - contains: - id: - description: Numeric identifier of the Volume - returned: always - type: int - sample: 1937415 - name: - description: Name of the Volume - returned: always - type: str - sample: my-volume - size: - description: Size of the Volume - returned: always - type: str - sample: 10 - linux_device: - description: Path to the device that contains the Volume. - returned: always - type: str - sample: /dev/disk/by-id/scsi-0HC_Volume_12345 - version_added: "0.1.0" - location: - description: Name of the location where the Volume resides in - returned: always - type: str - sample: fsn1 - server: - description: Name of the server where the Volume is attached to - returned: always - type: str - sample: my-server - delete_protection: - description: True if the Volume is protected for deletion - returned: always - type: bool - version_added: "0.1.0" - labels: - description: User-defined labels (key-value pairs) - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import Hcloud - - -class AnsibleHcloudVolumeInfo(Hcloud): - def __init__(self, module): - Hcloud.__init__(self, module, "hcloud_volume_info") - self.hcloud_volume_info = None - - def _prepare_result(self): - tmp = [] - - for volume in self.hcloud_volume_info: - if volume is not None: - server_name = None - if volume.server is not None: - server_name = to_native(volume.server.name) - tmp.append({ - "id": to_native(volume.id), - "name": to_native(volume.name), - "size": volume.size, - "location": to_native(volume.location.name), - "labels": volume.labels, - "server": server_name, - "linux_device": to_native(volume.linux_device), - "delete_protection": volume.protection["delete"], - }) - - return tmp - - def get_volumes(self): - try: - if self.module.params.get("id") is not None: - self.hcloud_volume_info = [self.client.volumes.get_by_id( - self.module.params.get("id") - )] - elif self.module.params.get("name") is not None: - self.hcloud_volume_info = [self.client.volumes.get_by_name( - self.module.params.get("name") - )] - elif self.module.params.get("label_selector") is not None: - self.hcloud_volume_info = self.client.volumes.get_all( - label_selector=self.module.params.get("label_selector")) - else: - self.hcloud_volume_info = self.client.volumes.get_all() - - except Exception as e: - self.module.fail_json(msg=e.message) - - @staticmethod - def define_module(): - return AnsibleModule( - argument_spec=dict( - id={"type": "int"}, - name={"type": "str"}, - label_selector={"type": "str"}, - **Hcloud.base_module_arguments() - ), - supports_check_mode=True, - ) - - -def main(): - module = AnsibleHcloudVolumeInfo.define_module() - - is_old_facts = module._name == 'hcloud_volume_facts' - if is_old_facts: - module.deprecate("The 'hcloud_volume_facts' module has been renamed to 'hcloud_volume_info', " - "and the renamed one no longer returns ansible_facts", version='2.0.0', collection_name="hetzner.hcloud") - - hcloud = AnsibleHcloudVolumeInfo(module) - - hcloud.get_volumes() - result = hcloud.get_result() - if is_old_facts: - ansible_info = { - 'hcloud_volume_facts': result['hcloud_volume_info'] - } - module.exit_json(ansible_facts=ansible_info) - else: - ansible_info = { - 'hcloud_volume_info': result['hcloud_volume_info'] - } - module.exit_json(**ansible_info) - - -if __name__ == "__main__": - main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/image_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/image_info.py new file mode 100644 index 000000000..b0d7fc482 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/image_info.py @@ -0,0 +1,209 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: image_info + +short_description: Gather infos about your Hetzner Cloud images. + + +description: + - Gather infos about your Hetzner Cloud images. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the image you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the image you want to get. + type: str + label_selector: + description: + - The label selector for the images you want to get. + type: str + type: + description: + - The type for the images you want to get. + default: system + choices: [ system, snapshot, backup ] + type: str + architecture: + description: + - The architecture for the images you want to get. + type: str + choices: [ x86, arm ] +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud image infos + hetzner.hcloud.image_info: + register: output + +- name: Print the gathered infos + debug: + var: output +""" + +RETURN = """ +hcloud_image_info: + description: The image infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the image + returned: always + type: int + sample: 1937415 + type: + description: Type of the image + returned: always + type: str + sample: system + status: + description: Status of the image + returned: always + type: str + sample: available + name: + description: Name of the image + returned: always + type: str + sample: ubuntu-22.04 + description: + description: Detail description of the image + returned: always + type: str + sample: Ubuntu 18.04 Standard 64 bit + os_flavor: + description: OS flavor of the image + returned: always + type: str + sample: ubuntu + os_version: + description: OS version of the image + returned: always + type: str + sample: 18.04 + architecture: + description: Image is compatible with this architecture + returned: always + type: str + sample: x86 + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.images import BoundImage + + +class AnsibleHCloudImageInfo(AnsibleHCloud): + represent = "hcloud_image_info" + + hcloud_image_info: list[BoundImage] | None = None + + def _prepare_result(self): + tmp = [] + + for image in self.hcloud_image_info: + if image is not None: + tmp.append( + { + "id": to_native(image.id), + "status": to_native(image.status), + "type": to_native(image.type), + "name": to_native(image.name), + "description": to_native(image.description), + "os_flavor": to_native(image.os_flavor), + "os_version": to_native(image.os_version), + "architecture": to_native(image.architecture), + "labels": image.labels, + } + ) + return tmp + + def get_images(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_image_info = [self.client.images.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None and self.module.params.get("architecture") is not None: + self.hcloud_image_info = [ + self.client.images.get_by_name_and_architecture( + self.module.params.get("name"), + self.module.params.get("architecture"), + ) + ] + elif self.module.params.get("name") is not None: + self.module.warn( + "This module only returns x86 images by default. Please set architecture:x86|arm to hide this message." + ) + self.hcloud_image_info = [self.client.images.get_by_name(self.module.params.get("name"))] + else: + params = {} + label_selector = self.module.params.get("label_selector") + if label_selector: + params["label_selector"] = label_selector + + image_type = self.module.params.get("type") + if image_type: + params["type"] = image_type + + architecture = self.module.params.get("architecture") + if architecture: + params["architecture"] = architecture + + self.hcloud_image_info = self.client.images.get_all(**params) + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + label_selector={"type": "str"}, + type={"choices": ["system", "snapshot", "backup"], "default": "system", "type": "str"}, + architecture={"choices": ["x86", "arm"], "type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudImageInfo.define_module() + hcloud = AnsibleHCloudImageInfo(module) + + hcloud.get_images() + result = hcloud.get_result() + + ansible_info = {"hcloud_image_info": result["hcloud_image_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py new file mode 100644 index 000000000..e623d1714 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/iso_info.py @@ -0,0 +1,206 @@ +#!/usr/bin/python + +# Copyright: (c) 2022, Patrice Le Guyader +# heavily inspired by the work of @LKaemmerling +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: iso_info + +short_description: Gather infos about the Hetzner Cloud ISO list. + +description: + - Gather infos about the Hetzner Cloud ISO list. + +author: + - Patrice Le Guyader (@patlegu) + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the ISO image you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the ISO you want to get. + type: str + architecture: + description: + - Filter ISOs with compatible architecture. + type: str + choices: [x86, arm] + include_architecture_wildcard: + description: + - Include ISOs with wildcard architecture (architecture is null). + - Works only if architecture filter is specified. + type: bool + +extends_documentation_fragment: + - hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Gather hcloud ISO type infos + hetzner.hcloud.iso_info: + register: output + +- name: Print the gathered infos + debug: + var: output.hcloud_iso_info +""" + +RETURN = """ +hcloud_iso_info: + description: The ISO type infos as list + returned: always + type: complex + contains: + id: + description: ID of the ISO + returned: always + type: int + sample: 22110 + name: + description: Unique identifier of the ISO. Only set for public ISOs + returned: always + type: str + sample: debian-12.0.0-amd64-netinst.iso + description: + description: Description of the ISO + returned: always + type: str + sample: Debian 12.0 (amd64/netinstall) + architecture: + description: > + Type of cpu architecture this ISO is compatible with. + None indicates no restriction on the architecture (wildcard). + returned: when supported + type: str + sample: x86 + type: + description: Type of the ISO, can be one of `public`, `private`. + returned: always + type: str + sample: public + deprecated: + description: > + ISO 8601 timestamp of deprecation, None if ISO is still available. + After the deprecation time it will no longer be possible to attach the + ISO to servers. This field is deprecated. Use `deprecation` instead. + returned: always + type: str + sample: "2024-12-01T00:00:00+00:00" + deprecation: + description: > + Describes if, when & how the resources was deprecated. If this field is + set to None the resource is not deprecated. If it has a value, it is + considered deprecated. + returned: if the resource is deprecated + type: dict + contains: + announced: + description: Date of when the deprecation was announced. + returned: always + type: str + sample: "2021-11-01T00:00:00+00:00" + unavailable_after: + description: > + After the time in this field, the resource will not be available + from the general listing endpoint of the resource type, and it can + not be used in new resources. For example, if this is an image, + you can not create new servers with this image after the mentioned + date. + returned: always + type: str + sample: "2021-12-01T00:00:00+00:00" +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.isos import BoundIso + + +class AnsibleHCloudIsoInfo(AnsibleHCloud): + represent = "hcloud_iso_info" + + hcloud_iso_info: list[BoundIso] | None = None + + def _prepare_result(self): + tmp = [] + + for iso_info in self.hcloud_iso_info: + if iso_info is None: + continue + + tmp.append( + { + "id": to_native(iso_info.id), + "name": to_native(iso_info.name), + "description": to_native(iso_info.description), + "type": iso_info.type, + "architecture": iso_info.architecture, + "deprecated": ( + iso_info.deprecation.unavailable_after if iso_info.deprecation is not None else None + ), + "deprecation": ( + { + "announced": iso_info.deprecation.announced.isoformat(), + "unavailable_after": iso_info.deprecation.unavailable_after.isoformat(), + } + if iso_info.deprecation is not None + else None + ), + } + ) + + return tmp + + def get_iso_infos(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_iso_info = [self.client.isos.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_iso_info = [self.client.isos.get_by_name(self.module.params.get("name"))] + else: + self.hcloud_iso_info = self.client.isos.get_all( + architecture=self.module.params.get("architecture"), + include_wildcard_architecture=self.module.params.get("include_wildcard_architecture"), + ) + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + architecture={"type": "str", "choices": ["x86", "arm"]}, + include_architecture_wildcard={"type": "bool"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudIsoInfo.define_module() + hcloud = AnsibleHCloudIsoInfo(module) + hcloud.get_iso_infos() + result = hcloud.get_result() + ansible_info = {"hcloud_iso_info": result["hcloud_iso_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py new file mode 100644 index 000000000..1a0d8712a --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer.py @@ -0,0 +1,340 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: load_balancer + +short_description: Create and manage cloud Load Balancers on the Hetzner Cloud. + +description: + - Create, update and manage cloud Load Balancers on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@LKaemmerling) +version_added: 0.1.0 +options: + id: + description: + - The ID of the Hetzner Cloud Load Balancer to manage. + - Only required if no Load Balancer I(name) is given + type: int + name: + description: + - The Name of the Hetzner Cloud Load Balancer to manage. + - Only required if no Load Balancer I(id) is given or a Load Balancer does not exist. + type: str + load_balancer_type: + description: + - The Load Balancer Type of the Hetzner Cloud Load Balancer to manage. + - Required if Load Balancer does not exist. + type: str + algorithm: + description: + - Algorithm of the Load Balancer. + type: str + default: round_robin + choices: [round_robin, least_connections] + location: + description: + - Location of Load Balancer. + - Required if no I(network_zone) is given and Load Balancer does not exist. + type: str + network_zone: + description: + - Network Zone of Load Balancer. + - Required of no I(location) is given and Load Balancer does not exist. + type: str + labels: + description: + - User-defined labels (key-value pairs). + type: dict + disable_public_interface: + description: + - Disables the public interface. + type: bool + default: False + delete_protection: + description: + - Protect the Load Balancer for deletion. + type: bool + state: + description: + - State of the Load Balancer. + default: present + choices: [ absent, present ] + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic Load Balancer + hetzner.hcloud.load_balancer: + name: my-Load Balancer + load_balancer_type: lb11 + algorithm: round_robin + location: fsn1 + state: present + +- name: Ensure the Load Balancer is absent (remove if needed) + hetzner.hcloud.load_balancer: + name: my-Load Balancer + state: absent +""" + +RETURN = """ +hcloud_load_balancer: + description: The Load Balancer instance + returned: Always + type: complex + contains: + id: + description: Numeric identifier of the Load Balancer + returned: always + type: int + sample: 1937415 + name: + description: Name of the Load Balancer + returned: always + type: str + sample: my-Load-Balancer + status: + description: Status of the Load Balancer + returned: always + type: str + sample: running + load_balancer_type: + description: Name of the Load Balancer type of the Load Balancer + returned: always + type: str + sample: cx11 + algorithm: + description: Algorithm of the Load Balancer. + returned: always + type: str + choices: [round_robin, least_connections] + sample: round_robin + ipv4_address: + description: Public IPv4 address of the Load Balancer + returned: always + type: str + sample: 116.203.104.109 + ipv6_address: + description: Public IPv6 address of the Load Balancer + returned: always + type: str + sample: 2a01:4f8:1c1c:c140::1 + location: + description: Name of the location of the Load Balancer + returned: always + type: str + sample: fsn1 + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict + delete_protection: + description: True if Load Balancer is protected for deletion + type: bool + returned: always + sample: false + disable_public_interface: + description: True if Load Balancer public interface is disabled + type: bool + returned: always + sample: false +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.load_balancers import ( + BoundLoadBalancer, + LoadBalancerAlgorithm, +) + + +class AnsibleHCloudLoadBalancer(AnsibleHCloud): + represent = "hcloud_load_balancer" + + hcloud_load_balancer: BoundLoadBalancer | None = None + + def _prepare_result(self): + private_ipv4_address = ( + None + if len(self.hcloud_load_balancer.private_net) == 0 + else to_native(self.hcloud_load_balancer.private_net[0].ip) + ) + return { + "id": to_native(self.hcloud_load_balancer.id), + "name": to_native(self.hcloud_load_balancer.name), + "ipv4_address": to_native(self.hcloud_load_balancer.public_net.ipv4.ip), + "ipv6_address": to_native(self.hcloud_load_balancer.public_net.ipv6.ip), + "private_ipv4_address": private_ipv4_address, + "load_balancer_type": to_native(self.hcloud_load_balancer.load_balancer_type.name), + "algorithm": to_native(self.hcloud_load_balancer.algorithm.type), + "location": to_native(self.hcloud_load_balancer.location.name), + "labels": self.hcloud_load_balancer.labels, + "delete_protection": self.hcloud_load_balancer.protection["delete"], + "disable_public_interface": False if self.hcloud_load_balancer.public_net.enabled else True, + } + + def _get_load_balancer(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_load_balancer = self.client.load_balancers.get_by_id(self.module.params.get("id")) + else: + self.hcloud_load_balancer = self.client.load_balancers.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_load_balancer(self): + self.module.fail_on_missing_params(required_params=["name", "load_balancer_type"]) + try: + params = { + "name": self.module.params.get("name"), + "algorithm": LoadBalancerAlgorithm(type=self.module.params.get("algorithm", "round_robin")), + "load_balancer_type": self.client.load_balancer_types.get_by_name( + self.module.params.get("load_balancer_type") + ), + "labels": self.module.params.get("labels"), + } + + if self.module.params.get("location") is None and self.module.params.get("network_zone") is None: + self.module.fail_json(msg="one of the following is required: location, network_zone") + elif self.module.params.get("location") is not None and self.module.params.get("network_zone") is None: + params["location"] = self.client.locations.get_by_name(self.module.params.get("location")) + elif self.module.params.get("location") is None and self.module.params.get("network_zone") is not None: + params["network_zone"] = self.module.params.get("network_zone") + + if not self.module.check_mode: + resp = self.client.load_balancers.create(**params) + resp.action.wait_until_finished(max_retries=1000) + + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None: + self._get_load_balancer() + self.hcloud_load_balancer.change_protection(delete=delete_protection).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_load_balancer() + + def _update_load_balancer(self): + try: + labels = self.module.params.get("labels") + if labels is not None and labels != self.hcloud_load_balancer.labels: + if not self.module.check_mode: + self.hcloud_load_balancer.update(labels=labels) + self._mark_as_changed() + + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None and delete_protection != self.hcloud_load_balancer.protection["delete"]: + if not self.module.check_mode: + self.hcloud_load_balancer.change_protection(delete=delete_protection).wait_until_finished() + self._mark_as_changed() + self._get_load_balancer() + + disable_public_interface = self.module.params.get("disable_public_interface") + if disable_public_interface is not None and disable_public_interface != ( + not self.hcloud_load_balancer.public_net.enabled + ): + if not self.module.check_mode: + if disable_public_interface is True: + self.hcloud_load_balancer.disable_public_interface().wait_until_finished() + else: + self.hcloud_load_balancer.enable_public_interface().wait_until_finished() + self._mark_as_changed() + + load_balancer_type = self.module.params.get("load_balancer_type") + if ( + load_balancer_type is not None + and self.hcloud_load_balancer.load_balancer_type.name != load_balancer_type + ): + new_load_balancer_type = self.client.load_balancer_types.get_by_name(load_balancer_type) + if not new_load_balancer_type: + self.module.fail_json(msg="unknown load balancer type") + if not self.module.check_mode: + self.hcloud_load_balancer.change_type( + load_balancer_type=new_load_balancer_type, + ).wait_until_finished(max_retries=1000) + + self._mark_as_changed() + + algorithm = self.module.params.get("algorithm") + if algorithm is not None and self.hcloud_load_balancer.algorithm.type != algorithm: + self.hcloud_load_balancer.change_algorithm( + algorithm=LoadBalancerAlgorithm(type=algorithm) + ).wait_until_finished() + self._mark_as_changed() + + self._get_load_balancer() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def present_load_balancer(self): + self._get_load_balancer() + if self.hcloud_load_balancer is None: + self._create_load_balancer() + else: + self._update_load_balancer() + + def delete_load_balancer(self): + try: + self._get_load_balancer() + if self.hcloud_load_balancer is not None: + if not self.module.check_mode: + self.client.load_balancers.delete(self.hcloud_load_balancer) + self._mark_as_changed() + self.hcloud_load_balancer = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + load_balancer_type={"type": "str"}, + algorithm={"choices": ["round_robin", "least_connections"], "default": "round_robin"}, + location={"type": "str"}, + network_zone={"type": "str"}, + labels={"type": "dict"}, + delete_protection={"type": "bool"}, + disable_public_interface={"type": "bool", "default": False}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name"]], + mutually_exclusive=[["location", "network_zone"]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudLoadBalancer.define_module() + + hcloud = AnsibleHCloudLoadBalancer(module) + state = module.params.get("state") + if state == "absent": + hcloud.delete_load_balancer() + elif state == "present": + hcloud.present_load_balancer() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py new file mode 100644 index 000000000..19ead98c2 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_info.py @@ -0,0 +1,424 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: load_balancer_info + +short_description: Gather infos about your Hetzner Cloud Load Balancers. + + +description: + - Gather infos about your Hetzner Cloud Load Balancers.. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the Load Balancers you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the Load Balancers you want to get. + type: str + label_selector: + description: + - The label selector for the Load Balancers you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud load_balancer infos + hetzner.hcloud.load_balancer_info: + register: output + +- name: Print the gathered infos + debug: + var: output +""" + +RETURN = """ +hcloud_load_balancer_info: + description: The load_balancer infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the Load Balancer + returned: always + type: int + sample: 1937415 + name: + description: Name of the Load Balancer + returned: always + type: str + sample: my-Load-Balancer + status: + description: Status of the Load Balancer + returned: always + type: str + sample: running + load_balancer_type: + description: Name of the Load Balancer type of the Load Balancer + returned: always + type: str + sample: cx11 + ipv4_address: + description: Public IPv4 address of the Load Balancer + returned: always + type: str + sample: 116.203.104.109 + ipv6_address: + description: Public IPv6 address of the Load Balancer + returned: always + type: str + sample: 2a01:4f8:1c1c:c140::1 + location: + description: Name of the location of the Load Balancer + returned: always + type: str + sample: fsn1 + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict + delete_protection: + description: True if Load Balancer is protected for deletion + type: bool + returned: always + sample: false + disable_public_interface: + description: True if Load Balancer public interface is disabled + type: bool + returned: always + sample: false + targets: + description: The targets of the Load Balancer + returned: always + type: complex + contains: + type: + description: Type of the Load Balancer Target + type: str + returned: always + sample: server + load_balancer: + description: Name of the Load Balancer + type: str + returned: always + sample: my-LoadBalancer + server: + description: Name of the Server + type: str + returned: if I(type) is server + sample: my-server + label_selector: + description: Label Selector + type: str + returned: if I(type) is label_selector + sample: application=backend + ip: + description: IP of the dedicated server + type: str + returned: if I(type) is ip + sample: 127.0.0.1 + use_private_ip: + description: + - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. + - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.load_balancer_network) + type: bool + sample: true + returned: always + health_status: + description: + - List of health statuses of the services on this target. Only present for target types "server" and "ip". + type: list + returned: if I(type) is server or ip + contains: + listen_port: + description: Load Balancer Target listen port + type: int + returned: always + sample: 80 + status: + description: Load Balancer Target status + type: str + choices: [healthy, unhealthy, unknown] + returned: always + sample: healthy + services: + description: all services from this Load Balancer + returned: Always + type: complex + contains: + listen_port: + description: The port the service listens on, i.e. the port users can connect to. + returned: always + type: int + sample: 443 + protocol: + description: Protocol of the service + returned: always + type: str + sample: http + destination_port: + description: + - The port traffic is forwarded to, i.e. the port the targets are listening and accepting connections on. + returned: always + type: int + sample: 80 + proxyprotocol: + description: + - Enable the PROXY protocol. + returned: always + type: bool + sample: false + http: + description: Configuration for HTTP and HTTPS services + returned: always + type: complex + contains: + cookie_name: + description: Name of the cookie which will be set when you enable sticky sessions + returned: always + type: str + sample: HCLBSTICKY + cookie_lifetime: + description: Lifetime of the cookie which will be set when you enable sticky sessions, in seconds + returned: always + type: int + sample: 3600 + certificates: + description: List of Names or IDs of certificates + returned: always + type: list + elements: str + sticky_sessions: + description: Enable or disable sticky_sessions + returned: always + type: bool + sample: true + redirect_http: + description: Redirect Traffic from Port 80 to Port 443, only available if protocol is https + returned: always + type: bool + sample: false + health_check: + description: Configuration for health checks + returned: always + type: complex + contains: + protocol: + description: Protocol the health checks will be performed over + returned: always + type: str + sample: http + port: + description: Port the health check will be performed on + returned: always + type: int + sample: 80 + interval: + description: Interval of health checks, in seconds + returned: always + type: int + sample: 15 + timeout: + description: Timeout of health checks, in seconds + returned: always + type: int + sample: 10 + retries: + description: Number of retries until a target is marked as unhealthy + returned: always + type: int + sample: 3 + http: + description: Additional Configuration of health checks with protocol http/https + returned: always + type: complex + contains: + domain: + description: Domain we will set within the HTTP HOST header + returned: always + type: str + sample: example.com + path: + description: Path we will try to access + returned: always + type: str + sample: / + response: + description: Response we expect, if response is not within the health check response the target is unhealthy + returned: always + type: str + status_codes: + description: List of HTTP status codes we expect to get when we perform the health check. + returned: always + type: list + elements: str + sample: ["2??","3??"] + tls: + description: Verify the TLS certificate, only available if health check protocol is https + returned: always + type: bool + sample: false +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.load_balancers import BoundLoadBalancer + + +class AnsibleHCloudLoadBalancerInfo(AnsibleHCloud): + represent = "hcloud_load_balancer_info" + + hcloud_load_balancer_info: list[BoundLoadBalancer] | None = None + + def _prepare_result(self): + tmp = [] + + for load_balancer in self.hcloud_load_balancer_info: + if load_balancer is not None: + services = [self._prepare_service_result(service) for service in load_balancer.services] + targets = [self._prepare_target_result(target) for target in load_balancer.targets] + + private_ipv4_address = ( + None if len(load_balancer.private_net) == 0 else to_native(load_balancer.private_net[0].ip) + ) + tmp.append( + { + "id": to_native(load_balancer.id), + "name": to_native(load_balancer.name), + "ipv4_address": to_native(load_balancer.public_net.ipv4.ip), + "ipv6_address": to_native(load_balancer.public_net.ipv6.ip), + "private_ipv4_address": private_ipv4_address, + "load_balancer_type": to_native(load_balancer.load_balancer_type.name), + "location": to_native(load_balancer.location.name), + "labels": load_balancer.labels, + "delete_protection": load_balancer.protection["delete"], + "disable_public_interface": False if load_balancer.public_net.enabled else True, + "targets": targets, + "services": services, + } + ) + return tmp + + @staticmethod + def _prepare_service_result(service): + http = None + if service.protocol != "tcp": + http = { + "cookie_name": to_native(service.http.cookie_name), + "cookie_lifetime": service.http.cookie_name, + "redirect_http": service.http.redirect_http, + "sticky_sessions": service.http.sticky_sessions, + "certificates": [to_native(certificate.name) for certificate in service.http.certificates], + } + health_check = { + "protocol": to_native(service.health_check.protocol), + "port": service.health_check.port, + "interval": service.health_check.interval, + "timeout": service.health_check.timeout, + "retries": service.health_check.retries, + } + if service.health_check.protocol != "tcp": + health_check["http"] = { + "domain": to_native(service.health_check.http.domain), + "path": to_native(service.health_check.http.path), + "response": to_native(service.health_check.http.response), + "certificates": [to_native(status_code) for status_code in service.health_check.http.status_codes], + "tls": service.health_check.http.tls, + } + return { + "protocol": to_native(service.protocol), + "listen_port": service.listen_port, + "destination_port": service.destination_port, + "proxyprotocol": service.proxyprotocol, + "http": http, + "health_check": health_check, + } + + @staticmethod + def _prepare_target_result(target): + result = { + "type": to_native(target.type), + "use_private_ip": target.use_private_ip, + } + if target.type == "server": + result["server"] = to_native(target.server.name) + elif target.type == "label_selector": + result["label_selector"] = to_native(target.label_selector.selector) + elif target.type == "ip": + result["ip"] = to_native(target.ip.ip) + + if target.health_status is not None: + result["health_status"] = [ + { + "listen_port": item.listen_port, + "status": item.status, + } + for item in target.health_status + ] + + return result + + def get_load_balancers(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_load_balancer_info = [self.client.load_balancers.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_load_balancer_info = [ + self.client.load_balancers.get_by_name(self.module.params.get("name")) + ] + else: + params = {} + label_selector = self.module.params.get("label_selector") + if label_selector: + params["label_selector"] = label_selector + + self.hcloud_load_balancer_info = self.client.load_balancers.get_all(**params) + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + label_selector={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudLoadBalancerInfo.define_module() + hcloud = AnsibleHCloudLoadBalancerInfo(module) + + hcloud.get_load_balancers() + result = hcloud.get_result() + + ansible_info = {"hcloud_load_balancer_info": result["hcloud_load_balancer_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py new file mode 100644 index 000000000..4560f8735 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_network.py @@ -0,0 +1,203 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: load_balancer_network + +short_description: Manage the relationship between Hetzner Cloud Networks and Load Balancers + + +description: + - Create and delete the relationship Hetzner Cloud Networks and Load Balancers + +author: + - Lukas Kaemmerling (@lkaemmerling) +version_added: 0.1.0 +options: + network: + description: + - Name or ID of the Hetzner Cloud Networks. + type: str + required: true + load_balancer: + description: + - Name or ID of the Hetzner Cloud Load Balancer. + type: str + required: true + ip: + description: + - The IP the Load Balancer should have. + type: str + state: + description: + - State of the load_balancer_network. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic Load Balancer network + hetzner.hcloud.load_balancer_network: + network: my-network + load_balancer: my-LoadBalancer + state: present + +- name: Create a Load Balancer network and specify the ip address + hetzner.hcloud.load_balancer_network: + network: my-network + load_balancer: my-LoadBalancer + ip: 10.0.0.1 + state: present + +- name: Ensure the Load Balancer network is absent (remove if needed) + hetzner.hcloud.load_balancer_network: + network: my-network + load_balancer: my-LoadBalancer + state: absent +""" + +RETURN = """ +hcloud_load_balancer_network: + description: The relationship between a Load Balancer and a network + returned: always + type: complex + contains: + network: + description: Name of the Network + type: str + returned: always + sample: my-network + load_balancer: + description: Name of the Load Balancer + type: str + returned: always + sample: my-LoadBalancer + ip: + description: IP of the Load Balancer within the Network ip range + type: str + returned: always + sample: 10.0.0.8 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.load_balancers import BoundLoadBalancer, PrivateNet +from ..module_utils.vendor.hcloud.networks import BoundNetwork + + +class AnsibleHCloudLoadBalancerNetwork(AnsibleHCloud): + represent = "hcloud_load_balancer_network" + + hcloud_network: BoundNetwork | None = None + hcloud_load_balancer: BoundLoadBalancer | None = None + hcloud_load_balancer_network: PrivateNet | None = None + + def _prepare_result(self): + return { + "network": to_native(self.hcloud_network.name), + "load_balancer": to_native(self.hcloud_load_balancer.name), + "ip": to_native(self.hcloud_load_balancer_network.ip), + } + + def _get_load_balancer_and_network(self): + try: + self.hcloud_network = self._client_get_by_name_or_id( + "networks", + self.module.params.get("network"), + ) + self.hcloud_load_balancer = self._client_get_by_name_or_id( + "load_balancers", + self.module.params.get("load_balancer"), + ) + + self.hcloud_load_balancer_network = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _get_load_balancer_network(self): + for private_net in self.hcloud_load_balancer.private_net: + if private_net.network.id == self.hcloud_network.id: + self.hcloud_load_balancer_network = private_net + + def _create_load_balancer_network(self): + params = {"network": self.hcloud_network} + + if self.module.params.get("ip") is not None: + params["ip"] = self.module.params.get("ip") + + if not self.module.check_mode: + try: + self.hcloud_load_balancer.attach_to_network(**params).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + self._mark_as_changed() + self._get_load_balancer_and_network() + self._get_load_balancer_network() + + def present_load_balancer_network(self): + self._get_load_balancer_and_network() + self._get_load_balancer_network() + if self.hcloud_load_balancer_network is None: + self._create_load_balancer_network() + + def delete_load_balancer_network(self): + self._get_load_balancer_and_network() + self._get_load_balancer_network() + if self.hcloud_load_balancer_network is not None and self.hcloud_load_balancer is not None: + if not self.module.check_mode: + try: + self.hcloud_load_balancer.detach_from_network( + self.hcloud_load_balancer_network.network + ).wait_until_finished() + self._mark_as_changed() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + self.hcloud_load_balancer_network = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + network={"type": "str", "required": True}, + load_balancer={"type": "str", "required": True}, + ip={"type": "str"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudLoadBalancerNetwork.define_module() + + hcloud = AnsibleHCloudLoadBalancerNetwork(module) + state = module.params["state"] + if state == "absent": + hcloud.delete_load_balancer_network() + elif state == "present": + hcloud.present_load_balancer_network() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py new file mode 100644 index 000000000..1fc18deef --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_service.py @@ -0,0 +1,578 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: load_balancer_service + +short_description: Create and manage the services of cloud Load Balancers on the Hetzner Cloud. + + +description: + - Create, update and manage the services of cloud Load Balancers on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@LKaemmerling) +version_added: 0.1.0 +options: + load_balancer: + description: + - Name or ID of the Hetzner Cloud Load Balancer the service belongs to + type: str + required: true + listen_port: + description: + - The port the service listens on, i.e. the port users can connect to. + type: int + required: true + destination_port: + description: + - The port traffic is forwarded to, i.e. the port the targets are listening and accepting connections on. + - Required if services does not exist and protocol is tcp. + type: int + protocol: + description: + - Protocol of the service. + - Required if Load Balancer does not exist. + type: str + choices: [ http, https, tcp ] + proxyprotocol: + description: + - Enable the PROXY protocol. + type: bool + default: False + http: + description: + - Configuration for HTTP and HTTPS services + type: dict + suboptions: + cookie_name: + description: + - Name of the cookie which will be set when you enable sticky sessions + type: str + cookie_lifetime: + description: + - Lifetime of the cookie which will be set when you enable sticky sessions, in seconds + type: int + certificates: + description: + - List of Names or IDs of certificates + type: list + elements: str + sticky_sessions: + description: + - Enable or disable sticky_sessions + type: bool + default: False + redirect_http: + description: + - Redirect Traffic from Port 80 to Port 443, only available if protocol is https + type: bool + default: False + health_check: + description: + - Configuration for health checks + type: dict + suboptions: + protocol: + description: + - Protocol the health checks will be performed over + type: str + choices: [ http, https, tcp ] + port: + description: + - Port the health check will be performed on + type: int + interval: + description: + - Interval of health checks, in seconds + type: int + timeout: + description: + - Timeout of health checks, in seconds + type: int + retries: + description: + - Number of retries until a target is marked as unhealthy + type: int + http: + description: + - Additional Configuration of health checks with protocol http/https + type: dict + suboptions: + domain: + description: + - Domain we will set within the HTTP HOST header + type: str + path: + description: + - Path we will try to access + type: str + response: + description: + - Response we expect, if response is not within the health check response the target is unhealthy + type: str + status_codes: + description: + - List of HTTP status codes we expect to get when we perform the health check. + type: list + elements: str + tls: + description: + - Verify the TLS certificate, only available if health check protocol is https + type: bool + default: False + state: + description: + - State of the Load Balancer. + default: present + choices: [ absent, present ] + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic Load Balancer service with Port 80 + hetzner.hcloud.load_balancer_service: + load_balancer: my-load-balancer + protocol: http + listen_port: 80 + state: present + +- name: Ensure the Load Balancer is absent (remove if needed) + hetzner.hcloud.load_balancer_service: + load_balancer: my-Load Balancer + protocol: http + listen_port: 80 + state: absent +""" + +RETURN = """ +hcloud_load_balancer_service: + description: The Load Balancer service instance + returned: Always + type: complex + contains: + load_balancer: + description: The name of the Load Balancer where the service belongs to + returned: always + type: str + sample: my-load-balancer + listen_port: + description: The port the service listens on, i.e. the port users can connect to. + returned: always + type: int + sample: 443 + protocol: + description: Protocol of the service + returned: always + type: str + sample: http + destination_port: + description: + - The port traffic is forwarded to, i.e. the port the targets are listening and accepting connections on. + returned: always + type: int + sample: 80 + proxyprotocol: + description: + - Enable the PROXY protocol. + returned: always + type: bool + sample: false + http: + description: Configuration for HTTP and HTTPS services + returned: always + type: complex + contains: + cookie_name: + description: Name of the cookie which will be set when you enable sticky sessions + returned: always + type: str + sample: HCLBSTICKY + cookie_lifetime: + description: Lifetime of the cookie which will be set when you enable sticky sessions, in seconds + returned: always + type: int + sample: 3600 + certificates: + description: List of Names or IDs of certificates + returned: always + type: list + elements: str + sticky_sessions: + description: Enable or disable sticky_sessions + returned: always + type: bool + sample: true + redirect_http: + description: Redirect Traffic from Port 80 to Port 443, only available if protocol is https + returned: always + type: bool + sample: false + health_check: + description: Configuration for health checks + returned: always + type: complex + contains: + protocol: + description: Protocol the health checks will be performed over + returned: always + type: str + sample: http + port: + description: Port the health check will be performed on + returned: always + type: int + sample: 80 + interval: + description: Interval of health checks, in seconds + returned: always + type: int + sample: 15 + timeout: + description: Timeout of health checks, in seconds + returned: always + type: int + sample: 10 + retries: + description: Number of retries until a target is marked as unhealthy + returned: always + type: int + sample: 3 + http: + description: Additional Configuration of health checks with protocol http/https + returned: always + type: complex + contains: + domain: + description: Domain we will set within the HTTP HOST header + returned: always + type: str + sample: example.com + path: + description: Path we will try to access + returned: always + type: str + sample: / + response: + description: Response we expect, if response is not within the health check response the target is unhealthy + returned: always + type: str + status_codes: + description: List of HTTP status codes we expect to get when we perform the health check. + returned: always + type: list + elements: str + sample: ["2??","3??"] + tls: + description: Verify the TLS certificate, only available if health check protocol is https + returned: always + type: bool + sample: false +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import APIException, HCloudException +from ..module_utils.vendor.hcloud.load_balancers import ( + BoundLoadBalancer, + LoadBalancerHealtCheckHttp, + LoadBalancerHealthCheck, + LoadBalancerService, + LoadBalancerServiceHttp, +) + + +class AnsibleHCloudLoadBalancerService(AnsibleHCloud): + represent = "hcloud_load_balancer_service" + + hcloud_load_balancer: BoundLoadBalancer | None = None + hcloud_load_balancer_service: LoadBalancerService | None = None + + def _prepare_result(self): + http = None + if self.hcloud_load_balancer_service.protocol != "tcp": + http = { + "cookie_name": to_native(self.hcloud_load_balancer_service.http.cookie_name), + "cookie_lifetime": self.hcloud_load_balancer_service.http.cookie_name, + "redirect_http": self.hcloud_load_balancer_service.http.redirect_http, + "sticky_sessions": self.hcloud_load_balancer_service.http.sticky_sessions, + "certificates": [ + to_native(certificate.name) for certificate in self.hcloud_load_balancer_service.http.certificates + ], + } + health_check = { + "protocol": to_native(self.hcloud_load_balancer_service.health_check.protocol), + "port": self.hcloud_load_balancer_service.health_check.port, + "interval": self.hcloud_load_balancer_service.health_check.interval, + "timeout": self.hcloud_load_balancer_service.health_check.timeout, + "retries": self.hcloud_load_balancer_service.health_check.retries, + } + if self.hcloud_load_balancer_service.health_check.protocol != "tcp": + health_check["http"] = { + "domain": to_native(self.hcloud_load_balancer_service.health_check.http.domain), + "path": to_native(self.hcloud_load_balancer_service.health_check.http.path), + "response": to_native(self.hcloud_load_balancer_service.health_check.http.response), + "status_codes": [ + to_native(status_code) + for status_code in self.hcloud_load_balancer_service.health_check.http.status_codes + ], + "tls": self.hcloud_load_balancer_service.health_check.http.tls, + } + return { + "load_balancer": to_native(self.hcloud_load_balancer.name), + "protocol": to_native(self.hcloud_load_balancer_service.protocol), + "listen_port": self.hcloud_load_balancer_service.listen_port, + "destination_port": self.hcloud_load_balancer_service.destination_port, + "proxyprotocol": self.hcloud_load_balancer_service.proxyprotocol, + "http": http, + "health_check": health_check, + } + + def _get_load_balancer(self): + try: + self.hcloud_load_balancer = self._client_get_by_name_or_id( + "load_balancers", + self.module.params.get("load_balancer"), + ) + self._get_load_balancer_service() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_load_balancer_service(self): + self.module.fail_on_missing_params(required_params=["protocol"]) + if self.module.params.get("protocol") == "tcp": + self.module.fail_on_missing_params(required_params=["destination_port"]) + + params = { + "protocol": self.module.params.get("protocol"), + "listen_port": self.module.params.get("listen_port"), + "proxyprotocol": self.module.params.get("proxyprotocol"), + } + + if self.module.params.get("destination_port"): + params["destination_port"] = self.module.params.get("destination_port") + + if self.module.params.get("http"): + params["http"] = self.__get_service_http(http_arg=self.module.params.get("http")) + + if self.module.params.get("health_check"): + params["health_check"] = self.__get_service_health_checks( + health_check=self.module.params.get("health_check") + ) + + if not self.module.check_mode: + try: + self.hcloud_load_balancer.add_service(LoadBalancerService(**params)).wait_until_finished( + max_retries=1000 + ) + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_load_balancer() + self._get_load_balancer_service() + + def __get_service_http(self, http_arg): + service_http = LoadBalancerServiceHttp(certificates=[]) + if http_arg.get("cookie_name") is not None: + service_http.cookie_name = http_arg.get("cookie_name") + if http_arg.get("cookie_lifetime") is not None: + service_http.cookie_lifetime = http_arg.get("cookie_lifetime") + if http_arg.get("sticky_sessions") is not None: + service_http.sticky_sessions = http_arg.get("sticky_sessions") + if http_arg.get("redirect_http") is not None: + service_http.redirect_http = http_arg.get("redirect_http") + if http_arg.get("certificates") is not None: + certificates = http_arg.get("certificates") + if certificates is not None: + for certificate in certificates: + hcloud_cert = None + try: + try: + hcloud_cert = self.client.certificates.get_by_name(certificate) + except Exception: + hcloud_cert = self.client.certificates.get_by_id(certificate) + except HCloudException as exception: + self.fail_json_hcloud(exception) + service_http.certificates.append(hcloud_cert) + + return service_http + + def __get_service_health_checks(self, health_check): + service_health_check = LoadBalancerHealthCheck() + if health_check.get("protocol") is not None: + service_health_check.protocol = health_check.get("protocol") + if health_check.get("port") is not None: + service_health_check.port = health_check.get("port") + if health_check.get("interval") is not None: + service_health_check.interval = health_check.get("interval") + if health_check.get("timeout") is not None: + service_health_check.timeout = health_check.get("timeout") + if health_check.get("retries") is not None: + service_health_check.retries = health_check.get("retries") + if health_check.get("http") is not None: + health_check_http = health_check.get("http") + service_health_check.http = LoadBalancerHealtCheckHttp() + if health_check_http.get("domain") is not None: + service_health_check.http.domain = health_check_http.get("domain") + if health_check_http.get("path") is not None: + service_health_check.http.path = health_check_http.get("path") + if health_check_http.get("response") is not None: + service_health_check.http.response = health_check_http.get("response") + if health_check_http.get("status_codes") is not None: + service_health_check.http.status_codes = health_check_http.get("status_codes") + if health_check_http.get("tls") is not None: + service_health_check.http.tls = health_check_http.get("tls") + + return service_health_check + + def _update_load_balancer_service(self): + changed = False + try: + params = { + "listen_port": self.module.params.get("listen_port"), + } + + if self.module.params.get("destination_port") is not None: + if self.hcloud_load_balancer_service.destination_port != self.module.params.get("destination_port"): + params["destination_port"] = self.module.params.get("destination_port") + changed = True + + if self.module.params.get("protocol") is not None: + if self.hcloud_load_balancer_service.protocol != self.module.params.get("protocol"): + params["protocol"] = self.module.params.get("protocol") + changed = True + + if self.module.params.get("proxyprotocol") is not None: + if self.hcloud_load_balancer_service.proxyprotocol != self.module.params.get("proxyprotocol"): + params["proxyprotocol"] = self.module.params.get("proxyprotocol") + changed = True + + if self.module.params.get("http") is not None: + params["http"] = self.__get_service_http(http_arg=self.module.params.get("http")) + changed = True + + if self.module.params.get("health_check") is not None: + params["health_check"] = self.__get_service_health_checks( + health_check=self.module.params.get("health_check") + ) + changed = True + + if not self.module.check_mode: + self.hcloud_load_balancer.update_service(LoadBalancerService(**params)).wait_until_finished( + max_retries=1000 + ) + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._get_load_balancer() + + if changed: + self._mark_as_changed() + + def _get_load_balancer_service(self): + for service in self.hcloud_load_balancer.services: + if self.module.params.get("listen_port") == service.listen_port: + self.hcloud_load_balancer_service = service + + def present_load_balancer_service(self): + self._get_load_balancer() + if self.hcloud_load_balancer_service is None: + self._create_load_balancer_service() + else: + self._update_load_balancer_service() + + def delete_load_balancer_service(self): + try: + self._get_load_balancer() + if self.hcloud_load_balancer_service is not None: + if not self.module.check_mode: + try: + self.hcloud_load_balancer.delete_service(self.hcloud_load_balancer_service).wait_until_finished( + max_retries=1000 + ) + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self.hcloud_load_balancer_service = None + except APIException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + load_balancer={"type": "str", "required": True}, + listen_port={"type": "int", "required": True}, + destination_port={"type": "int"}, + protocol={ + "type": "str", + "choices": ["http", "https", "tcp"], + }, + proxyprotocol={"type": "bool", "default": False}, + http={ + "type": "dict", + "options": dict( + cookie_name={"type": "str"}, + cookie_lifetime={"type": "int"}, + sticky_sessions={"type": "bool", "default": False}, + redirect_http={"type": "bool", "default": False}, + certificates={"type": "list", "elements": "str"}, + ), + }, + health_check={ + "type": "dict", + "options": dict( + protocol={ + "type": "str", + "choices": ["http", "https", "tcp"], + }, + port={"type": "int"}, + interval={"type": "int"}, + timeout={"type": "int"}, + retries={"type": "int"}, + http={ + "type": "dict", + "options": dict( + domain={"type": "str"}, + path={"type": "str"}, + response={"type": "str"}, + status_codes={"type": "list", "elements": "str"}, + tls={"type": "bool", "default": False}, + ), + }, + ), + }, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudLoadBalancerService.define_module() + + hcloud = AnsibleHCloudLoadBalancerService(module) + state = module.params.get("state") + if state == "absent": + hcloud.delete_load_balancer_service() + elif state == "present": + hcloud.present_load_balancer_service() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py new file mode 100644 index 000000000..36e7f608f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_target.py @@ -0,0 +1,313 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: load_balancer_target + +short_description: Manage Hetzner Cloud Load Balancer targets + + +description: + - Create and delete Hetzner Cloud Load Balancer targets + +author: + - Lukas Kaemmerling (@lkaemmerling) +version_added: 0.1.0 +options: + type: + description: + - The type of the target. + type: str + choices: [ server, label_selector, ip ] + required: true + load_balancer: + description: + - Name or ID of the Hetzner Cloud Load Balancer. + type: str + required: true + server: + description: + - Name or ID of the Hetzner Cloud Server. + - Required if I(type) is server + type: str + label_selector: + description: + - A Label Selector that will be used to determine the targets dynamically + - Required if I(type) is label_selector + type: str + ip: + description: + - An IP from a Hetzner Dedicated Server, needs to belongs to the same user as the project. + - Required if I(type) is ip + type: str + use_private_ip: + description: + - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. + - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.load_balancer_network) + type: bool + default: False + state: + description: + - State of the load_balancer_network. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a server Load Balancer target + hetzner.hcloud.load_balancer_target: + type: server + load_balancer: my-LoadBalancer + server: my-server + state: present + +- name: Create a label_selector Load Balancer target + hetzner.hcloud.load_balancer_target: + type: label_selector + load_balancer: my-LoadBalancer + label_selector: application=backend + state: present + +- name: Create an IP Load Balancer target + hetzner.hcloud.load_balancer_target: + type: ip + load_balancer: my-LoadBalancer + ip: 127.0.0.1 + state: present + +- name: Ensure the Load Balancer target is absent (remove if needed) + hetzner.hcloud.load_balancer_target: + type: server + load_balancer: my-LoadBalancer + server: my-server + state: absent +""" + +RETURN = """ +hcloud_load_balancer_target: + description: The relationship between a Load Balancer and a network + returned: always + type: complex + contains: + type: + description: Type of the Load Balancer Target + type: str + returned: always + sample: server + load_balancer: + description: Name of the Load Balancer + type: str + returned: always + sample: my-LoadBalancer + server: + description: Name of the Server + type: str + returned: if I(type) is server + sample: my-server + label_selector: + description: Label Selector + type: str + returned: if I(type) is label_selector + sample: application=backend + ip: + description: IP of the dedicated server + type: str + returned: if I(type) is ip + sample: 127.0.0.1 + use_private_ip: + description: + - Route the traffic over the private IP of the Load Balancer through a Hetzner Cloud Network. + - Load Balancer needs to be attached to a network. See M(hetzner.hcloud.load_balancer_network) + type: bool + sample: true + returned: always +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import APIException, HCloudException +from ..module_utils.vendor.hcloud.load_balancers import ( + BoundLoadBalancer, + LoadBalancerTarget, + LoadBalancerTargetIP, + LoadBalancerTargetLabelSelector, +) +from ..module_utils.vendor.hcloud.servers import BoundServer + + +class AnsibleHCloudLoadBalancerTarget(AnsibleHCloud): + represent = "hcloud_load_balancer_target" + + hcloud_load_balancer: BoundLoadBalancer | None = None + hcloud_load_balancer_target: LoadBalancerTarget | None = None + hcloud_server: BoundServer | None = None + + def _prepare_result(self): + result = { + "type": to_native(self.hcloud_load_balancer_target.type), + "load_balancer": to_native(self.hcloud_load_balancer.name), + "use_private_ip": self.hcloud_load_balancer_target.use_private_ip, + } + + if self.hcloud_load_balancer_target.type == "server": + result["server"] = to_native(self.hcloud_load_balancer_target.server.name) + elif self.hcloud_load_balancer_target.type == "label_selector": + result["label_selector"] = to_native(self.hcloud_load_balancer_target.label_selector.selector) + elif self.hcloud_load_balancer_target.type == "ip": + result["ip"] = to_native(self.hcloud_load_balancer_target.ip.ip) + return result + + def _get_load_balancer_and_target(self): + try: + self.hcloud_load_balancer = self._client_get_by_name_or_id( + "load_balancers", + self.module.params.get("load_balancer"), + ) + + if self.module.params.get("type") == "server": + self.hcloud_server = self._client_get_by_name_or_id( + "servers", + self.module.params.get("server"), + ) + + self.hcloud_load_balancer_target = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _get_load_balancer_target(self): + for target in self.hcloud_load_balancer.targets: + if self.module.params.get("type") == "server" and target.type == "server": + if target.server.id == self.hcloud_server.id: + self.hcloud_load_balancer_target = target + elif self.module.params.get("type") == "label_selector" and target.type == "label_selector": + if target.label_selector.selector == self.module.params.get("label_selector"): + self.hcloud_load_balancer_target = target + elif self.module.params.get("type") == "ip" and target.type == "ip": + if target.ip.ip == self.module.params.get("ip"): + self.hcloud_load_balancer_target = target + + def _create_load_balancer_target(self): + params = {"target": None} + + if self.module.params.get("type") == "server": + self.module.fail_on_missing_params(required_params=["server"]) + params["target"] = LoadBalancerTarget( + type=self.module.params.get("type"), + server=self.hcloud_server, + use_private_ip=self.module.params.get("use_private_ip"), + ) + elif self.module.params.get("type") == "label_selector": + self.module.fail_on_missing_params(required_params=["label_selector"]) + params["target"] = LoadBalancerTarget( + type=self.module.params.get("type"), + label_selector=LoadBalancerTargetLabelSelector(selector=self.module.params.get("label_selector")), + use_private_ip=self.module.params.get("use_private_ip"), + ) + elif self.module.params.get("type") == "ip": + self.module.fail_on_missing_params(required_params=["ip"]) + params["target"] = LoadBalancerTarget( + type=self.module.params.get("type"), + ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), + use_private_ip=False, + ) + + if not self.module.check_mode: + try: + self.hcloud_load_balancer.add_target(**params).wait_until_finished() + except APIException as exception: + if exception.code == "locked" or exception.code == "conflict": + self._create_load_balancer_target() + else: + self.fail_json_hcloud(exception) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + self._mark_as_changed() + self._get_load_balancer_and_target() + self._get_load_balancer_target() + + def present_load_balancer_target(self): + self._get_load_balancer_and_target() + self._get_load_balancer_target() + if self.hcloud_load_balancer_target is None: + self._create_load_balancer_target() + + def delete_load_balancer_target(self): + self._get_load_balancer_and_target() + self._get_load_balancer_target() + if self.hcloud_load_balancer_target is not None and self.hcloud_load_balancer is not None: + if not self.module.check_mode: + target = None + if self.module.params.get("type") == "server": + self.module.fail_on_missing_params(required_params=["server"]) + target = LoadBalancerTarget(type=self.module.params.get("type"), server=self.hcloud_server) + elif self.module.params.get("type") == "label_selector": + self.module.fail_on_missing_params(required_params=["label_selector"]) + target = LoadBalancerTarget( + type=self.module.params.get("type"), + label_selector=LoadBalancerTargetLabelSelector( + selector=self.module.params.get("label_selector") + ), + use_private_ip=self.module.params.get("use_private_ip"), + ) + elif self.module.params.get("type") == "ip": + self.module.fail_on_missing_params(required_params=["ip"]) + target = LoadBalancerTarget( + type=self.module.params.get("type"), + ip=LoadBalancerTargetIP(ip=self.module.params.get("ip")), + use_private_ip=False, + ) + try: + self.hcloud_load_balancer.remove_target(target).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self.hcloud_load_balancer_target = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + type={"type": "str", "required": True, "choices": ["server", "label_selector", "ip"]}, + load_balancer={"type": "str", "required": True}, + server={"type": "str"}, + label_selector={"type": "str"}, + ip={"type": "str"}, + use_private_ip={"type": "bool", "default": False}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudLoadBalancerTarget.define_module() + + hcloud = AnsibleHCloudLoadBalancerTarget(module) + state = module.params["state"] + if state == "absent": + hcloud.delete_load_balancer_target() + elif state == "present": + hcloud.present_load_balancer_target() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py new file mode 100644 index 000000000..67feafd59 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/load_balancer_type_info.py @@ -0,0 +1,161 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: load_balancer_type_info + +short_description: Gather infos about the Hetzner Cloud Load Balancer types. + + +description: + - Gather infos about your Hetzner Cloud Load Balancer types. + +author: + - Lukas Kaemmerling (@LKaemmerling) +version_added: 0.1.0 +options: + id: + description: + - The ID of the Load Balancer type you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the Load Balancer type you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud Load Balancer type infos + hetzner.hcloud.load_balancer_type_info: + register: output + +- name: Print the gathered infos + debug: + var: output.hcloud_load_balancer_type_info +""" + +RETURN = """ +hcloud_load_balancer_type_info: + description: The Load Balancer type infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the Load Balancer type + returned: always + type: int + sample: 1937415 + name: + description: Name of the Load Balancer type + returned: always + type: str + sample: lb11 + description: + description: Description of the Load Balancer type + returned: always + type: str + sample: LB11 + max_connections: + description: Number of maximum simultaneous open connections + returned: always + type: int + sample: 1 + max_services: + description: Number of services a Load Balancer of this type can have + returned: always + type: int + sample: 1 + max_targets: + description: Number of targets a single Load Balancer can have + returned: always + type: int + sample: 25 + max_assigned_certificates: + description: Number of SSL Certificates that can be assigned to a single Load Balancer + returned: always + type: int + sample: 5 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.load_balancer_types import BoundLoadBalancerType + + +class AnsibleHCloudLoadBalancerTypeInfo(AnsibleHCloud): + represent = "hcloud_load_balancer_type_info" + + hcloud_load_balancer_type_info: list[BoundLoadBalancerType] | None = None + + def _prepare_result(self): + tmp = [] + + for load_balancer_type in self.hcloud_load_balancer_type_info: + if load_balancer_type is not None: + tmp.append( + { + "id": to_native(load_balancer_type.id), + "name": to_native(load_balancer_type.name), + "description": to_native(load_balancer_type.description), + "max_connections": load_balancer_type.max_connections, + "max_services": load_balancer_type.max_services, + "max_targets": load_balancer_type.max_targets, + "max_assigned_certificates": load_balancer_type.max_assigned_certificates, + } + ) + return tmp + + def get_load_balancer_types(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_load_balancer_type_info = [ + self.client.load_balancer_types.get_by_id(self.module.params.get("id")) + ] + elif self.module.params.get("name") is not None: + self.hcloud_load_balancer_type_info = [ + self.client.load_balancer_types.get_by_name(self.module.params.get("name")) + ] + else: + self.hcloud_load_balancer_type_info = self.client.load_balancer_types.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudLoadBalancerTypeInfo.define_module() + hcloud = AnsibleHCloudLoadBalancerTypeInfo(module) + + hcloud.get_load_balancer_types() + result = hcloud.get_result() + + ansible_info = {"hcloud_load_balancer_type_info": result["hcloud_load_balancer_type_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/location_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/location_info.py new file mode 100644 index 000000000..ac495c6c8 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/location_info.py @@ -0,0 +1,145 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: location_info + +short_description: Gather infos about your Hetzner Cloud locations. + + +description: + - Gather infos about your Hetzner Cloud locations. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the location you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the location you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud location infos + hetzner.hcloud.location_info: + register: output + +- name: Print the gathered infos + debug: + var: output +""" + +RETURN = """ +hcloud_location_info: + description: The location infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the location + returned: always + type: int + sample: 1937415 + name: + description: Name of the location + returned: always + type: str + sample: fsn1 + description: + description: Detail description of the location + returned: always + type: str + sample: Falkenstein DC Park 1 + country: + description: Country code of the location + returned: always + type: str + sample: DE + city: + description: City of the location + returned: always + type: str + sample: Falkenstein +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.locations import BoundLocation + + +class AnsibleHCloudLocationInfo(AnsibleHCloud): + represent = "hcloud_location_info" + + hcloud_location_info: list[BoundLocation] | None = None + + def _prepare_result(self): + tmp = [] + + for location in self.hcloud_location_info: + if location is not None: + tmp.append( + { + "id": to_native(location.id), + "name": to_native(location.name), + "description": to_native(location.description), + "city": to_native(location.city), + "country": to_native(location.country), + } + ) + return tmp + + def get_locations(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_location_info = [self.client.locations.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_location_info = [self.client.locations.get_by_name(self.module.params.get("name"))] + else: + self.hcloud_location_info = self.client.locations.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudLocationInfo.define_module() + hcloud = AnsibleHCloudLocationInfo(module) + + hcloud.get_locations() + result = hcloud.get_result() + + ansible_info = {"hcloud_location_info": result["hcloud_location_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/network.py b/ansible_collections/hetzner/hcloud/plugins/modules/network.py new file mode 100644 index 000000000..24e45a48d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/network.py @@ -0,0 +1,259 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: network + +short_description: Create and manage cloud Networks on the Hetzner Cloud. + + +description: + - Create, update and manage cloud Networks on the Hetzner Cloud. + - You need at least hcloud-python 1.3.0. + +author: + - Lukas Kaemmerling (@lkaemmerling) + +options: + id: + description: + - The ID of the Hetzner Cloud Networks to manage. + - Only required if no Network I(name) is given. + type: int + name: + description: + - The Name of the Hetzner Cloud Network to manage. + - Only required if no Network I(id) is given or a Network does not exist. + type: str + ip_range: + description: + - IP range of the Network. + - Required if Network does not exist. + type: str + expose_routes_to_vswitch: + description: + - Indicates if the routes from this network should be exposed to the vSwitch connection. + - The exposing only takes effect if a vSwitch connection is active. + type: bool + labels: + description: + - User-defined labels (key-value pairs). + type: dict + delete_protection: + description: + - Protect the Network for deletion. + type: bool + state: + description: + - State of the Network. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic network + hetzner.hcloud.network: + name: my-network + ip_range: 10.0.0.0/8 + state: present + +- name: Ensure the Network is absent (remove if needed) + hetzner.hcloud.network: + name: my-network + state: absent +""" + +RETURN = """ +hcloud_network: + description: The Network + returned: always + type: complex + contains: + id: + description: ID of the Network + type: int + returned: always + sample: 12345 + name: + description: Name of the Network + type: str + returned: always + sample: my-volume + ip_range: + description: IP range of the Network + type: str + returned: always + sample: 10.0.0.0/8 + expose_routes_to_vswitch: + description: Indicates if the routes from this network should be exposed to the vSwitch connection. + type: bool + returned: always + sample: false + delete_protection: + description: True if Network is protected for deletion + type: bool + returned: always + sample: false + version_added: "0.1.0" + labels: + description: User-defined labels (key-value pairs) + type: dict + returned: always + sample: + key: value + mylabel: 123 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork + + +class AnsibleHCloudNetwork(AnsibleHCloud): + represent = "hcloud_network" + + hcloud_network: BoundNetwork | None = None + + def _prepare_result(self): + return { + "id": to_native(self.hcloud_network.id), + "name": to_native(self.hcloud_network.name), + "ip_range": to_native(self.hcloud_network.ip_range), + "expose_routes_to_vswitch": self.hcloud_network.expose_routes_to_vswitch, + "delete_protection": self.hcloud_network.protection["delete"], + "labels": self.hcloud_network.labels, + } + + def _get_network(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_network = self.client.networks.get_by_id(self.module.params.get("id")) + else: + self.hcloud_network = self.client.networks.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_network(self): + self.module.fail_on_missing_params(required_params=["name", "ip_range"]) + params = { + "name": self.module.params.get("name"), + "ip_range": self.module.params.get("ip_range"), + "labels": self.module.params.get("labels"), + } + + expose_routes_to_vswitch = self.module.params.get("expose_routes_to_vswitch") + if expose_routes_to_vswitch is not None: + params["expose_routes_to_vswitch"] = expose_routes_to_vswitch + + try: + if not self.module.check_mode: + self.client.networks.create(**params) + + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None: + self._get_network() + self.hcloud_network.change_protection(delete=delete_protection).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_network() + + def _update_network(self): + try: + labels = self.module.params.get("labels") + if labels is not None and labels != self.hcloud_network.labels: + if not self.module.check_mode: + self.hcloud_network.update(labels=labels) + self._mark_as_changed() + + ip_range = self.module.params.get("ip_range") + if ip_range is not None and ip_range != self.hcloud_network.ip_range: + if not self.module.check_mode: + self.hcloud_network.change_ip_range(ip_range=ip_range).wait_until_finished() + self._mark_as_changed() + + expose_routes_to_vswitch = self.module.params.get("expose_routes_to_vswitch") + if ( + expose_routes_to_vswitch is not None + and expose_routes_to_vswitch != self.hcloud_network.expose_routes_to_vswitch + ): + if not self.module.check_mode: + self.hcloud_network.update(expose_routes_to_vswitch=expose_routes_to_vswitch) + self._mark_as_changed() + + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None and delete_protection != self.hcloud_network.protection["delete"]: + if not self.module.check_mode: + self.hcloud_network.change_protection(delete=delete_protection).wait_until_finished() + self._mark_as_changed() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._get_network() + + def present_network(self): + self._get_network() + if self.hcloud_network is None: + self._create_network() + else: + self._update_network() + + def delete_network(self): + try: + self._get_network() + if self.hcloud_network is not None: + if not self.module.check_mode: + self.client.networks.delete(self.hcloud_network) + self._mark_as_changed() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self.hcloud_network = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + ip_range={"type": "str"}, + expose_routes_to_vswitch={"type": "bool"}, + labels={"type": "dict"}, + delete_protection={"type": "bool"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name"]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudNetwork.define_module() + + hcloud = AnsibleHCloudNetwork(module) + state = module.params["state"] + if state == "absent": + hcloud.delete_network() + elif state == "present": + hcloud.present_network() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/network_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/network_info.py new file mode 100644 index 000000000..4008352b4 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/network_info.py @@ -0,0 +1,296 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: network_info + +short_description: Gather info about your Hetzner Cloud networks. + + +description: + - Gather info about your Hetzner Cloud networks. + +author: + - Christopher Schmitt (@cschmitt-hcloud) + +options: + id: + description: + - The ID of the network you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the network you want to get. + type: str + label_selector: + description: + - The label selector for the network you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud network info + local_action: + module: hcloud_network_info + +- name: Print the gathered info + debug: + var: hcloud_network_info +""" + +RETURN = """ +hcloud_network_info: + description: The network info as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the network + returned: always + type: int + sample: 1937415 + name: + description: Name of the network + returned: always + type: str + sample: awesome-network + ip_range: + description: IP range of the network + returned: always + type: str + sample: 10.0.0.0/16 + subnetworks: + description: Subnetworks belonging to the network + returned: always + type: complex + contains: + type: + description: Type of the subnetwork. + returned: always + type: str + sample: cloud + network_zone: + description: Network of the subnetwork. + returned: always + type: str + sample: eu-central + ip_range: + description: IP range of the subnetwork + returned: always + type: str + sample: 10.0.0.0/24 + gateway: + description: Gateway of this subnetwork + returned: always + type: str + sample: 10.0.0.1 + routes: + description: Routes belonging to the network + returned: always + type: complex + contains: + ip_range: + description: Destination network or host of this route. + returned: always + type: str + sample: 10.0.0.0/16 + gateway: + description: Gateway of this route + returned: always + type: str + sample: 10.0.0.1 + expose_routes_to_vswitch: + description: Indicates if the routes from this network should be exposed to the vSwitch connection. + returned: always + type: bool + sample: false + servers: + description: Servers attached to the network + returned: always + type: complex + contains: + id: + description: Numeric identifier of the server + returned: always + type: int + sample: 1937415 + name: + description: Name of the server + returned: always + type: str + sample: my-server + status: + description: Status of the server + returned: always + type: str + sample: running + server_type: + description: Name of the server type of the server + returned: always + type: str + sample: cx11 + ipv4_address: + description: Public IPv4 address of the server, None if not existing + returned: always + type: str + sample: 116.203.104.109 + ipv6: + description: IPv6 network of the server, None if not existing + returned: always + type: str + sample: 2a01:4f8:1c1c:c140::/64 + location: + description: Name of the location of the server + returned: always + type: str + sample: fsn1 + datacenter: + description: Name of the datacenter of the server + returned: always + type: str + sample: fsn1-dc14 + rescue_enabled: + description: True if rescue mode is enabled, Server will then boot into rescue system on next reboot + returned: always + type: bool + sample: false + backup_window: + description: Time window (UTC) in which the backup will run, or null if the backups are not enabled + returned: always + type: bool + sample: 22-02 + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict + delete_protection: + description: True if the network is protected for deletion + returned: always + type: bool + version_added: "0.1.0" + labels: + description: Labels of the network + returned: always + type: dict +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork + + +class AnsibleHCloudNetworkInfo(AnsibleHCloud): + represent = "hcloud_network_info" + + hcloud_network_info: list[BoundNetwork] | None = None + + def _prepare_result(self): + tmp = [] + + for network in self.hcloud_network_info: + if network is not None: + subnets = [] + for subnet in network.subnets: + prepared_subnet = { + "type": subnet.type, + "ip_range": subnet.ip_range, + "network_zone": subnet.network_zone, + "gateway": subnet.gateway, + } + subnets.append(prepared_subnet) + routes = [] + for route in network.routes: + prepared_route = {"destination": route.destination, "gateway": route.gateway} + routes.append(prepared_route) + + servers = [] + for server in network.servers: + image = None if server.image is None else to_native(server.image.name) + ipv4_address = None if server.public_net.ipv4 is None else to_native(server.public_net.ipv4.ip) + ipv6 = None if server.public_net.ipv6 is None else to_native(server.public_net.ipv6.ip) + prepared_server = { + "id": to_native(server.id), + "name": to_native(server.name), + "ipv4_address": ipv4_address, + "ipv6": ipv6, + "image": image, + "server_type": to_native(server.server_type.name), + "datacenter": to_native(server.datacenter.name), + "location": to_native(server.datacenter.location.name), + "rescue_enabled": server.rescue_enabled, + "backup_window": to_native(server.backup_window), + "labels": server.labels, + "status": to_native(server.status), + } + servers.append(prepared_server) + + tmp.append( + { + "id": to_native(network.id), + "name": to_native(network.name), + "ip_range": to_native(network.ip_range), + "subnetworks": subnets, + "routes": routes, + "expose_routes_to_vswitch": network.expose_routes_to_vswitch, + "servers": servers, + "labels": network.labels, + "delete_protection": network.protection["delete"], + } + ) + return tmp + + def get_networks(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_network_info = [self.client.networks.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_network_info = [self.client.networks.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("label_selector") is not None: + self.hcloud_network_info = self.client.networks.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_network_info = self.client.networks.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + label_selector={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudNetworkInfo.define_module() + hcloud = AnsibleHCloudNetworkInfo(module) + + hcloud.get_networks() + result = hcloud.get_result() + + info = {"hcloud_network_info": result["hcloud_network_info"]} + module.exit_json(**info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py b/ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py new file mode 100644 index 000000000..ba26fad22 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/placement_group.py @@ -0,0 +1,220 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: placement_group + +short_description: Create and manage placement groups on the Hetzner Cloud. + + +description: + - Create, update and manage placement groups on the Hetzner Cloud. + +author: + - Adrian Huber (@Adi146) + +options: + id: + description: + - The ID of the Hetzner Cloud placement group to manage. + - Only required if no placement group I(name) is given + type: int + name: + description: + - The Name of the Hetzner Cloud placement group to manage. + - Only required if no placement group I(id) is given, or a placement group does not exist. + type: str + labels: + description: + - User-defined labels (key-value pairs) + type: dict + type: + description: + - The Type of the Hetzner Cloud placement group. + type: str + state: + description: + - State of the placement group. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic placement group + hetzner.hcloud.placement_group: + name: my-placement-group + state: present + type: spread + +- name: Create a placement group with labels + hetzner.hcloud.placement_group: + name: my-placement-group + type: spread + labels: + key: value + mylabel: 123 + state: present + +- name: Ensure the placement group is absent (remove if needed) + hetzner.hcloud.placement_group: + name: my-placement-group + state: absent +""" + +RETURN = """ +hcloud_placement_group: + description: The placement group instance + returned: Always + type: complex + contains: + id: + description: Numeric identifier of the placement group + returned: always + type: int + sample: 1937415 + name: + description: Name of the placement group + returned: always + type: str + sample: my placement group + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict + type: + description: Type of the placement group + returned: always + type: str + sample: spread + servers: + description: Server IDs of the placement group + returned: always + type: list + elements: int + sample: + - 4711 + - 4712 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.placement_groups import BoundPlacementGroup + + +class AnsibleHCloudPlacementGroup(AnsibleHCloud): + represent = "hcloud_placement_group" + + hcloud_placement_group: BoundPlacementGroup | None = None + + def _prepare_result(self): + return { + "id": to_native(self.hcloud_placement_group.id), + "name": to_native(self.hcloud_placement_group.name), + "labels": self.hcloud_placement_group.labels, + "type": to_native(self.hcloud_placement_group.type), + "servers": self.hcloud_placement_group.servers, + } + + def _get_placement_group(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_placement_group = self.client.placement_groups.get_by_id(self.module.params.get("id")) + elif self.module.params.get("name") is not None: + self.hcloud_placement_group = self.client.placement_groups.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_placement_group(self): + self.module.fail_on_missing_params(required_params=["name"]) + params = { + "name": self.module.params.get("name"), + "type": self.module.params.get("type"), + "labels": self.module.params.get("labels"), + } + if not self.module.check_mode: + try: + self.client.placement_groups.create(**params) + except HCloudException as exception: + self.fail_json_hcloud(exception, params=params) + self._mark_as_changed() + self._get_placement_group() + + def _update_placement_group(self): + name = self.module.params.get("name") + if name is not None and self.hcloud_placement_group.name != name: + self.module.fail_on_missing_params(required_params=["id"]) + if not self.module.check_mode: + self.hcloud_placement_group.update(name=name) + self._mark_as_changed() + + labels = self.module.params.get("labels") + if labels is not None and self.hcloud_placement_group.labels != labels: + if not self.module.check_mode: + self.hcloud_placement_group.update(labels=labels) + self._mark_as_changed() + + self._get_placement_group() + + def present_placement_group(self): + self._get_placement_group() + if self.hcloud_placement_group is None: + self._create_placement_group() + else: + self._update_placement_group() + + def delete_placement_group(self): + self._get_placement_group() + if self.hcloud_placement_group is not None: + if not self.module.check_mode: + self.client.placement_groups.delete(self.hcloud_placement_group) + self._mark_as_changed() + self.hcloud_placement_group = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + labels={"type": "dict"}, + type={"type": "str"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name"]], + required_if=[["state", "present", ["name"]]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudPlacementGroup.define_module() + + hcloud = AnsibleHCloudPlacementGroup(module) + state = module.params.get("state") + if state == "absent": + hcloud.delete_placement_group() + elif state == "present": + hcloud.present_placement_group() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py new file mode 100644 index 000000000..607f6c7e1 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip.py @@ -0,0 +1,260 @@ +#!/usr/bin/python + +# Copyright: (c) 2022, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: primary_ip + +short_description: Create and manage cloud Primary IPs on the Hetzner Cloud. + + +description: + - Create, update and manage cloud Primary IPs on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@lkaemmerling) +version_added: 1.8.0 +options: + id: + description: + - The ID of the Hetzner Cloud Primary IPs to manage. + - Only required if no Primary IP I(name) is given. + type: int + name: + description: + - The Name of the Hetzner Cloud Primary IPs to manage. + - Only required if no Primary IP I(id) is given or a Primary IP does not exist. + type: str + datacenter: + description: + - Home Location of the Hetzner Cloud Primary IP. + - Required if no I(server) is given and Primary IP does not exist. + type: str + type: + description: + - Type of the Primary IP. + - Required if Primary IP does not exist + choices: [ ipv4, ipv6 ] + type: str + auto_delete: + description: + - Delete this Primary IP when the resource it is assigned to is deleted + type: bool + default: no + delete_protection: + description: + - Protect the Primary IP for deletion. + type: bool + labels: + description: + - User-defined labels (key-value pairs). + type: dict + state: + description: + - State of the Primary IP. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic IPv4 Primary IP + hetzner.hcloud.primary_ip: + name: my-primary-ip + datacenter: fsn1-dc14 + type: ipv4 + state: present +- name: Create a basic IPv6 Primary IP + hetzner.hcloud.primary_ip: + name: my-primary-ip + datacenter: fsn1-dc14 + type: ipv6 + state: present +- name: Primary IP should be absent + hetzner.hcloud.primary_ip: + name: my-primary-ip + state: absent +""" + +RETURN = """ +hcloud_primary_ip: + description: The Primary IP instance + returned: Always + type: complex + contains: + id: + description: ID of the Primary IP + type: int + returned: Always + sample: 12345 + name: + description: Name of the Primary IP + type: str + returned: Always + sample: my-primary-ip + ip: + description: IP Address of the Primary IP + type: str + returned: Always + sample: 116.203.104.109 + type: + description: Type of the Primary IP + type: str + returned: Always + sample: ipv4 + datacenter: + description: Name of the datacenter of the Primary IP + type: str + returned: Always + sample: fsn1-dc14 + delete_protection: + description: True if Primary IP is protected for deletion + type: bool + returned: always + sample: false + labels: + description: User-defined labels (key-value pairs) + type: dict + returned: Always + sample: + key: value + mylabel: 123 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.primary_ips import BoundPrimaryIP + + +class AnsibleHCloudPrimaryIP(AnsibleHCloud): + represent = "hcloud_primary_ip" + + hcloud_primary_ip: BoundPrimaryIP | None = None + + def _prepare_result(self): + return { + "id": to_native(self.hcloud_primary_ip.id), + "name": to_native(self.hcloud_primary_ip.name), + "ip": to_native(self.hcloud_primary_ip.ip), + "type": to_native(self.hcloud_primary_ip.type), + "datacenter": to_native(self.hcloud_primary_ip.datacenter.name), + "labels": self.hcloud_primary_ip.labels, + "delete_protection": self.hcloud_primary_ip.protection["delete"], + } + + def _get_primary_ip(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_primary_ip = self.client.primary_ips.get_by_id(self.module.params.get("id")) + else: + self.hcloud_primary_ip = self.client.primary_ips.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_primary_ip(self): + self.module.fail_on_missing_params(required_params=["type", "datacenter"]) + try: + params = { + "type": self.module.params.get("type"), + "name": self.module.params.get("name"), + "datacenter": self.client.datacenters.get_by_name(self.module.params.get("datacenter")), + } + + if self.module.params.get("labels") is not None: + params["labels"] = self.module.params.get("labels") + if not self.module.check_mode: + resp = self.client.primary_ips.create(**params) + self.hcloud_primary_ip = resp.primary_ip + + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None: + self.hcloud_primary_ip.change_protection(delete=delete_protection).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_primary_ip() + + def _update_primary_ip(self): + try: + labels = self.module.params.get("labels") + if labels is not None and labels != self.hcloud_primary_ip.labels: + if not self.module.check_mode: + self.hcloud_primary_ip.update(labels=labels) + self._mark_as_changed() + + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None and delete_protection != self.hcloud_primary_ip.protection["delete"]: + if not self.module.check_mode: + self.hcloud_primary_ip.change_protection(delete=delete_protection).wait_until_finished() + self._mark_as_changed() + + self._get_primary_ip() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def present_primary_ip(self): + self._get_primary_ip() + if self.hcloud_primary_ip is None: + self._create_primary_ip() + else: + self._update_primary_ip() + + def delete_primary_ip(self): + try: + self._get_primary_ip() + if self.hcloud_primary_ip is not None: + if not self.module.check_mode: + self.client.primary_ips.delete(self.hcloud_primary_ip) + self._mark_as_changed() + self.hcloud_primary_ip = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + datacenter={"type": "str"}, + auto_delete={"type": "bool", "default": False}, + type={"choices": ["ipv4", "ipv6"]}, + labels={"type": "dict"}, + delete_protection={"type": "bool"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name"]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudPrimaryIP.define_module() + + hcloud = AnsibleHCloudPrimaryIP(module) + state = module.params["state"] + if state == "absent": + hcloud.delete_primary_ip() + elif state == "present": + hcloud.present_primary_ip() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py new file mode 100644 index 000000000..c0bfdbb35 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/primary_ip_info.py @@ -0,0 +1,203 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: primary_ip_info + +short_description: Gather infos about the Hetzner Cloud Primary IPs. + +description: + - Gather facts about your Hetzner Cloud Primary IPs. + +author: + - Lukas Kaemmerling (@LKaemmerling) + - Kevin Castner (@kcastner) + +options: + id: + description: + - The ID of the Primary IP you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name for the Primary IP you want to get. + type: str + label_selector: + description: + - The label selector for the Primary IP you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud Primary IP infos + hetzner.hcloud.primary_ip_info: + register: output + +- name: Gather hcloud Primary IP infos by id + hetzner.hcloud.primary_ip_info: + id: 673954 + register: output + +- name: Gather hcloud Primary IP infos by name + hetzner.hcloud.primary_ip_info: + name: srv1-v4 + register: output + +- name: Gather hcloud Primary IP infos by label + hetzner.hcloud.primary_ip_info: + label_selector: srv03-ips + register: output + +- name: Print the gathered infos + debug: + var: output +""" + +RETURN = """ +hcloud_primary_ip_info: + description: The Primary IP infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the Primary IP + returned: always + type: int + sample: 1937415 + name: + description: Name of the Primary IP + returned: always + type: str + sample: my-primary-ip + ip: + description: IP address of the Primary IP + returned: always + type: str + sample: 131.232.99.1 + type: + description: Type of the Primary IP + returned: always + type: str + sample: ipv4 + assignee_id: + description: Numeric identifier of the ressource where the Primary IP is assigned to. + returned: always + type: int + sample: 19584637 + assignee_type: + description: Name of the type where the Primary IP is assigned to. + returned: always + type: str + sample: server + home_location: + description: Location with datacenter where the Primary IP was created in + returned: always + type: str + sample: fsn1-dc1 + dns_ptr: + description: Shows the DNS PTR Record for Primary IP. + returned: always + type: str + sample: srv01.example.com + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict + delete_protection: + description: True if the Primary IP is protected for deletion + returned: always + type: bool +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.primary_ips import BoundPrimaryIP + + +class AnsibleHCloudPrimaryIPInfo(AnsibleHCloud): + represent = "hcloud_primary_ip_info" + + hcloud_primary_ip_info: list[BoundPrimaryIP] | None = None + + def _prepare_result(self): + tmp = [] + + for primary_ip in self.hcloud_primary_ip_info: + if primary_ip is not None: + dns_ptr = None + if len(primary_ip.dns_ptr) > 0: + dns_ptr = primary_ip.dns_ptr[0]["dns_ptr"] + tmp.append( + { + "id": to_native(primary_ip.id), + "name": to_native(primary_ip.name), + "ip": to_native(primary_ip.ip), + "type": to_native(primary_ip.type), + "assignee_id": ( + to_native(primary_ip.assignee_id) if primary_ip.assignee_id is not None else None + ), + "assignee_type": to_native(primary_ip.assignee_type), + "home_location": to_native(primary_ip.datacenter.name), + "dns_ptr": to_native(dns_ptr) if dns_ptr is not None else None, + "labels": primary_ip.labels, + "delete_protection": primary_ip.protection["delete"], + } + ) + + return tmp + + def get_primary_ips(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_primary_ip_info = [self.client.primary_ips.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_primary_ip_info = [self.client.primary_ips.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("label_selector") is not None: + self.hcloud_primary_ip_info = self.client.primary_ips.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_primary_ip_info = self.client.primary_ips.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + label_selector={"type": "str"}, + name={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudPrimaryIPInfo.define_module() + hcloud = AnsibleHCloudPrimaryIPInfo(module) + + hcloud.get_primary_ips() + result = hcloud.get_result() + + ansible_info = {"hcloud_primary_ip_info": result["hcloud_primary_ip_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/rdns.py b/ansible_collections/hetzner/hcloud/plugins/modules/rdns.py new file mode 100644 index 000000000..b2decdec8 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/rdns.py @@ -0,0 +1,360 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: rdns + +short_description: Create and manage reverse DNS entries on the Hetzner Cloud. + + +description: + - Create, update and delete reverse DNS entries on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@lkaemmerling) + +options: + server: + description: + - Name or ID of the Hetzner Cloud server you want to add the reverse DNS entry to. + type: str + floating_ip: + description: + - Name or ID of the Hetzner Cloud Floating IP you want to add the reverse DNS entry to. + type: str + primary_ip: + description: + - Name or ID of the Hetzner Cloud Primary IP you want to add the reverse DNS entry to. + type: str + load_balancer: + description: + - Name or ID of the Hetzner Cloud Load Balancer you want to add the reverse DNS entry to. + type: str + ip_address: + description: + - The IP address that should point to I(dns_ptr). + type: str + required: true + dns_ptr: + description: + - The DNS address the I(ip_address) should resolve to. + - Omit the param to reset the reverse DNS entry to the default value. + type: str + state: + description: + - State of the reverse DNS entry. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a reverse DNS entry for a server + hetzner.hcloud.rdns: + server: my-server + ip_address: 123.123.123.123 + dns_ptr: example.com + state: present + +- name: Create a reverse DNS entry for a Floating IP + hetzner.hcloud.rdns: + floating_ip: my-floating-ip + ip_address: 123.123.123.123 + dns_ptr: example.com + state: present + +- name: Create a reverse DNS entry for a Primary IP + hetzner.hcloud.rdns: + primary_ip: my-primary-ip + ip_address: 123.123.123.123 + dns_ptr: example.com + state: present + +- name: Create a reverse DNS entry for a Load Balancer + hetzner.hcloud.rdns: + load_balancer: my-load-balancer + ip_address: 123.123.123.123 + dns_ptr: example.com + state: present + +- name: Ensure the reverse DNS entry is absent (remove if needed) + hetzner.hcloud.rdns: + server: my-server + ip_address: 123.123.123.123 + dns_ptr: example.com + state: absent +""" + +RETURN = """ +hcloud_rdns: + description: The reverse DNS entry + returned: always + type: complex + contains: + server: + description: Name of the server + type: str + returned: always + sample: my-server + floating_ip: + description: Name of the Floating IP + type: str + returned: always + sample: my-floating-ip + primary_ip: + description: Name of the Primary IP + type: str + returned: always + sample: my-primary-ip + load_balancer: + description: Name of the Load Balancer + type: str + returned: always + sample: my-load-balancer + ip_address: + description: The IP address that point to the DNS ptr + type: str + returned: always + sample: 123.123.123.123 + dns_ptr: + description: The DNS that resolves to the IP + type: str + returned: always + sample: example.com +""" + +import ipaddress +from typing import Any + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.floating_ips import BoundFloatingIP +from ..module_utils.vendor.hcloud.load_balancers import BoundLoadBalancer +from ..module_utils.vendor.hcloud.primary_ips import BoundPrimaryIP +from ..module_utils.vendor.hcloud.servers import BoundServer + + +class AnsibleHCloudReverseDNS(AnsibleHCloud): + represent = "hcloud_rdns" + + hcloud_resource: BoundServer | BoundFloatingIP | BoundLoadBalancer | BoundPrimaryIP | None = None + hcloud_rdns: dict[str, Any] | None = None + + def _prepare_result(self): + result = { + "server": None, + "floating_ip": None, + "load_balancer": None, + "ip_address": to_native(self.hcloud_rdns["ip_address"]), + "dns_ptr": to_native(self.hcloud_rdns["dns_ptr"]), + } + + if self.module.params.get("server"): + result["server"] = to_native(self.hcloud_resource.name) + elif self.module.params.get("floating_ip"): + result["floating_ip"] = to_native(self.hcloud_resource.name) + elif self.module.params.get("load_balancer"): + result["load_balancer"] = to_native(self.hcloud_resource.name) + elif self.module.params.get("primary_ip"): + result["primary_ip"] = to_native(self.hcloud_resource.name) + return result + + def _get_resource(self): + try: + if self.module.params.get("server"): + self.hcloud_resource = self._client_get_by_name_or_id( + "servers", + self.module.params.get("server"), + ) + elif self.module.params.get("floating_ip"): + self.hcloud_resource = self._client_get_by_name_or_id( + "floating_ips", + self.module.params.get("floating_ip"), + ) + elif self.module.params.get("primary_ip"): + self.hcloud_resource = self._client_get_by_name_or_id( + "primary_ips", + self.module.params.get("primary_ip"), + ) + elif self.module.params.get("load_balancer"): + self.hcloud_resource = self._client_get_by_name_or_id( + "load_balancers", + self.module.params.get("load_balancer"), + ) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _get_rdns(self): + ip_address = self.module.params.get("ip_address") + + try: + ip_address_obj = ipaddress.ip_address(ip_address) + except ValueError: + self.module.fail_json(msg=f"The given IP address is not valid: {ip_address}") + + if ip_address_obj.version == 4: + if self.module.params.get("server"): + if self.hcloud_resource.public_net.ipv4.ip == ip_address: + self.hcloud_rdns = { + "ip_address": self.hcloud_resource.public_net.ipv4.ip, + "dns_ptr": self.hcloud_resource.public_net.ipv4.dns_ptr, + } + else: + self.module.fail_json(msg="The selected server does not have this IP address") + elif self.module.params.get("floating_ip"): + if self.hcloud_resource.ip == ip_address: + self.hcloud_rdns = { + "ip_address": self.hcloud_resource.ip, + "dns_ptr": self.hcloud_resource.dns_ptr[0]["dns_ptr"], + } + else: + self.module.fail_json(msg="The selected Floating IP does not have this IP address") + elif self.module.params.get("primary_ip"): + if self.hcloud_resource.ip == ip_address: + self.hcloud_rdns = { + "ip_address": self.hcloud_resource.ip, + "dns_ptr": self.hcloud_resource.dns_ptr[0]["dns_ptr"], + } + else: + self.module.fail_json(msg="The selected Primary IP does not have this IP address") + elif self.module.params.get("load_balancer"): + if self.hcloud_resource.public_net.ipv4.ip == ip_address: + self.hcloud_rdns = { + "ip_address": self.hcloud_resource.public_net.ipv4.ip, + "dns_ptr": self.hcloud_resource.public_net.ipv4.dns_ptr, + } + else: + self.module.fail_json(msg="The selected Load Balancer does not have this IP address") + + elif ip_address_obj.version == 6: + if self.module.params.get("server"): + for ipv6_address_dns_ptr in self.hcloud_resource.public_net.ipv6.dns_ptr: + if ipv6_address_dns_ptr["ip"] == ip_address: + self.hcloud_rdns = { + "ip_address": ipv6_address_dns_ptr["ip"], + "dns_ptr": ipv6_address_dns_ptr["dns_ptr"], + } + elif self.module.params.get("floating_ip"): + for ipv6_address_dns_ptr in self.hcloud_resource.dns_ptr: + if ipv6_address_dns_ptr["ip"] == ip_address: + self.hcloud_rdns = { + "ip_address": ipv6_address_dns_ptr["ip"], + "dns_ptr": ipv6_address_dns_ptr["dns_ptr"], + } + elif self.module.params.get("primary_ip"): + for ipv6_address_dns_ptr in self.hcloud_resource.dns_ptr: + if ipv6_address_dns_ptr["ip"] == ip_address: + self.hcloud_rdns = { + "ip_address": ipv6_address_dns_ptr["ip"], + "dns_ptr": ipv6_address_dns_ptr["dns_ptr"], + } + elif self.module.params.get("load_balancer"): + for ipv6_address_dns_ptr in self.hcloud_resource.public_net.ipv6.dns_ptr: + if ipv6_address_dns_ptr["ip"] == ip_address: + self.hcloud_rdns = { + "ip_address": ipv6_address_dns_ptr["ip"], + "dns_ptr": ipv6_address_dns_ptr["dns_ptr"], + } + + def _create_rdns(self): + self.module.fail_on_missing_params(required_params=["dns_ptr"]) + params = { + "ip": self.module.params.get("ip_address"), + "dns_ptr": self.module.params.get("dns_ptr"), + } + + if not self.module.check_mode: + try: + self.hcloud_resource.change_dns_ptr(**params).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_resource() + self._get_rdns() + + def _update_rdns(self): + dns_ptr = self.module.params.get("dns_ptr") + if dns_ptr != self.hcloud_rdns["dns_ptr"]: + params = { + "ip": self.module.params.get("ip_address"), + "dns_ptr": dns_ptr, + } + + if not self.module.check_mode: + try: + self.hcloud_resource.change_dns_ptr(**params).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_resource() + self._get_rdns() + + def present_rdns(self): + self._get_resource() + self._get_rdns() + if self.hcloud_rdns is None: + self._create_rdns() + else: + self._update_rdns() + + def delete_rdns(self): + self._get_resource() + self._get_rdns() + if self.hcloud_rdns is not None: + if not self.module.check_mode: + try: + self.hcloud_resource.change_dns_ptr(ip=self.hcloud_rdns["ip_address"], dns_ptr=None) + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self.hcloud_rdns = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + server={"type": "str"}, + floating_ip={"type": "str"}, + load_balancer={"type": "str"}, + primary_ip={"type": "str"}, + ip_address={"type": "str", "required": True}, + dns_ptr={"type": "str"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["server", "floating_ip", "load_balancer", "primary_ip"]], + mutually_exclusive=[["server", "floating_ip", "load_balancer", "primary_ip"]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudReverseDNS.define_module() + + hcloud = AnsibleHCloudReverseDNS(module) + state = module.params["state"] + if state == "absent": + hcloud.delete_rdns() + elif state == "present": + hcloud.present_rdns() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/route.py b/ansible_collections/hetzner/hcloud/plugins/modules/route.py new file mode 100644 index 000000000..3c96a7382 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/route.py @@ -0,0 +1,190 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: route + +short_description: Create and delete cloud routes on the Hetzner Cloud. + + +description: + - Create, update and delete cloud routes on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@lkaemmerling) + +options: + network: + description: + - Name or ID of the Hetzner Cloud Network. + type: str + required: true + destination: + description: + - Destination network or host of this route. + type: str + required: true + gateway: + description: + - Gateway for the route. + type: str + required: true + state: + description: + - State of the route. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic route + hetzner.hcloud.route: + network: my-network + destination: 10.100.1.0/24 + gateway: 10.0.1.1 + state: present + +- name: Ensure the route is absent + hetzner.hcloud.route: + network: my-network + destination: 10.100.1.0/24 + gateway: 10.0.1.1 + state: absent +""" + +RETURN = """ +hcloud_route: + description: One Route of a Network + returned: always + type: complex + contains: + network: + description: Name of the Network + type: str + returned: always + sample: my-network + destination: + description: Destination network or host of this route + type: str + returned: always + sample: 10.0.0.0/8 + gateway: + description: Gateway of the route + type: str + returned: always + sample: 10.0.0.1 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork, NetworkRoute + + +class AnsibleHCloudRoute(AnsibleHCloud): + represent = "hcloud_route" + + hcloud_network: BoundNetwork | None = None + hcloud_route: NetworkRoute | None = None + + def _prepare_result(self): + return { + "network": to_native(self.hcloud_network.name), + "destination": to_native(self.hcloud_route.destination), + "gateway": self.hcloud_route.gateway, + } + + def _get_network(self): + try: + self.hcloud_network = self._client_get_by_name_or_id( + "networks", + self.module.params.get("network"), + ) + self.hcloud_route = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _get_route(self): + destination = self.module.params.get("destination") + gateway = self.module.params.get("gateway") + for route in self.hcloud_network.routes: + if route.destination == destination and route.gateway == gateway: + self.hcloud_route = route + + def _create_route(self): + route = NetworkRoute( + destination=self.module.params.get("destination"), gateway=self.module.params.get("gateway") + ) + + if not self.module.check_mode: + try: + self.hcloud_network.add_route(route=route).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + self._mark_as_changed() + self._get_network() + self._get_route() + + def present_route(self): + self._get_network() + self._get_route() + if self.hcloud_route is None: + self._create_route() + + def delete_route(self): + self._get_network() + self._get_route() + if self.hcloud_route is not None and self.hcloud_network is not None: + if not self.module.check_mode: + try: + self.hcloud_network.delete_route(self.hcloud_route).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self.hcloud_route = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + network={"type": "str", "required": True}, + gateway={"type": "str", "required": True}, + destination={"type": "str", "required": True}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudRoute.define_module() + + hcloud = AnsibleHCloudRoute(module) + state = module.params["state"] + if state == "absent": + hcloud.delete_route() + elif state == "present": + hcloud.present_route() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/server.py b/ansible_collections/hetzner/hcloud/plugins/modules/server.py new file mode 100644 index 000000000..f5cadb807 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server.py @@ -0,0 +1,952 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: server + +short_description: Create and manage cloud servers on the Hetzner Cloud. + + +description: + - Create, update and manage cloud servers on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the Hetzner Cloud server to manage. + - Only required if no server I(name) is given + type: int + name: + description: + - The Name of the Hetzner Cloud server to manage. + - Only required if no server I(id) is given or a server does not exist. + type: str + server_type: + description: + - The Server Type of the Hetzner Cloud server to manage. + - Required if server does not exist. + type: str + ssh_keys: + description: + - List of SSH key names + - The key names correspond to the SSH keys configured for your + Hetzner Cloud account access. + type: list + elements: str + volumes: + description: + - List of Volumes IDs that should be attached to the server on server creation. + type: list + elements: str + firewalls: + description: + - List of Firewall IDs that should be attached to the server on server creation. + type: list + elements: str + image: + description: + - Image the server should be created from. + - Required if server does not exist. + type: str + location: + description: + - Location of Server. + - Required if no I(datacenter) is given and server does not exist. + type: str + datacenter: + description: + - Datacenter of Server. + - Required if no I(location) is given and server does not exist. + type: str + backups: + description: + - Enable or disable Backups for the given Server. + type: bool + upgrade_disk: + description: + - Resize the disk size, when resizing a server. + - If you want to downgrade the server later, this value should be False. + type: bool + default: false + enable_ipv4: + description: + - Enables the public ipv4 address + type: bool + default: true + enable_ipv6: + description: + - Enables the public ipv6 address + type: bool + default: true + ipv4: + description: + - ID of the ipv4 Primary IP to use. If omitted and enable_ipv4 is true, a new ipv4 Primary IP will automatically be created + type: str + ipv6: + description: + - ID of the ipv6 Primary IP to use. If omitted and enable_ipv6 is true, a new ipv6 Primary IP will automatically be created. + type: str + private_networks: + description: + - List of private networks the server is attached to (name or ID) + - If None, private networks are left as they are (e.g. if previously added by hcloud_server_network), + if it has any other value (including []), only those networks are attached to the server. + type: list + elements: str + force_upgrade: + description: + - Deprecated + - Force the upgrade of the server. + - Power off the server if it is running on upgrade. + type: bool + force: + description: + - Force the update of the server. + - May power off the server if update. + type: bool + default: false + allow_deprecated_image: + description: + - Allows the creation of servers with deprecated images. + type: bool + default: false + user_data: + description: + - User Data to be passed to the server on creation. + - Only used if server does not exist. + type: str + rescue_mode: + description: + - Add the Hetzner rescue system type you want the server to be booted into. + type: str + labels: + description: + - User-defined labels (key-value pairs). + type: dict + delete_protection: + description: + - Protect the Server for deletion. + - Needs to be the same as I(rebuild_protection). + type: bool + rebuild_protection: + description: + - Protect the Server for rebuild. + - Needs to be the same as I(delete_protection). + type: bool + placement_group: + description: + - Placement Group of the server. + type: str + state: + description: + - State of the server. + default: present + choices: [ absent, present, restarted, started, stopped, rebuild ] + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Create a basic server + hetzner.hcloud.server: + name: my-server + server_type: cx11 + image: ubuntu-22.04 + state: present + +- name: Create a basic server with ssh key + hetzner.hcloud.server: + name: my-server + server_type: cx11 + image: ubuntu-22.04 + location: fsn1 + ssh_keys: + - me@myorganisation + state: present + +- name: Resize an existing server + hetzner.hcloud.server: + name: my-server + server_type: cx21 + upgrade_disk: true + state: present + +- name: Ensure the server is absent (remove if needed) + hetzner.hcloud.server: + name: my-server + state: absent + +- name: Ensure the server is started + hetzner.hcloud.server: + name: my-server + state: started + +- name: Ensure the server is stopped + hetzner.hcloud.server: + name: my-server + state: stopped + +- name: Ensure the server is restarted + hetzner.hcloud.server: + name: my-server + state: restarted + +- name: Ensure the server is will be booted in rescue mode and therefore restarted + hetzner.hcloud.server: + name: my-server + rescue_mode: linux64 + state: restarted + +- name: Ensure the server is rebuild + hetzner.hcloud.server: + name: my-server + image: ubuntu-22.04 + state: rebuild + +- name: Add server to placement group + hetzner.hcloud.server: + name: my-server + placement_group: my-placement-group + force: true + state: present + +- name: Remove server from placement group + hetzner.hcloud.server: + name: my-server + placement_group: + state: present + +- name: Add server with private network only + hetzner.hcloud.server: + name: my-server + enable_ipv4: false + enable_ipv6: false + private_networks: + - my-network + - 4711 + state: present +""" + +RETURN = """ +hcloud_server: + description: The server instance + returned: Always + type: complex + contains: + id: + description: Numeric identifier of the server + returned: always + type: int + sample: 1937415 + name: + description: Name of the server + returned: always + type: str + sample: my-server + created: + description: Point in time when the Server was created (in ISO-8601 format) + returned: always + type: str + sample: "2023-11-06T13:36:56+00:00" + status: + description: Status of the server + returned: always + type: str + sample: running + server_type: + description: Name of the server type of the server + returned: always + type: str + sample: cx11 + ipv4_address: + description: Public IPv4 address of the server + returned: always + type: str + sample: 116.203.104.109 + ipv6: + description: IPv6 network of the server + returned: always + type: str + sample: 2a01:4f8:1c1c:c140::/64 + private_networks: + description: List of private networks the server is attached to (name or ID) + returned: always + type: list + elements: str + sample: ['my-network', 'another-network', '4711'] + private_networks_info: + description: List of private networks the server is attached to (dict with name and ip) + returned: always + type: list + elements: dict + sample: [{'name': 'my-network', 'ip': '192.168.1.1'}, {'name': 'another-network', 'ip': '10.185.50.40'}] + location: + description: Name of the location of the server + returned: always + type: str + sample: fsn1 + placement_group: + description: Placement Group of the server + type: str + returned: always + sample: 4711 + version_added: "1.5.0" + datacenter: + description: Name of the datacenter of the server + returned: always + type: str + sample: fsn1-dc14 + rescue_enabled: + description: True if rescue mode is enabled, Server will then boot into rescue system on next reboot + returned: always + type: bool + sample: false + backup_window: + description: Time window (UTC) in which the backup will run, or null if the backups are not enabled + returned: always + type: bool + sample: 22-02 + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict + delete_protection: + description: True if server is protected for deletion + type: bool + returned: always + sample: false + version_added: "0.1.0" + rebuild_protection: + description: True if server is protected for rebuild + type: bool + returned: always + sample: false + version_added: "0.1.0" +""" + +from datetime import datetime, timedelta, timezone + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.firewalls import FirewallResource +from ..module_utils.vendor.hcloud.servers import ( + BoundServer, + Server, + ServerCreatePublicNetwork, +) +from ..module_utils.vendor.hcloud.ssh_keys import SSHKey +from ..module_utils.vendor.hcloud.volumes import Volume + + +class AnsibleHCloudServer(AnsibleHCloud): + represent = "hcloud_server" + + hcloud_server: BoundServer | None = None + + def _prepare_result(self): + image = None if self.hcloud_server.image is None else to_native(self.hcloud_server.image.name) + placement_group = ( + None if self.hcloud_server.placement_group is None else to_native(self.hcloud_server.placement_group.name) + ) + ipv4_address = ( + None if self.hcloud_server.public_net.ipv4 is None else to_native(self.hcloud_server.public_net.ipv4.ip) + ) + ipv6 = None if self.hcloud_server.public_net.ipv6 is None else to_native(self.hcloud_server.public_net.ipv6.ip) + backup_window = ( + None if self.hcloud_server.backup_window is None else to_native(self.hcloud_server.backup_window) + ) + return { + "id": to_native(self.hcloud_server.id), + "name": to_native(self.hcloud_server.name), + "created": to_native(self.hcloud_server.created.isoformat()), + "ipv4_address": ipv4_address, + "ipv6": ipv6, + "private_networks": [to_native(net.network.name) for net in self.hcloud_server.private_net], + "private_networks_info": [ + {"name": to_native(net.network.name), "ip": net.ip} for net in self.hcloud_server.private_net + ], + "image": image, + "server_type": to_native(self.hcloud_server.server_type.name), + "datacenter": to_native(self.hcloud_server.datacenter.name), + "location": to_native(self.hcloud_server.datacenter.location.name), + "placement_group": placement_group, + "rescue_enabled": self.hcloud_server.rescue_enabled, + "backup_window": backup_window, + "labels": self.hcloud_server.labels, + "delete_protection": self.hcloud_server.protection["delete"], + "rebuild_protection": self.hcloud_server.protection["rebuild"], + "status": to_native(self.hcloud_server.status), + } + + def _get_server(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_server = self.client.servers.get_by_id(self.module.params.get("id")) + else: + self.hcloud_server = self.client.servers.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_server(self): + self.module.fail_on_missing_params(required_params=["name", "server_type", "image"]) + + server_type = self._get_server_type() + + params = { + "name": self.module.params.get("name"), + "server_type": server_type, + "user_data": self.module.params.get("user_data"), + "labels": self.module.params.get("labels"), + "image": self._get_image(server_type), + "placement_group": self._get_placement_group(), + "public_net": ServerCreatePublicNetwork( + enable_ipv4=self.module.params.get("enable_ipv4"), + enable_ipv6=self.module.params.get("enable_ipv6"), + ), + } + + if self.module.params.get("ipv4") is not None: + primary_ip = self.client.primary_ips.get_by_name(self.module.params.get("ipv4")) + if not primary_ip: + primary_ip = self.client.primary_ips.get_by_id(self.module.params.get("ipv4")) + params["public_net"].ipv4 = primary_ip + + if self.module.params.get("ipv6") is not None: + primary_ip = self.client.primary_ips.get_by_name(self.module.params.get("ipv6")) + if not primary_ip: + primary_ip = self.client.primary_ips.get_by_id(self.module.params.get("ipv6")) + params["public_net"].ipv6 = primary_ip + + if self.module.params.get("private_networks") is not None: + _networks = [] + for network_name_or_id in self.module.params.get("private_networks"): + _networks.append( + self.client.networks.get_by_name(network_name_or_id) + or self.client.networks.get_by_id(network_name_or_id) + ) + params["networks"] = _networks + + if self.module.params.get("ssh_keys") is not None: + params["ssh_keys"] = [SSHKey(name=ssh_key_name) for ssh_key_name in self.module.params.get("ssh_keys")] + + if self.module.params.get("volumes") is not None: + params["volumes"] = [Volume(id=volume_id) for volume_id in self.module.params.get("volumes")] + if self.module.params.get("firewalls") is not None: + params["firewalls"] = [] + for firewall_param in self.module.params.get("firewalls"): + firewall = self.client.firewalls.get_by_name(firewall_param) + if firewall is not None: + # When firewall name is not available look for id instead + params["firewalls"].append(firewall) + else: + params["firewalls"].append(self.client.firewalls.get_by_id(firewall_param)) + + if self.module.params.get("location") is None and self.module.params.get("datacenter") is None: + # When not given, the API will choose the location. + params["location"] = None + params["datacenter"] = None + elif self.module.params.get("location") is not None and self.module.params.get("datacenter") is None: + params["location"] = self.client.locations.get_by_name(self.module.params.get("location")) + elif self.module.params.get("location") is None and self.module.params.get("datacenter") is not None: + params["datacenter"] = self.client.datacenters.get_by_name(self.module.params.get("datacenter")) + + if self.module.params.get("state") == "stopped": + params["start_after_create"] = False + if not self.module.check_mode: + try: + resp = self.client.servers.create(**params) + self.result["root_password"] = resp.root_password + resp.action.wait_until_finished(max_retries=1000) + [action.wait_until_finished() for action in resp.next_actions] + + rescue_mode = self.module.params.get("rescue_mode") + if rescue_mode: + self._get_server() + self._set_rescue_mode(rescue_mode) + + backups = self.module.params.get("backups") + if backups: + self._get_server() + self.hcloud_server.enable_backup().wait_until_finished() + + delete_protection = self.module.params.get("delete_protection") + rebuild_protection = self.module.params.get("rebuild_protection") + if delete_protection is not None and rebuild_protection is not None: + self._get_server() + self.hcloud_server.change_protection( + delete=delete_protection, + rebuild=rebuild_protection, + ).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_server() + + def _get_image(self, server_type): + image_resp = self.client.images.get_list( + name=self.module.params.get("image"), + architecture=server_type.architecture, + include_deprecated=True, + ) + images = getattr(image_resp, "images") + image = None + if images is not None and len(images) > 0: + # If image name is not available look for id instead + image = images[0] + else: + try: + image = self.client.images.get_by_id(self.module.params.get("image")) + except HCloudException as exception: + self.fail_json_hcloud(exception, msg=f"Image {self.module.params.get('image')} was not found") + if image.deprecated is not None: + available_until = image.deprecated + timedelta(days=90) + if self.module.params.get("allow_deprecated_image"): + self.module.warn( + f"You try to use a deprecated image. The image {image.name} will " + f"continue to be available until {available_until.strftime('%Y-%m-%d')}." + ) + else: + self.module.fail_json( + msg=( + f"You try to use a deprecated image. The image {image.name} will " + f"continue to be available until {available_until.strftime('%Y-%m-%d')}. " + "If you want to use this image use allow_deprecated_image=true." + ) + ) + return image + + def _get_server_type(self): + server_type = self.client.server_types.get_by_name(self.module.params.get("server_type")) + if server_type is None: + try: + server_type = self.client.server_types.get_by_id(self.module.params.get("server_type")) + except HCloudException as exception: + self.fail_json_hcloud( + exception, + msg=f"server_type {self.module.params.get('server_type')} was not found", + ) + + self._check_and_warn_deprecated_server(server_type) + + return server_type + + def _check_and_warn_deprecated_server(self, server_type): + if server_type.deprecation is None: + return + + if server_type.deprecation.unavailable_after < datetime.now(timezone.utc): + self.module.warn( + f"Attention: The server plan {server_type.name} is deprecated and can " + "no longer be ordered. Existing servers of that plan will continue to " + "work as before and no action is required on your part. " + "It is possible to migrate this server to another server plan by setting " + "the server_type parameter on the hetzner.hcloud.server module." + ) + else: + server_type_unavailable_date = server_type.deprecation.unavailable_after.strftime("%Y-%m-%d") + self.module.warn( + f"Attention: The server plan {server_type.name} is deprecated and will " + f"no longer be available for order as of {server_type_unavailable_date}. " + "Existing servers of that plan will continue to work as before and no " + "action is required on your part. " + "It is possible to migrate this server to another server plan by setting " + "the server_type parameter on the hetzner.hcloud.server module." + ) + + def _get_placement_group(self): + if self.module.params.get("placement_group") is None: + return None + + placement_group = self.client.placement_groups.get_by_name(self.module.params.get("placement_group")) + if placement_group is None: + try: + placement_group = self.client.placement_groups.get_by_id(self.module.params.get("placement_group")) + except HCloudException as exception: + self.fail_json_hcloud( + exception, + msg=f"placement_group {self.module.params.get('placement_group')} was not found", + ) + + return placement_group + + def _get_primary_ip(self, field): + if self.module.params.get(field) is None: + return None + + primary_ip = self.client.primary_ips.get_by_name(self.module.params.get(field)) + if primary_ip is None: + try: + primary_ip = self.client.primary_ips.get_by_id(self.module.params.get(field)) + except HCloudException as exception: + self.fail_json_hcloud(exception, msg=f"primary_ip {self.module.params.get(field)} was not found") + + return primary_ip + + def _update_server(self): + if "force_upgrade" in self.module.params and self.module.params.get("force_upgrade") is not None: + self.module.warn("force_upgrade is deprecated, use force instead") + + try: + previous_server_status = self.hcloud_server.status + + rescue_mode = self.module.params.get("rescue_mode") + if rescue_mode and self.hcloud_server.rescue_enabled is False: + if not self.module.check_mode: + self._set_rescue_mode(rescue_mode) + self._mark_as_changed() + elif not rescue_mode and self.hcloud_server.rescue_enabled is True: + if not self.module.check_mode: + self.hcloud_server.disable_rescue().wait_until_finished() + self._mark_as_changed() + + backups = self.module.params.get("backups") + if backups and self.hcloud_server.backup_window is None: + if not self.module.check_mode: + self.hcloud_server.enable_backup().wait_until_finished() + self._mark_as_changed() + elif backups is not None and not backups and self.hcloud_server.backup_window is not None: + if not self.module.check_mode: + self.hcloud_server.disable_backup().wait_until_finished() + self._mark_as_changed() + + labels = self.module.params.get("labels") + if labels is not None and labels != self.hcloud_server.labels: + if not self.module.check_mode: + self.hcloud_server.update(labels=labels) + self._mark_as_changed() + + wanted_firewalls = self.module.params.get("firewalls") + if wanted_firewalls is not None: + # Removing existing but not wanted firewalls + for current_firewall in self.hcloud_server.public_net.firewalls: + if current_firewall.firewall.name not in wanted_firewalls: + self._mark_as_changed() + if self.module.check_mode: + continue + + firewall_resource = FirewallResource(type="server", server=self.hcloud_server) + actions = self.client.firewalls.remove_from_resources( + current_firewall.firewall, + [firewall_resource], + ) + for action in actions: + action.wait_until_finished() + + # Adding wanted firewalls that doesn't exist yet + for firewall_name in wanted_firewalls: + found = False + for firewall in self.hcloud_server.public_net.firewalls: + if firewall.firewall.name == firewall_name: + found = True + break + + if not found: + self._mark_as_changed() + if not self.module.check_mode: + firewall = self.client.firewalls.get_by_name(firewall_name) + if firewall is None: + self.module.fail_json(msg=f"firewall {firewall_name} was not found") + firewall_resource = FirewallResource(type="server", server=self.hcloud_server) + actions = self.client.firewalls.apply_to_resources(firewall, [firewall_resource]) + for action in actions: + action.wait_until_finished() + + if "placement_group" in self.module.params: + if self.module.params["placement_group"] is None and self.hcloud_server.placement_group is not None: + if not self.module.check_mode: + self.hcloud_server.remove_from_placement_group().wait_until_finished() + self._mark_as_changed() + else: + placement_group = self._get_placement_group() + if placement_group is not None and ( + self.hcloud_server.placement_group is None + or self.hcloud_server.placement_group.id != placement_group.id + ): + self.stop_server_if_forced() + if not self.module.check_mode: + self.hcloud_server.add_to_placement_group(placement_group).wait_until_finished() + self._mark_as_changed() + + if "ipv4" in self.module.params: + if ( + self.module.params["ipv4"] is None + and self.hcloud_server.public_net.primary_ipv4 is not None + and not self.module.params.get("enable_ipv4") + ): + self.stop_server_if_forced() + if not self.module.check_mode: + self.hcloud_server.public_net.primary_ipv4.unassign().wait_until_finished() + self._mark_as_changed() + else: + primary_ip = self._get_primary_ip("ipv4") + if primary_ip is not None and ( + self.hcloud_server.public_net.primary_ipv4 is None + or self.hcloud_server.public_net.primary_ipv4.id != primary_ip.id + ): + self.stop_server_if_forced() + if not self.module.check_mode: + if self.hcloud_server.public_net.primary_ipv4: + self.hcloud_server.public_net.primary_ipv4.unassign().wait_until_finished() + primary_ip.assign(self.hcloud_server.id, "server").wait_until_finished() + self._mark_as_changed() + if "ipv6" in self.module.params: + if ( + (self.module.params["ipv6"] is None or self.module.params["ipv6"] == "") + and self.hcloud_server.public_net.primary_ipv6 is not None + and not self.module.params.get("enable_ipv6") + ): + self.stop_server_if_forced() + if not self.module.check_mode: + self.hcloud_server.public_net.primary_ipv6.unassign().wait_until_finished() + self._mark_as_changed() + else: + primary_ip = self._get_primary_ip("ipv6") + if primary_ip is not None and ( + self.hcloud_server.public_net.primary_ipv6 is None + or self.hcloud_server.public_net.primary_ipv6.id != primary_ip.id + ): + self.stop_server_if_forced() + if not self.module.check_mode: + if self.hcloud_server.public_net.primary_ipv6 is not None: + self.hcloud_server.public_net.primary_ipv6.unassign().wait_until_finished() + primary_ip.assign(self.hcloud_server.id, "server").wait_until_finished() + self._mark_as_changed() + if "private_networks" in self.module.params and self.module.params["private_networks"] is not None: + if not bool(self.module.params["private_networks"]): + # This handles None, "" and [] + networks_target = {} + else: + _networks = {} + for network_name_or_id in self.module.params.get("private_networks"): + _found_network = self.client.networks.get_by_name( + network_name_or_id + ) or self.client.networks.get_by_id(network_name_or_id) + _networks.update({_found_network.id: _found_network}) + networks_target = _networks + networks_is = dict() + for p_network in self.hcloud_server.private_net: + networks_is.update({p_network.network.id: p_network.network}) + for network_id in set(list(networks_is) + list(networks_target)): + if network_id in networks_is and network_id not in networks_target: + self.stop_server_if_forced() + if not self.module.check_mode: + self.hcloud_server.detach_from_network(networks_is[network_id]).wait_until_finished() + self._mark_as_changed() + elif network_id in networks_target and network_id not in networks_is: + self.stop_server_if_forced() + if not self.module.check_mode: + self.hcloud_server.attach_to_network(networks_target[network_id]).wait_until_finished() + self._mark_as_changed() + + server_type = self.module.params.get("server_type") + if server_type is not None: + if self.hcloud_server.server_type.name == server_type: + # Check if we should warn for using an deprecated server type + self._check_and_warn_deprecated_server(self.hcloud_server.server_type) + + else: + # Server type should be changed + self.stop_server_if_forced() + + timeout = 100 + if self.module.params.get("upgrade_disk"): + timeout = 1000 # When we upgrade the disk to the resize progress takes some more time. + if not self.module.check_mode: + self.hcloud_server.change_type( + server_type=self._get_server_type(), + upgrade_disk=self.module.params.get("upgrade_disk"), + ).wait_until_finished(timeout) + self._mark_as_changed() + + if not self.module.check_mode and ( + (self.module.params.get("state") == "present" and previous_server_status == Server.STATUS_RUNNING) + or self.module.params.get("state") == "started" + ): + self.start_server() + + delete_protection = self.module.params.get("delete_protection") + rebuild_protection = self.module.params.get("rebuild_protection") + if (delete_protection is not None and rebuild_protection is not None) and ( + delete_protection != self.hcloud_server.protection["delete"] + or rebuild_protection != self.hcloud_server.protection["rebuild"] + ): + if not self.module.check_mode: + self.hcloud_server.change_protection( + delete=delete_protection, + rebuild=rebuild_protection, + ).wait_until_finished() + self._mark_as_changed() + self._get_server() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _set_rescue_mode(self, rescue_mode): + if self.module.params.get("ssh_keys"): + resp = self.hcloud_server.enable_rescue( + type=rescue_mode, + ssh_keys=[ + self.client.ssh_keys.get_by_name(ssh_key_name).id + for ssh_key_name in self.module.params.get("ssh_keys") + ], + ) + else: + resp = self.hcloud_server.enable_rescue(type=rescue_mode) + resp.action.wait_until_finished() + self.result["root_password"] = resp.root_password + + def start_server(self): + try: + if self.hcloud_server: + if self.hcloud_server.status != Server.STATUS_RUNNING: + if not self.module.check_mode: + self.client.servers.power_on(self.hcloud_server).wait_until_finished() + self._mark_as_changed() + self._get_server() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def stop_server(self): + try: + if self.hcloud_server: + if self.hcloud_server.status != Server.STATUS_OFF: + if not self.module.check_mode: + self.client.servers.power_off(self.hcloud_server).wait_until_finished() + self._mark_as_changed() + self._get_server() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def stop_server_if_forced(self): + previous_server_status = self.hcloud_server.status + if previous_server_status == Server.STATUS_RUNNING and not self.module.check_mode: + if ( + self.module.params.get("force_upgrade") + or self.module.params.get("force") + or self.module.params.get("state") == "stopped" + ): + self.stop_server() # Only stopped server can be upgraded + return previous_server_status + else: + self.module.warn( + f"You can not upgrade a running instance {self.hcloud_server.name}. " + "You need to stop the instance or use force=true." + ) + + return None + + def rebuild_server(self): + self.module.fail_on_missing_params(required_params=["image"]) + try: + if not self.module.check_mode: + image = self._get_image(self.hcloud_server.server_type) + # When we rebuild the server progress takes some more time. + resp = self.client.servers.rebuild(self.hcloud_server, image, return_response=True) + resp.action.wait_until_finished(1000) + self._mark_as_changed() + + self._get_server() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def present_server(self): + self._get_server() + if self.hcloud_server is None: + self._create_server() + else: + self._update_server() + + def delete_server(self): + try: + self._get_server() + if self.hcloud_server is not None: + if not self.module.check_mode: + self.client.servers.delete(self.hcloud_server).wait_until_finished() + self._mark_as_changed() + self.hcloud_server = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + image={"type": "str"}, + server_type={"type": "str"}, + location={"type": "str"}, + datacenter={"type": "str"}, + user_data={"type": "str"}, + ssh_keys={"type": "list", "elements": "str", "no_log": False}, + volumes={"type": "list", "elements": "str"}, + firewalls={"type": "list", "elements": "str"}, + labels={"type": "dict"}, + backups={"type": "bool"}, + upgrade_disk={"type": "bool", "default": False}, + enable_ipv4={"type": "bool", "default": True}, + enable_ipv6={"type": "bool", "default": True}, + ipv4={"type": "str"}, + ipv6={"type": "str"}, + private_networks={"type": "list", "elements": "str", "default": None}, + force={"type": "bool", "default": False}, + force_upgrade={"type": "bool"}, + allow_deprecated_image={"type": "bool", "default": False}, + rescue_mode={"type": "str"}, + delete_protection={"type": "bool"}, + rebuild_protection={"type": "bool"}, + placement_group={"type": "str"}, + state={ + "choices": ["absent", "present", "restarted", "started", "stopped", "rebuild"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name"]], + mutually_exclusive=[["location", "datacenter"]], + required_together=[["delete_protection", "rebuild_protection"]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudServer.define_module() + + hcloud = AnsibleHCloudServer(module) + state = module.params.get("state") + if state == "absent": + hcloud.delete_server() + elif state == "present": + hcloud.present_server() + elif state == "started": + hcloud.present_server() + hcloud.start_server() + elif state == "stopped": + hcloud.present_server() + hcloud.stop_server() + elif state == "restarted": + hcloud.present_server() + hcloud.stop_server() + hcloud.start_server() + elif state == "rebuild": + hcloud.present_server() + hcloud.rebuild_server() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/server_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/server_info.py new file mode 100644 index 000000000..cee1634cb --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server_info.py @@ -0,0 +1,238 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: server_info + +short_description: Gather infos about your Hetzner Cloud servers. + + +description: + - Gather infos about your Hetzner Cloud servers. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the server you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the server you want to get. + type: str + label_selector: + description: + - The label selector for the server you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud server infos + hetzner.hcloud.server_info: + register: output + +- name: Print the gathered infos + debug: + var: output.hcloud_server_info +""" + +RETURN = """ +hcloud_server_info: + description: The server infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the server + returned: always + type: int + sample: 1937415 + name: + description: Name of the server + returned: always + type: str + sample: my-server + created: + description: Point in time when the Server was created (in ISO-8601 format) + returned: always + type: str + sample: "2023-11-06T13:36:56+00:00" + status: + description: Status of the server + returned: always + type: str + sample: running + server_type: + description: Name of the server type of the server + returned: always + type: str + sample: cx11 + ipv4_address: + description: Public IPv4 address of the server + returned: always + type: str + sample: 116.203.104.109 + ipv6: + description: IPv6 network of the server + returned: always + type: str + sample: 2a01:4f8:1c1c:c140::/64 + private_networks: + description: List of private networks the server is attached to (name) + returned: always + type: list + elements: str + sample: ['my-network', 'another-network'] + private_networks_info: + description: List of private networks the server is attached to (dict with name and ip) + returned: always + type: list + elements: dict + sample: [{'name': 'my-network', 'ip': '192.168.1.1'}, {'name': 'another-network', 'ip': '10.185.50.40'}] + location: + description: Name of the location of the server + returned: always + type: str + sample: fsn1 + placement_group: + description: Placement Group of the server + type: str + returned: always + sample: 4711 + version_added: "1.5.0" + datacenter: + description: Name of the datacenter of the server + returned: always + type: str + sample: fsn1-dc14 + rescue_enabled: + description: True if rescue mode is enabled, Server will then boot into rescue system on next reboot + returned: always + type: bool + sample: false + backup_window: + description: Time window (UTC) in which the backup will run, or null if the backups are not enabled + returned: always + type: bool + sample: 22-02 + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict + delete_protection: + description: True if server is protected for deletion + type: bool + returned: always + sample: false + version_added: "0.1.0" + rebuild_protection: + description: True if server is protected for rebuild + type: bool + returned: always + sample: false + version_added: "0.1.0" +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.servers import BoundServer + + +class AnsibleHCloudServerInfo(AnsibleHCloud): + represent = "hcloud_server_info" + + hcloud_server_info: list[BoundServer] | None = None + + def _prepare_result(self): + tmp = [] + + for server in self.hcloud_server_info: + if server is not None: + image = None if server.image is None else to_native(server.image.name) + placement_group = None if server.placement_group is None else to_native(server.placement_group.name) + ipv4_address = None if server.public_net.ipv4 is None else to_native(server.public_net.ipv4.ip) + ipv6 = None if server.public_net.ipv6 is None else to_native(server.public_net.ipv6.ip) + backup_window = None if server.backup_window is None else to_native(server.backup_window) + tmp.append( + { + "id": to_native(server.id), + "name": to_native(server.name), + "created": to_native(server.created.isoformat()), + "ipv4_address": ipv4_address, + "ipv6": ipv6, + "private_networks": [to_native(net.network.name) for net in server.private_net], + "private_networks_info": [ + {"name": to_native(net.network.name), "ip": net.ip} for net in server.private_net + ], + "image": image, + "server_type": to_native(server.server_type.name), + "datacenter": to_native(server.datacenter.name), + "location": to_native(server.datacenter.location.name), + "placement_group": placement_group, + "rescue_enabled": server.rescue_enabled, + "backup_window": backup_window, + "labels": server.labels, + "status": to_native(server.status), + "delete_protection": server.protection["delete"], + "rebuild_protection": server.protection["rebuild"], + } + ) + return tmp + + def get_servers(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_server_info = [self.client.servers.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_server_info = [self.client.servers.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("label_selector") is not None: + self.hcloud_server_info = self.client.servers.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_server_info = self.client.servers.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + label_selector={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudServerInfo.define_module() + hcloud = AnsibleHCloudServerInfo(module) + + hcloud.get_servers() + result = hcloud.get_result() + + ansible_info = {"hcloud_server_info": result["hcloud_server_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/server_network.py b/ansible_collections/hetzner/hcloud/plugins/modules/server_network.py new file mode 100644 index 000000000..ca80a8a76 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server_network.py @@ -0,0 +1,246 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: server_network + +short_description: Manage the relationship between Hetzner Cloud Networks and servers + + +description: + - Create and delete the relationship Hetzner Cloud Networks and servers + +author: + - Lukas Kaemmerling (@lkaemmerling) + +options: + network: + description: + - Name or ID of the Hetzner Cloud Networks. + type: str + required: true + server: + description: + - Name or ID of the Hetzner Cloud server. + type: str + required: true + ip: + description: + - The IP the server should have. + type: str + alias_ips: + description: + - Alias IPs the server has. + type: list + elements: str + state: + description: + - State of the server_network. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic server network + hetzner.hcloud.server_network: + network: my-network + server: my-server + state: present + +- name: Create a server network and specify the ip address + hetzner.hcloud.server_network: + network: my-network + server: my-server + ip: 10.0.0.1 + state: present + +- name: Create a server network and add alias ips + hetzner.hcloud.server_network: + network: my-network + server: my-server + ip: 10.0.0.1 + alias_ips: + - 10.1.0.1 + - 10.2.0.1 + state: present + +- name: Ensure the server network is absent (remove if needed) + hetzner.hcloud.server_network: + network: my-network + server: my-server + state: absent +""" + +RETURN = """ +hcloud_server_network: + description: The relationship between a server and a network + returned: always + type: complex + contains: + network: + description: Name of the Network + type: str + returned: always + sample: my-network + server: + description: Name of the server + type: str + returned: always + sample: my-server + ip: + description: IP of the server within the Network ip range + type: str + returned: always + sample: 10.0.0.8 + alias_ips: + description: Alias IPs of the server within the Network ip range + type: list + elements: str + returned: always + sample: [10.1.0.1, ...] +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import APIException, HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork +from ..module_utils.vendor.hcloud.servers import BoundServer, PrivateNet + + +class AnsibleHCloudServerNetwork(AnsibleHCloud): + represent = "hcloud_server_network" + + hcloud_network: BoundNetwork | None = None + hcloud_server: BoundServer | None = None + hcloud_server_network: PrivateNet | None = None + + def _prepare_result(self): + return { + "network": to_native(self.hcloud_network.name), + "server": to_native(self.hcloud_server.name), + "ip": to_native(self.hcloud_server_network.ip), + "alias_ips": self.hcloud_server_network.alias_ips, + } + + def _get_server_and_network(self): + try: + self.hcloud_network = self._client_get_by_name_or_id( + "networks", + self.module.params.get("network"), + ) + self.hcloud_server = self._client_get_by_name_or_id( + "servers", + self.module.params.get("server"), + ) + self.hcloud_server_network = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _get_server_network(self): + for private_net in self.hcloud_server.private_net: + if private_net.network.id == self.hcloud_network.id: + self.hcloud_server_network = private_net + + def _create_server_network(self): + params = { + "network": self.hcloud_network, + } + + if self.module.params.get("ip") is not None: + params["ip"] = self.module.params.get("ip") + if self.module.params.get("alias_ips") is not None: + params["alias_ips"] = self.module.params.get("alias_ips") + + if not self.module.check_mode: + try: + self.hcloud_server.attach_to_network(**params).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + self._mark_as_changed() + self._get_server_and_network() + self._get_server_network() + + def _update_server_network(self): + params = { + "network": self.hcloud_network, + } + alias_ips = self.module.params.get("alias_ips") + if alias_ips is not None and sorted(self.hcloud_server_network.alias_ips) != sorted(alias_ips): + params["alias_ips"] = alias_ips + + if not self.module.check_mode: + try: + self.hcloud_server.change_alias_ips(**params).wait_until_finished() + except APIException as exception: + self.fail_json_hcloud(exception) + + self._mark_as_changed() + self._get_server_and_network() + self._get_server_network() + + def present_server_network(self): + self._get_server_and_network() + self._get_server_network() + if self.hcloud_server_network is None: + self._create_server_network() + else: + self._update_server_network() + + def delete_server_network(self): + self._get_server_and_network() + self._get_server_network() + if self.hcloud_server_network is not None and self.hcloud_server is not None: + if not self.module.check_mode: + try: + self.hcloud_server.detach_from_network(self.hcloud_server_network.network).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self.hcloud_server_network = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + network={"type": "str", "required": True}, + server={"type": "str", "required": True}, + ip={"type": "str"}, + alias_ips={"type": "list", "elements": "str"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudServerNetwork.define_module() + + hcloud = AnsibleHCloudServerNetwork(module) + state = module.params["state"] + if state == "absent": + hcloud.delete_server_network() + elif state == "present": + hcloud.present_server_network() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py new file mode 100644 index 000000000..61f1f5011 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/server_type_info.py @@ -0,0 +1,204 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: server_type_info + +short_description: Gather infos about the Hetzner Cloud server types. + + +description: + - Gather infos about your Hetzner Cloud server types. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the server type you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the server type you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud server type infos + hetzner.hcloud.server_type_info: + register: output + +- name: Print the gathered infos + debug: + var: output.hcloud_server_type_info +""" + +RETURN = """ +hcloud_server_type_info: + description: The server type infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the server type + returned: always + type: int + sample: 1937415 + name: + description: Name of the server type + returned: always + type: str + sample: fsn1 + description: + description: Detail description of the server type + returned: always + type: str + sample: Falkenstein DC Park 1 + cores: + description: Number of cpu cores a server of this type will have + returned: always + type: int + sample: 1 + memory: + description: Memory a server of this type will have in GB + returned: always + type: int + sample: 1 + disk: + description: Disk size a server of this type will have in GB + returned: always + type: int + sample: 25 + storage_type: + description: Type of server boot drive + returned: always + type: str + sample: local + cpu_type: + description: Type of cpu + returned: always + type: str + sample: shared + architecture: + description: Architecture of cpu + returned: always + type: str + sample: x86 + included_traffic: + description: Free traffic per month in bytes + returned: always + type: int + sample: 21990232555520 + deprecation: + description: | + Describes if, when & how the resources was deprecated. + If this field is set to None the resource is not deprecated. If it has a value, it is considered deprecated. + returned: success + type: dict + contains: + announced: + description: Date of when the deprecation was announced. + returned: success + type: str + sample: "2021-11-09T09:00:00+00:00" + unavailable_after: + description: | + After the time in this field, the resource will not be available from the general listing + endpoint of the resource type, and it can not be used in new resources. For example, if this is + an image, you can not create new servers with this image after the mentioned date. + returned: success + type: str + sample: "2021-12-01T00:00:00+00:00" + +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.server_types import BoundServerType + + +class AnsibleHCloudServerTypeInfo(AnsibleHCloud): + represent = "hcloud_server_type_info" + + hcloud_server_type_info: list[BoundServerType] | None = None + + def _prepare_result(self): + tmp = [] + + for server_type in self.hcloud_server_type_info: + if server_type is not None: + tmp.append( + { + "id": to_native(server_type.id), + "name": to_native(server_type.name), + "description": to_native(server_type.description), + "cores": server_type.cores, + "memory": server_type.memory, + "disk": server_type.disk, + "storage_type": to_native(server_type.storage_type), + "cpu_type": to_native(server_type.cpu_type), + "architecture": to_native(server_type.architecture), + "included_traffic": server_type.included_traffic, + "deprecation": ( + { + "announced": server_type.deprecation.announced.isoformat(), + "unavailable_after": server_type.deprecation.unavailable_after.isoformat(), + } + if server_type.deprecation is not None + else None + ), + } + ) + return tmp + + def get_server_types(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_server_type_info = [self.client.server_types.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_server_type_info = [self.client.server_types.get_by_name(self.module.params.get("name"))] + else: + self.hcloud_server_type_info = self.client.server_types.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudServerTypeInfo.define_module() + hcloud = AnsibleHCloudServerTypeInfo(module) + + hcloud.get_server_types() + result = hcloud.get_result() + + ansible_info = {"hcloud_server_type_info": result["hcloud_server_type_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py new file mode 100644 index 000000000..349c52c68 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key.py @@ -0,0 +1,234 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: ssh_key + +short_description: Create and manage ssh keys on the Hetzner Cloud. + + +description: + - Create, update and manage ssh keys on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the Hetzner Cloud ssh_key to manage. + - Only required if no ssh_key I(name) is given + type: int + name: + description: + - The Name of the Hetzner Cloud ssh_key to manage. + - Only required if no ssh_key I(id) is given or a ssh_key does not exist. + type: str + fingerprint: + description: + - The Fingerprint of the Hetzner Cloud ssh_key to manage. + - Only required if no ssh_key I(id) or I(name) is given. + type: str + labels: + description: + - User-defined labels (key-value pairs) + type: dict + public_key: + description: + - The Public Key to add. + - Required if ssh_key does not exist. + type: str + state: + description: + - State of the ssh_key. + default: present + choices: [ absent, present ] + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Create a basic ssh_key + hetzner.hcloud.ssh_key: + name: my-ssh_key + public_key: ssh-rsa AAAjjk76kgf...Xt + state: present + +- name: Create a ssh_key with labels + hetzner.hcloud.ssh_key: + name: my-ssh_key + public_key: ssh-rsa AAAjjk76kgf...Xt + labels: + key: value + mylabel: 123 + state: present + +- name: Ensure the ssh_key is absent (remove if needed) + hetzner.hcloud.ssh_key: + name: my-ssh_key + state: absent +""" + +RETURN = """ +hcloud_ssh_key: + description: The ssh_key instance + returned: Always + type: complex + contains: + id: + description: ID of the ssh_key + type: int + returned: Always + sample: 12345 + name: + description: Name of the ssh_key + type: str + returned: Always + sample: my-ssh-key + fingerprint: + description: Fingerprint of the ssh_key + type: str + returned: Always + sample: b7:2f:30:a0:2f:6c:58:6c:21:04:58:61:ba:06:3b:2f + public_key: + description: Public key of the ssh_key + type: str + returned: Always + sample: "ssh-rsa AAAjjk76kgf...Xt" + labels: + description: User-defined labels (key-value pairs) + type: dict + returned: Always + sample: + key: value + mylabel: 123 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.ssh_keys import BoundSSHKey + + +class AnsibleHCloudSSHKey(AnsibleHCloud): + represent = "hcloud_ssh_key" + + hcloud_ssh_key: BoundSSHKey | None = None + + def _prepare_result(self): + return { + "id": to_native(self.hcloud_ssh_key.id), + "name": to_native(self.hcloud_ssh_key.name), + "fingerprint": to_native(self.hcloud_ssh_key.fingerprint), + "public_key": to_native(self.hcloud_ssh_key.public_key), + "labels": self.hcloud_ssh_key.labels, + } + + def _get_ssh_key(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_ssh_key = self.client.ssh_keys.get_by_id(self.module.params.get("id")) + elif self.module.params.get("fingerprint") is not None: + self.hcloud_ssh_key = self.client.ssh_keys.get_by_fingerprint(self.module.params.get("fingerprint")) + elif self.module.params.get("name") is not None: + self.hcloud_ssh_key = self.client.ssh_keys.get_by_name(self.module.params.get("name")) + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_ssh_key(self): + self.module.fail_on_missing_params(required_params=["name", "public_key"]) + params = { + "name": self.module.params.get("name"), + "public_key": self.module.params.get("public_key"), + "labels": self.module.params.get("labels"), + } + + if not self.module.check_mode: + try: + self.client.ssh_keys.create(**params) + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_ssh_key() + + def _update_ssh_key(self): + name = self.module.params.get("name") + if name is not None and self.hcloud_ssh_key.name != name: + self.module.fail_on_missing_params(required_params=["id"]) + if not self.module.check_mode: + self.hcloud_ssh_key.update(name=name) + self._mark_as_changed() + + labels = self.module.params.get("labels") + if labels is not None and self.hcloud_ssh_key.labels != labels: + if not self.module.check_mode: + self.hcloud_ssh_key.update(labels=labels) + self._mark_as_changed() + + self._get_ssh_key() + + def present_ssh_key(self): + self._get_ssh_key() + if self.hcloud_ssh_key is None: + self._create_ssh_key() + else: + self._update_ssh_key() + + def delete_ssh_key(self): + self._get_ssh_key() + if self.hcloud_ssh_key is not None: + if not self.module.check_mode: + try: + self.client.ssh_keys.delete(self.hcloud_ssh_key) + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self.hcloud_ssh_key = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + public_key={"type": "str"}, + fingerprint={"type": "str"}, + labels={"type": "dict"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name", "fingerprint"]], + required_if=[["state", "present", ["name"]]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudSSHKey.define_module() + + hcloud = AnsibleHCloudSSHKey(module) + state = module.params.get("state") + if state == "absent": + hcloud.delete_ssh_key() + elif state == "present": + hcloud.present_ssh_key() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py new file mode 100644 index 000000000..7a4ab5928 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/ssh_key_info.py @@ -0,0 +1,156 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: ssh_key_info +short_description: Gather infos about your Hetzner Cloud ssh_keys. +description: + - Gather facts about your Hetzner Cloud ssh_keys. +author: + - Christopher Schmitt (@cschmitt-hcloud) +options: + id: + description: + - The ID of the ssh key you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the ssh key you want to get. + type: str + fingerprint: + description: + - The fingerprint of the ssh key you want to get. + type: str + label_selector: + description: + - The label selector for the ssh key you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud sshkey infos + hetzner.hcloud.ssh_key_info: + register: output +- name: Print the gathered infos + debug: + var: output.hcloud_ssh_key_info +""" + +RETURN = """ +hcloud_ssh_key_info: + description: The ssh key instances + returned: Always + type: complex + contains: + id: + description: Numeric identifier of the ssh_key + returned: always + type: int + sample: 1937415 + name: + description: Name of the ssh_key + returned: always + type: str + sample: my-ssh-key + fingerprint: + description: Fingerprint of the ssh key + returned: always + type: str + sample: 0e:e0:bd:c7:2d:1f:69:49:94:44:91:f1:19:fd:35:f3 + public_key: + description: The actual public key + returned: always + type: str + sample: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGpl/tnk74nnQJxxLAtutUApUZMRJxryKh7VXkNbd4g9 john@example.com" + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.ssh_keys import BoundSSHKey + + +class AnsibleHCloudSSHKeyInfo(AnsibleHCloud): + represent = "hcloud_ssh_key_info" + + hcloud_ssh_key_info: list[BoundSSHKey] | None = None + + def _prepare_result(self): + ssh_keys = [] + + for ssh_key in self.hcloud_ssh_key_info: + if ssh_key: + ssh_keys.append( + { + "id": to_native(ssh_key.id), + "name": to_native(ssh_key.name), + "fingerprint": to_native(ssh_key.fingerprint), + "public_key": to_native(ssh_key.public_key), + "labels": ssh_key.labels, + } + ) + return ssh_keys + + def get_ssh_keys(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_ssh_key_info = [self.client.ssh_keys.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("fingerprint") is not None: + self.hcloud_ssh_key_info = [ + self.client.ssh_keys.get_by_fingerprint(self.module.params.get("fingerprint")) + ] + elif self.module.params.get("label_selector") is not None: + self.hcloud_ssh_key_info = self.client.ssh_keys.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_ssh_key_info = self.client.ssh_keys.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + fingerprint={"type": "str"}, + label_selector={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudSSHKeyInfo.define_module() + hcloud = AnsibleHCloudSSHKeyInfo(module) + + hcloud.get_ssh_keys() + result = hcloud.get_result() + + ansible_info = {"hcloud_ssh_key_info": result["hcloud_ssh_key_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py b/ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py new file mode 100644 index 000000000..aea40bb13 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/subnetwork.py @@ -0,0 +1,236 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: subnetwork + +short_description: Manage cloud subnetworks on the Hetzner Cloud. + + +description: + - Create, update and delete cloud subnetworks on the Hetzner Cloud. + +author: + - Lukas Kaemmerling (@lkaemmerling) + +options: + network: + description: + - The name or ID of the Hetzner Cloud Networks. + type: str + required: true + ip_range: + description: + - IP range of the subnetwork. + type: str + required: true + type: + description: + - Type of subnetwork. + type: str + choices: [ server, cloud, vswitch ] + required: true + network_zone: + description: + - Name of network zone. + type: str + required: true + vswitch_id: + description: + - ID of the vSwitch you want to couple with your Network. + - Required if type == vswitch + type: int + state: + description: + - State of the subnetwork. + default: present + choices: [ absent, present ] + type: str + +extends_documentation_fragment: +- hetzner.hcloud.hcloud +""" + +EXAMPLES = """ +- name: Create a basic subnetwork + hetzner.hcloud.subnetwork: + network: my-network + ip_range: 10.0.0.0/16 + network_zone: eu-central + type: cloud + state: present + +- name: Create a basic subnetwork + hetzner.hcloud.subnetwork: + network: my-vswitch-network + ip_range: 10.0.0.0/24 + network_zone: eu-central + type: vswitch + vswitch_id: 123 + state: present + +- name: Ensure the subnetwork is absent (remove if needed) + hetzner.hcloud.subnetwork: + network: my-network + ip_range: 10.0.0.0/8 + network_zone: eu-central + type: cloud + state: absent +""" + +RETURN = """ +hcloud_subnetwork: + description: One Subnet of a Network + returned: always + type: complex + contains: + network: + description: Name of the Network + type: str + returned: always + sample: my-network + ip_range: + description: IP range of the Network + type: str + returned: always + sample: 10.0.0.0/8 + type: + description: Type of subnetwork + type: str + returned: always + sample: server + network_zone: + description: Name of network zone + type: str + returned: always + sample: eu-central + vswitch_id: + description: ID of the vswitch, null if not type vswitch + type: int + returned: always + sample: 123 + gateway: + description: Gateway of the subnetwork + type: str + returned: always + sample: 10.0.0.1 +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.networks import BoundNetwork, NetworkSubnet + + +class AnsibleHCloudSubnetwork(AnsibleHCloud): + represent = "hcloud_subnetwork" + + hcloud_network: BoundNetwork | None = None + hcloud_subnetwork: NetworkSubnet | None = None + + def _prepare_result(self): + return { + "network": to_native(self.hcloud_network.name), + "ip_range": to_native(self.hcloud_subnetwork.ip_range), + "type": to_native(self.hcloud_subnetwork.type), + "network_zone": to_native(self.hcloud_subnetwork.network_zone), + "gateway": self.hcloud_subnetwork.gateway, + "vswitch_id": self.hcloud_subnetwork.vswitch_id, + } + + def _get_network(self): + try: + self.hcloud_network = self._client_get_by_name_or_id( + "networks", + self.module.params.get("network"), + ) + self.hcloud_subnetwork = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _get_subnetwork(self): + subnet_ip_range = self.module.params.get("ip_range") + for subnetwork in self.hcloud_network.subnets: + if subnetwork.ip_range == subnet_ip_range: + self.hcloud_subnetwork = subnetwork + + def _create_subnetwork(self): + params = { + "ip_range": self.module.params.get("ip_range"), + "type": self.module.params.get("type"), + "network_zone": self.module.params.get("network_zone"), + } + if self.module.params.get("type") == NetworkSubnet.TYPE_VSWITCH: + self.module.fail_on_missing_params(required_params=["vswitch_id"]) + params["vswitch_id"] = self.module.params.get("vswitch_id") + + if not self.module.check_mode: + try: + self.hcloud_network.add_subnet(subnet=NetworkSubnet(**params)).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + self._mark_as_changed() + self._get_network() + self._get_subnetwork() + + def present_subnetwork(self): + self._get_network() + self._get_subnetwork() + if self.hcloud_subnetwork is None: + self._create_subnetwork() + + def delete_subnetwork(self): + self._get_network() + self._get_subnetwork() + if self.hcloud_subnetwork is not None and self.hcloud_network is not None: + if not self.module.check_mode: + try: + self.hcloud_network.delete_subnet(self.hcloud_subnetwork).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self.hcloud_subnetwork = None + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + network={"type": "str", "required": True}, + network_zone={"type": "str", "required": True}, + type={"type": "str", "required": True, "choices": ["server", "cloud", "vswitch"]}, + ip_range={"type": "str", "required": True}, + vswitch_id={"type": "int"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudSubnetwork.define_module() + + hcloud = AnsibleHCloudSubnetwork(module) + state = module.params["state"] + if state == "absent": + hcloud.delete_subnetwork() + elif state == "present": + hcloud.present_subnetwork() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/volume.py b/ansible_collections/hetzner/hcloud/plugins/modules/volume.py new file mode 100644 index 000000000..8442ed90b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/volume.py @@ -0,0 +1,331 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: volume + +short_description: Create and manage block Volume on the Hetzner Cloud. + + +description: + - Create, update and attach/detach block Volume on the Hetzner Cloud. + +author: + - Christopher Schmitt (@cschmitt-hcloud) + +options: + id: + description: + - The ID of the Hetzner Cloud Block Volume to manage. + - Only required if no volume I(name) is given + type: int + name: + description: + - The Name of the Hetzner Cloud Block Volume to manage. + - Only required if no volume I(id) is given or a volume does not exist. + type: str + size: + description: + - The size of the Block Volume in GB. + - Required if volume does not yet exists. + type: int + automount: + description: + - Automatically mount the Volume. + type: bool + default: False + format: + description: + - Automatically Format the volume on creation + - Can only be used in case the Volume does not exist. + type: str + choices: [xfs, ext4] + location: + description: + - Location of the Hetzner Cloud Volume. + - Required if no I(server) is given and Volume does not exist. + type: str + server: + description: + - Server Name the Volume should be assigned to. + - Required if no I(location) is given and Volume does not exist. + type: str + delete_protection: + description: + - Protect the Volume for deletion. + type: bool + labels: + description: + - User-defined key-value pairs. + type: dict + state: + description: + - State of the Volume. + default: present + choices: [absent, present] + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Create a Volume + hetzner.hcloud.volume: + name: my-volume + location: fsn1 + size: 100 + state: present +- name: Create a Volume and format it with ext4 + hetzner.hcloud.volume: + name: my-volume + location: fsn + format: ext4 + size: 100 + state: present +- name: Mount a existing Volume and automount + hetzner.hcloud.volume: + name: my-volume + server: my-server + automount: true + state: present +- name: Mount a existing Volume and automount + hetzner.hcloud.volume: + name: my-volume + server: my-server + automount: true + state: present +- name: Ensure the Volume is absent (remove if needed) + hetzner.hcloud.volume: + name: my-volume + state: absent +""" + +RETURN = """ +hcloud_volume: + description: The block Volume + returned: Always + type: complex + contains: + id: + description: ID of the Volume + type: int + returned: Always + sample: 12345 + name: + description: Name of the Volume + type: str + returned: Always + sample: my-volume + size: + description: Size in GB of the Volume + type: int + returned: Always + sample: 1337 + linux_device: + description: Path to the device that contains the Volume. + returned: always + type: str + sample: /dev/disk/by-id/scsi-0HC_Volume_12345 + version_added: "0.1.0" + location: + description: Location name where the Volume is located at + type: str + returned: Always + sample: "fsn1" + labels: + description: User-defined labels (key-value pairs) + type: dict + returned: Always + sample: + key: value + mylabel: 123 + server: + description: Server name where the Volume is attached to + type: str + returned: Always + sample: "my-server" + delete_protection: + description: True if Volume is protected for deletion + type: bool + returned: always + sample: false + version_added: "0.1.0" +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.volumes import BoundVolume + + +class AnsibleHCloudVolume(AnsibleHCloud): + represent = "hcloud_volume" + + hcloud_volume: BoundVolume | None = None + + def _prepare_result(self): + server_name = None + if self.hcloud_volume.server is not None: + server_name = to_native(self.hcloud_volume.server.name) + + return { + "id": to_native(self.hcloud_volume.id), + "name": to_native(self.hcloud_volume.name), + "size": self.hcloud_volume.size, + "location": to_native(self.hcloud_volume.location.name), + "labels": self.hcloud_volume.labels, + "server": server_name, + "linux_device": to_native(self.hcloud_volume.linux_device), + "delete_protection": self.hcloud_volume.protection["delete"], + } + + def _get_volume(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_volume = self.client.volumes.get_by_id(self.module.params.get("id")) + else: + self.hcloud_volume = self.client.volumes.get_by_name(self.module.params.get("name")) + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def _create_volume(self): + self.module.fail_on_missing_params(required_params=["name", "size"]) + params = { + "name": self.module.params.get("name"), + "size": self.module.params.get("size"), + "automount": self.module.params.get("automount"), + "format": self.module.params.get("format"), + "labels": self.module.params.get("labels"), + } + if self.module.params.get("server") is not None: + params["server"] = self.client.servers.get_by_name(self.module.params.get("server")) + elif self.module.params.get("location") is not None: + params["location"] = self.client.locations.get_by_name(self.module.params.get("location")) + else: + self.module.fail_json(msg="server or location is required") + + if not self.module.check_mode: + try: + resp = self.client.volumes.create(**params) + resp.action.wait_until_finished() + [action.wait_until_finished() for action in resp.next_actions] + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None: + self._get_volume() + self.hcloud_volume.change_protection(delete=delete_protection).wait_until_finished() + except HCloudException as exception: + self.fail_json_hcloud(exception) + self._mark_as_changed() + self._get_volume() + + def _update_volume(self): + try: + size = self.module.params.get("size") + if size: + if self.hcloud_volume.size < size: + if not self.module.check_mode: + self.hcloud_volume.resize(size).wait_until_finished() + self._mark_as_changed() + elif self.hcloud_volume.size > size: + self.module.warn("Shrinking of volumes is not supported") + + server_name = self.module.params.get("server") + if server_name: + server = self.client.servers.get_by_name(server_name) + if self.hcloud_volume.server is None or self.hcloud_volume.server.name != server.name: + if not self.module.check_mode: + automount = self.module.params.get("automount", False) + self.hcloud_volume.attach(server, automount=automount).wait_until_finished() + self._mark_as_changed() + else: + if self.hcloud_volume.server is not None: + if not self.module.check_mode: + self.hcloud_volume.detach().wait_until_finished() + self._mark_as_changed() + + labels = self.module.params.get("labels") + if labels is not None and labels != self.hcloud_volume.labels: + if not self.module.check_mode: + self.hcloud_volume.update(labels=labels) + self._mark_as_changed() + + delete_protection = self.module.params.get("delete_protection") + if delete_protection is not None and delete_protection != self.hcloud_volume.protection["delete"]: + if not self.module.check_mode: + self.hcloud_volume.change_protection(delete=delete_protection).wait_until_finished() + self._mark_as_changed() + + self._get_volume() + except HCloudException as exception: + self.fail_json_hcloud(exception) + + def present_volume(self): + self._get_volume() + if self.hcloud_volume is None: + self._create_volume() + else: + self._update_volume() + + def delete_volume(self): + try: + self._get_volume() + if self.hcloud_volume is not None: + if not self.module.check_mode: + if self.hcloud_volume.server is not None: + self.hcloud_volume.detach().wait_until_finished() + self.client.volumes.delete(self.hcloud_volume) + self._mark_as_changed() + self.hcloud_volume = None + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + size={"type": "int"}, + location={"type": "str"}, + server={"type": "str"}, + labels={"type": "dict"}, + automount={"type": "bool", "default": False}, + format={"type": "str", "choices": ["xfs", "ext4"]}, + delete_protection={"type": "bool"}, + state={ + "choices": ["absent", "present"], + "default": "present", + }, + **super().base_module_arguments(), + ), + required_one_of=[["id", "name"]], + mutually_exclusive=[["location", "server"]], + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudVolume.define_module() + + hcloud = AnsibleHCloudVolume(module) + state = module.params.get("state") + if state == "absent": + module.fail_on_missing_params(required_params=["name"]) + hcloud.delete_volume() + else: + hcloud.present_volume() + + module.exit_json(**hcloud.get_result()) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py b/ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py new file mode 100644 index 000000000..1e507690e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/plugins/modules/volume_info.py @@ -0,0 +1,174 @@ +#!/usr/bin/python + +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +DOCUMENTATION = """ +--- +module: volume_info + +short_description: Gather infos about your Hetzner Cloud Volumes. + +description: + - Gather infos about your Hetzner Cloud Volumes. + +author: + - Lukas Kaemmerling (@LKaemmerling) + +options: + id: + description: + - The ID of the Volume you want to get. + - The module will fail if the provided ID is invalid. + type: int + name: + description: + - The name of the Volume you want to get. + type: str + label_selector: + description: + - The label selector for the Volume you want to get. + type: str +extends_documentation_fragment: +- hetzner.hcloud.hcloud + +""" + +EXAMPLES = """ +- name: Gather hcloud Volume infos + hetzner.hcloud.volume_info: + register: output +- name: Print the gathered infos + debug: + var: output.hcloud_volume_info +""" + +RETURN = """ +hcloud_volume_info: + description: The Volume infos as list + returned: always + type: complex + contains: + id: + description: Numeric identifier of the Volume + returned: always + type: int + sample: 1937415 + name: + description: Name of the Volume + returned: always + type: str + sample: my-volume + size: + description: Size of the Volume + returned: always + type: str + sample: 10 + linux_device: + description: Path to the device that contains the Volume. + returned: always + type: str + sample: /dev/disk/by-id/scsi-0HC_Volume_12345 + version_added: "0.1.0" + location: + description: Name of the location where the Volume resides in + returned: always + type: str + sample: fsn1 + server: + description: Name of the server where the Volume is attached to + returned: always + type: str + sample: my-server + delete_protection: + description: True if the Volume is protected for deletion + returned: always + type: bool + version_added: "0.1.0" + labels: + description: User-defined labels (key-value pairs) + returned: always + type: dict +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_native + +from ..module_utils.hcloud import AnsibleHCloud +from ..module_utils.vendor.hcloud import HCloudException +from ..module_utils.vendor.hcloud.volumes import BoundVolume + + +class AnsibleHCloudVolumeInfo(AnsibleHCloud): + represent = "hcloud_volume_info" + + hcloud_volume_info: list[BoundVolume] | None = None + + def _prepare_result(self): + tmp = [] + + for volume in self.hcloud_volume_info: + if volume is not None: + server_name = None + if volume.server is not None: + server_name = to_native(volume.server.name) + tmp.append( + { + "id": to_native(volume.id), + "name": to_native(volume.name), + "size": volume.size, + "location": to_native(volume.location.name), + "labels": volume.labels, + "server": server_name, + "linux_device": to_native(volume.linux_device), + "delete_protection": volume.protection["delete"], + } + ) + + return tmp + + def get_volumes(self): + try: + if self.module.params.get("id") is not None: + self.hcloud_volume_info = [self.client.volumes.get_by_id(self.module.params.get("id"))] + elif self.module.params.get("name") is not None: + self.hcloud_volume_info = [self.client.volumes.get_by_name(self.module.params.get("name"))] + elif self.module.params.get("label_selector") is not None: + self.hcloud_volume_info = self.client.volumes.get_all( + label_selector=self.module.params.get("label_selector") + ) + else: + self.hcloud_volume_info = self.client.volumes.get_all() + + except HCloudException as exception: + self.fail_json_hcloud(exception) + + @classmethod + def define_module(cls): + return AnsibleModule( + argument_spec=dict( + id={"type": "int"}, + name={"type": "str"}, + label_selector={"type": "str"}, + **super().base_module_arguments(), + ), + supports_check_mode=True, + ) + + +def main(): + module = AnsibleHCloudVolumeInfo.define_module() + hcloud = AnsibleHCloudVolumeInfo(module) + + hcloud.get_volumes() + result = hcloud.get_result() + + ansible_info = {"hcloud_volume_info": result["hcloud_volume_info"]} + module.exit_json(**ansible_info) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/hetzner/hcloud/pyproject.toml b/ansible_collections/hetzner/hcloud/pyproject.toml new file mode 100644 index 000000000..5263580cb --- /dev/null +++ b/ansible_collections/hetzner/hcloud/pyproject.toml @@ -0,0 +1,28 @@ +[tool.black] +line-length = 120 + +[tool.isort] +profile = "black" +combine_as_imports = true +add_imports = ["from __future__ import annotations"] + +[tool.pylint.main] +py-version = "3.8" +recursive = true +jobs = 0 + +max-line-length = 120 +ignore-paths = [ + "plugins/module_utils/vendor", +] + +[tool.pylint.reports] +output-format = "colorized" + +[tool.pylint.basic] +good-names = ["i", "j", "k", "ex", "_", "ip", "id"] + +[tool.pylint.messages_control] +disable = [ + "wrong-import-position", +] diff --git a/ansible_collections/hetzner/hcloud/renovate.json b/ansible_collections/hetzner/hcloud/renovate.json new file mode 100644 index 000000000..94c5bd199 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/renovate.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["github>hetznercloud/.github//renovate/default"], + "regexManagers": [ + { + "fileMatch": ["^scripts/vendor.py$"], + "matchStrings": ["HCLOUD_VERSION = \"v(?<currentValue>.*)\""], + "datasourceTemplate": "pypi", + "depNameTemplate": "hcloud" + } + ] +} diff --git a/ansible_collections/hetzner/hcloud/requirements.txt b/ansible_collections/hetzner/hcloud/requirements.txt new file mode 100644 index 000000000..0e9148f96 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/requirements.txt @@ -0,0 +1,12 @@ +ansible-core>=2.13 + +# Collections requirements +netaddr +cryptography + +python-dateutil +requests + +# Development requirements +pylint +antsibull-docs>=2.7,<2.8 diff --git a/ansible_collections/hetzner/hcloud/scripts/integration-test-files.sh b/ansible_collections/hetzner/hcloud/scripts/integration-test-files.sh new file mode 100755 index 000000000..fbae089b5 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/scripts/integration-test-files.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Sync the integration test files from the template to all the integrations targets. + +integration_targets="tests/integration/targets/" +integration_common="tests/integration/common" + +# banner +banner() { + echo "#" + echo "# DO NOT EDIT THIS FILE! Please edit the files in $integration_common instead." + echo "#" +} + +# copy_file <src> <dest> +copy_file() { + mkdir -p "$(dirname "$2")" + banner > "$2" + cat "$1" >> "$2" +} + +for target in "$integration_targets"*; do + if [[ "$(basename "$target")" = setup_* ]]; then + continue + fi + copy_file "$integration_common"/defaults/main/common.yml "$target"/defaults/main/common.yml + copy_file "$integration_common"/tasks/main.yml "$target"/tasks/main.yml +done diff --git a/ansible_collections/hetzner/hcloud/scripts/vendor.py b/ansible_collections/hetzner/hcloud/scripts/vendor.py new file mode 100755 index 000000000..b55a66577 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/scripts/vendor.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +""" +Fetch and bundles the hcloud package inside the collection. + +Fetch the desired version `HCLOUD_VERSION` from https://github.com/hetznercloud/hcloud-python +`HCLOUD_SOURCE_URL` using git, apply some code modifications to comply with ansible, +move the modified files at the vendor location `HCLOUD_VENDOR_PATH`. +""" + +from __future__ import annotations + +import logging +import re +from argparse import ArgumentParser +from pathlib import Path +from shutil import move, rmtree +from subprocess import check_call +from tempfile import TemporaryDirectory +from textwrap import dedent + +logger = logging.getLogger("vendor") + +HCLOUD_SOURCE_URL = "https://github.com/hetznercloud/hcloud-python" +HCLOUD_VERSION = "v1.33.2" +HCLOUD_VENDOR_PATH = "plugins/module_utils/vendor/hcloud" + + +def apply_code_modifications(source_path: Path): + # The ansible galaxy-importer consider __version___.py to be an invalid filename in module_utils/ + # Move the __version__.py file to _version.py + move(source_path / "__version__.py", source_path / "_version.py") + + for file in source_path.rglob("*.py"): + content = file.read_text() + content_orig = content + + # Move the __version__.py file to _version.py + content = re.sub( + r"from .__version__ import VERSION", + r"from ._version import VERSION", + content, + ) + + # Wrap requests imports + content = re.sub( + r"import requests", + dedent( + r""" + try: + import requests + except ImportError: + requests = None + """ + ).strip(), + content, + ) + + # Wrap dateutil imports + content = re.sub( + r"from dateutil.parser import isoparse", + dedent( + r""" + try: + from dateutil.parser import isoparse + except ImportError: + isoparse = None + """ + ).strip(), + content, + ) + + # Remove requests.Response typings + content = re.sub( + r": requests\.Response", + r"", + content, + ) + + if content != content_orig: + logger.info("Applied code modifications on %s", file) + + file.write_text(content) + + +def main(check: bool = False) -> int: + with TemporaryDirectory() as tmp_dir: + tmp_dir_path = Path(tmp_dir) + logger.info("Created temporary directory %s", tmp_dir_path) + + check_call(["git", "clone", "--depth=1", "--branch", HCLOUD_VERSION, HCLOUD_SOURCE_URL, tmp_dir_path]) + logger.info("Cloned the source files in %s", tmp_dir_path) + + apply_code_modifications(tmp_dir_path / "hcloud") + logger.info("Applied code modifications on the source files") + + rmtree(HCLOUD_VENDOR_PATH) + move(tmp_dir_path / "hcloud", HCLOUD_VENDOR_PATH) + logger.info("Bundled the modified sources files in the collection") + + if check: + check_call(["git", "diff", "--exit-code", "--", HCLOUD_VENDOR_PATH]) + + return 0 + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, format="%(levelname)-8s: %(message)s") + + parser = ArgumentParser() + parser.add_argument("--check", action="store_true", default=False) + args = parser.parse_args() + + raise SystemExit(main(check=args.check)) diff --git a/ansible_collections/hetzner/hcloud/tests/config.yml b/ansible_collections/hetzner/hcloud/tests/config.yml new file mode 100644 index 000000000..340add1aa --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/config.yml @@ -0,0 +1,3 @@ +--- +modules: + python_requires: ">=3.8" diff --git a/ansible_collections/hetzner/hcloud/tests/constraints.txt b/ansible_collections/hetzner/hcloud/tests/constraints.txt new file mode 100644 index 000000000..7e72ee478 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/constraints.txt @@ -0,0 +1,2 @@ +python-dateutil>=2.7.5 +requests>=2.20 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/README.md b/ansible_collections/hetzner/hcloud/tests/integration/README.md new file mode 100644 index 000000000..e3bb08319 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/README.md @@ -0,0 +1,27 @@ +# Integration tests + +This document provides information to work with the integration tests. + +## Guidelines + +A set of guidelines to follow when writing integrations tests. + +### Prepare and cleanup + +The integration tests use a small testing framework that helps to set up and teardown any resources needed or generated by the tests. This small testing framework is located in the `tests/integration/common` directory. The files within the `common` directory are then duplicated and kept in sync in all the integration tests targets (`tests/integration/targets/hcloud_*`). + +- Use a `tasks/prepare.yml` file to set up resources needed during the tests. +- Use a `tasks/cleanup.yml` file to teardown resources from the `tasts/prepare.yml` tasks **and** the resources generated by the tests. +- Use a `tasks/test.yml` file to defines your tests. +- You may explode the tests into multiple `tasks/test-*.yml` files and import them in the `tasks/test.yml` file. +- The `tasks/cleanup.yml` file cannot use variables present in the `tasks/prepare.yml` file because cleanup should also run before the prepare tasks. + +### Naming convention + +The integration tests handle a lot of different variables, names, identifier. To reduce this complexity, make sure to use the following naming conventions: + +- Any test resources MUST be registered using the `test_<resource>` variable name (e.g. `test_server` or `test_floating_ip`) and MUST be created and cleaned in the `tasks/prepare.yml` and `tasks/cleanup.yml`. The scope of this variable is the entire target. +- In `tasks/prepare.yml`, tasks names MUST start with: `Create test_<resource>` (e.g. `Create test_server` or `Create test_floating_ip`) +- In `tasks/cleanup.yml`, tasks names MUST start with: `Cleanup test_<resource>` (e.g. `Cleanup test_server` or `Cleanup test_floating_ip`) +- Any fact starting with `_` is scoped to the current file and MUST NOT be used outside of it. +- Any test result MUST be registered using the `result` variable name unless it is required in a future test, in that case it MUST use the `<resource>` variable name as prefix. diff --git a/ansible_collections/hetzner/hcloud/tests/integration/common/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/common/defaults/main/common.yml new file mode 100644 index 000000000..e45380565 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/common/defaults/main/common.yml @@ -0,0 +1,9 @@ +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/common/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/common/tasks/main.yml new file mode 100644 index 000000000..c3f93a394 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/common/tasks/main.yml @@ -0,0 +1,28 @@ +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/constraints.txt b/ansible_collections/hetzner/hcloud/tests/integration/constraints.txt deleted file mode 100644 index 19d5ecf28..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/constraints.txt +++ /dev/null @@ -1 +0,0 @@ -hcloud >= 1.10.0 # minimum version diff --git a/ansible_collections/hetzner/hcloud/tests/integration/requirements.txt b/ansible_collections/hetzner/hcloud/tests/integration/requirements.txt index d3249deff..8b3801627 100644 --- a/ansible_collections/hetzner/hcloud/tests/integration/requirements.txt +++ b/ansible_collections/hetzner/hcloud/tests/integration/requirements.txt @@ -1,2 +1,5 @@ netaddr -hcloud +cryptography + +python-dateutil +requests diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/defaults/main/main.yml new file mode 100644 index 000000000..e94b57771 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_certificate_name: "{{ hcloud_ns }}" +hcloud_dns_test_domain: "{{ (hcloud_ns + 100 | random | string) | md5 }}.hc-integrations-test.de" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/meta/main.yml new file mode 100644 index 000000000..163f4db09 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_selfsigned_certificate diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/tasks/cleanup.yml new file mode 100644 index 000000000..2513909aa --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: cleanup test certificate + hetzner.hcloud.certificate: + name: "{{ hcloud_certificate_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/tasks/test.yml new file mode 100644 index 000000000..240e6a18c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate/tasks/test.yml @@ -0,0 +1,155 @@ +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: test missing required parameters on create certificate + hetzner.hcloud.certificate: + name: "{{ hcloud_certificate_name }}" + register: result + ignore_errors: true +- name: verify fail test missing required parameters on create certificate + assert: + that: + - result is failed + - 'result.msg == "missing required arguments: certificate, private_key"' + +- name: test create certificate with check mode + hetzner.hcloud.certificate: + name: "{{ hcloud_certificate_name }}" + certificate: "{{ test_certificate_content }}" + private_key: "{{ test_certificate_privatekey_content }}" + register: result + check_mode: true +- name: test create certificate with check mode + assert: + that: + - result is changed + +- name: test create certificate + hetzner.hcloud.certificate: + name: "{{ hcloud_certificate_name }}" + certificate: "{{ test_certificate_content }}" + private_key: "{{ test_certificate_privatekey_content }}" + labels: + key: value + my-label: label + register: certificate +- name: verify create certificate + assert: + that: + - certificate is changed + - certificate.hcloud_certificate.name == hcloud_certificate_name + - certificate.hcloud_certificate.domain_names[0] == "www.example.com" + - certificate.hcloud_certificate.labels.key == "value" + +- name: test create certificate idempotence + hetzner.hcloud.certificate: + name: "{{ hcloud_certificate_name }}" + certificate: "{{ test_certificate_content }}" + private_key: "{{ test_certificate_privatekey_content }}" + register: result +- name: verify create certificate idempotence + assert: + that: + - result is not changed + +- name: test update certificate with check mode + hetzner.hcloud.certificate: + id: "{{ certificate.hcloud_certificate.id }}" + name: "changed-{{ hcloud_certificate_name }}" + register: result + check_mode: true +- name: test create certificate with check mode + assert: + that: + - result is changed + +- name: test update certificate + hetzner.hcloud.certificate: + id: "{{ certificate.hcloud_certificate.id }}" + name: "changed-{{ hcloud_certificate_name }}" + labels: + key: value + register: result +- name: test update certificate + assert: + that: + - result is changed + - result.hcloud_certificate.name == "changed-{{ hcloud_certificate_name }}" + +- name: test update certificate with same labels + hetzner.hcloud.certificate: + id: "{{ certificate.hcloud_certificate.id }}" + name: "changed-{{ hcloud_certificate_name }}" + labels: + key: value + register: result +- name: test update certificate with same labels + assert: + that: + - result is not changed + +- name: test update certificate with other labels + hetzner.hcloud.certificate: + id: "{{ certificate.hcloud_certificate.id }}" + name: "changed-{{ hcloud_certificate_name }}" + labels: + key: value + test: "val123" + register: result +- name: test update certificate with other labels + assert: + that: + - result is changed + +- name: test rename certificate + hetzner.hcloud.certificate: + id: "{{ certificate.hcloud_certificate.id }}" + name: "{{ hcloud_certificate_name }}" + register: result +- name: test rename certificate + assert: + that: + - result is changed + - result.hcloud_certificate.name == hcloud_certificate_name + +- name: absent certificate + hetzner.hcloud.certificate: + id: "{{ certificate.hcloud_certificate.id }}" + state: absent + register: result +- name: verify absent server + assert: + that: + - result is success + +- name: generate dns domain name + set_fact: + # hcloud_dns_test_domain uses random, which generates a new random number + # on every invocation, by saving it into a fact we generate the number once + hcloud_dns_test_domain: "{{ hcloud_dns_test_domain }}" + +- name: test create managed certificate + hetzner.hcloud.certificate: + name: "{{ hcloud_certificate_name }}" + domain_names: + - "{{ hcloud_dns_test_domain }}" + type: managed + labels: + HC-Use-Staging-CA: "true" + register: result +- name: verify create managed certificate + assert: + that: + - result is changed + - result.hcloud_certificate.name == hcloud_certificate_name + - result.hcloud_certificate.domain_names[0] == hcloud_dns_test_domain + +- name: test delete certificate + hetzner.hcloud.certificate: + id: "{{ result.hcloud_certificate.id }}" + state: absent + register: result +- name: verify test delete certificate + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/defaults/main/main.yml new file mode 100644 index 000000000..5220424ed --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_certificate_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/meta/main.yml new file mode 100644 index 000000000..163f4db09 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_selfsigned_certificate diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/cleanup.yml new file mode 100644 index 000000000..59a50c8f7 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_certificate + hetzner.hcloud.certificate: + name: "{{ hcloud_certificate_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/prepare.yml new file mode 100644 index 000000000..bb1dacdc5 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/prepare.yml @@ -0,0 +1,10 @@ +--- +- name: Create test_certificate + hetzner.hcloud.certificate: + name: "{{ hcloud_certificate_name }}" + certificate: "{{ test_certificate_content }}" + private_key: "{{ test_certificate_privatekey_content }}" + labels: + key: value + my-label: label + register: test_certificate diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/test.yml new file mode 100644 index 000000000..d15db0aee --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/certificate_info/tasks/test.yml @@ -0,0 +1,77 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_certificate_info + hetzner.hcloud.certificate_info: + register: result +- name: Verify hcloud_certificate_info + ansible.builtin.assert: + that: + - result.hcloud_certificate_info | list | count >= 1 + +- name: Gather hcloud_certificate_info in check mode + hetzner.hcloud.certificate_info: + check_mode: true + register: result +- name: Verify hcloud_certificate_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_certificate_info | list | count >= 1 + +- name: Gather hcloud_certificate_info with correct id + hetzner.hcloud.certificate_info: + id: "{{ test_certificate.hcloud_certificate.id }}" + register: result +- name: Verify hcloud_certificate_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_certificate_info | list | count == 1 + +- name: Gather hcloud_certificate_info with wrong id + hetzner.hcloud.certificate_info: + id: "{{ test_certificate.hcloud_certificate.id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_certificate_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_certificate_info with correct name + hetzner.hcloud.certificate_info: + name: "{{ hcloud_certificate_name }}" + register: result +- name: Verify hcloud_certificate_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_certificate_info | list | count == 1 + +- name: Gather hcloud_certificate_info with wrong name + hetzner.hcloud.certificate_info: + name: "{{ hcloud_certificate_name }}-invalid" + register: result +- name: Verify hcloud_certificate_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_certificate_info | list | count == 0 + +- name: Gather hcloud_certificate_info with correct label selector + hetzner.hcloud.certificate_info: + label_selector: "key=value" + register: result +- name: Verify hcloud_certificate_info with correct label selector + ansible.builtin.assert: + that: + - > + result.hcloud_certificate_info + | selectattr('name', 'equalto', hcloud_certificate_name) + | list | count == 1 + +- name: Gather hcloud_certificate_info with wrong label selector + hetzner.hcloud.certificate_info: + label_selector: "key!=value" + register: result +- name: Verify hcloud_certificate_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_certificate_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/defaults/main/main.yml new file mode 100644 index 000000000..61490c913 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/defaults/main/main.yml @@ -0,0 +1,6 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_datacenter_id: 4 +hcloud_datacenter_name: fsn1-dc14 +hcloud_location_name: fsn1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/tasks/test.yml new file mode 100644 index 000000000..530b4ddb2 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/datacenter_info/tasks/test.yml @@ -0,0 +1,58 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_datacenter_info + hetzner.hcloud.datacenter_info: + register: result +- name: Verify hcloud_datacenter_info + ansible.builtin.assert: + that: + - result.hcloud_datacenter_info | list | count >= 5 + +- name: Gather hcloud_datacenter_info in check mode + hetzner.hcloud.datacenter_info: + check_mode: true + register: result +- name: Verify hcloud_datacenter_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_datacenter_info | list | count >= 5 + +- name: Gather hcloud_datacenter_info with correct id + hetzner.hcloud.datacenter_info: + id: "{{ hcloud_datacenter_id }}" + register: result +- name: Verify hcloud_datacenter_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_datacenter_info | list | count == 1 + - result.hcloud_datacenter_info[0].name == hcloud_datacenter_name + - result.hcloud_datacenter_info[0].location == hcloud_location_name + +- name: Gather hcloud_datacenter_info with wrong id + hetzner.hcloud.datacenter_info: + id: "{{ hcloud_datacenter_id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_datacenter_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_datacenter_info with correct name + hetzner.hcloud.datacenter_info: + name: "{{ hcloud_datacenter_name }}" + register: result +- name: Verify hcloud_datacenter_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_datacenter_info | list | count == 1 + +- name: Gather hcloud_datacenter_info with wrong name + hetzner.hcloud.datacenter_info: + name: "{{ hcloud_datacenter_name }}-invalid" + register: result +- name: Verify hcloud_datacenter_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_datacenter_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/defaults/main/main.yml new file mode 100644 index 000000000..989e14fd1 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_firewall_name: "{{ hcloud_ns }}" +hcloud_server_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/cleanup.yml new file mode 100644 index 000000000..37fbd3413 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/cleanup.yml @@ -0,0 +1,10 @@ +--- +- name: Cleanup test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: Cleanup test_firewall + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/prepare.yml new file mode 100644 index 000000000..cf6daa322 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/prepare.yml @@ -0,0 +1,8 @@ +--- +- name: Create test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: ubuntu-22.04 + state: stopped + register: test_server diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/test.yml new file mode 100644 index 000000000..57059848f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall/tasks/test.yml @@ -0,0 +1,197 @@ +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Test missing required parameters + hetzner.hcloud.firewall: + state: present + ignore_errors: true + register: result +- name: Verify missing required parameters + ansible.builtin.assert: + that: + - result is failed + - 'result.msg == "one of the following is required: id, name"' + +- name: Test create with check mode + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + rules: + - description: allow icmp in + direction: in + protocol: icmp + source_ips: ["0.0.0.0/0", "::/0"] + labels: + key: value + check_mode: true + register: result +- name: Verify create with check mode + ansible.builtin.assert: + that: + - result is changed + +- name: Test create + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + rules: + - description: allow icmp in + direction: in + protocol: icmp + source_ips: ["0.0.0.0/0", "::/0"] + labels: + key: value + register: result +- name: Verify create + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_firewall.name == hcloud_firewall_name + - result.hcloud_firewall.rules | list | count == 1 + - result.hcloud_firewall.rules[0].description == "allow icmp in" + - result.hcloud_firewall.rules[0].direction == "in" + - result.hcloud_firewall.rules[0].protocol == "icmp" + - result.hcloud_firewall.rules[0].source_ips == ["0.0.0.0/0", "::/0"] + - result.hcloud_firewall.labels.key == "value" + - result.hcloud_firewall.applied_to | list | count == 0 + +- name: Test create idempotency + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + rules: + - description: allow icmp in + direction: in + protocol: icmp + source_ips: ["0.0.0.0/0", "::/0"] + labels: + key: value + register: result +- name: Verify create idempotency + ansible.builtin.assert: + that: + - result is not changed + +- name: Assign firewall to test_server + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + servers: ["{{ test_server.hcloud_server.name }}"] + state: present + +- name: Test update + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + rules: + - description: allow icmp in + direction: in + protocol: icmp + source_ips: ["0.0.0.0/0", "::/0"] + - description: allow http in + direction: in + protocol: tcp + port: 80 + source_ips: ["0.0.0.0/0", "::/0"] + - description: allow http out + direction: out + protocol: tcp + port: 80 + destination_ips: ["0.0.0.0/0", "::/0"] + labels: + key: value + label: label + register: result +- name: Verify update + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_firewall.name == hcloud_firewall_name + - result.hcloud_firewall.rules | list | count == 3 + - result.hcloud_firewall.rules[0].description == "allow icmp in" + - result.hcloud_firewall.rules[0].direction == "in" + - result.hcloud_firewall.rules[0].protocol == "icmp" + - result.hcloud_firewall.rules[0].source_ips == ["0.0.0.0/0", "::/0"] + - result.hcloud_firewall.rules[1].description == "allow http in" + - result.hcloud_firewall.rules[1].direction == "in" + - result.hcloud_firewall.rules[1].protocol == "tcp" + - result.hcloud_firewall.rules[1].port == "80" + - result.hcloud_firewall.rules[1].source_ips == ["0.0.0.0/0", "::/0"] + - result.hcloud_firewall.rules[2].description == "allow http out" + - result.hcloud_firewall.rules[2].direction == "out" + - result.hcloud_firewall.rules[2].protocol == "tcp" + - result.hcloud_firewall.rules[2].port == "80" + - result.hcloud_firewall.rules[2].destination_ips == ["0.0.0.0/0", "::/0"] + - result.hcloud_firewall.labels.key == "value" + - result.hcloud_firewall.labels.label == "label" + +- name: Test update idempotency + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + rules: + - description: allow icmp in + direction: in + protocol: icmp + source_ips: ["0.0.0.0/0", "::/0"] + - description: allow http in + direction: in + protocol: tcp + port: 80 + source_ips: ["0.0.0.0/0", "::/0"] + - description: allow http out + direction: out + protocol: tcp + port: 80 + destination_ips: ["0.0.0.0/0", "::/0"] + labels: + key: value + label: label + register: result +- name: Verify update idempotency + ansible.builtin.assert: + that: + - result is not changed + +- name: Test update name + hetzner.hcloud.firewall: + id: "{{ result.hcloud_firewall.id }}" + name: "changed-{{ hcloud_firewall_name }}" + register: result +- name: Verify update name + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_firewall.name == "changed-" + hcloud_firewall_name + +- name: Test update name and labels + hetzner.hcloud.firewall: + id: "{{ result.hcloud_firewall.id }}" + name: "{{ hcloud_firewall_name }}" + labels: + key: value + register: result +- name: Verify update name and labels + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_firewall.name == hcloud_firewall_name + - result.hcloud_firewall.labels.key == "value" + - result.hcloud_firewall.labels.label is not defined + +- name: Test delete + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + state: absent + ignore_errors: true + register: result +- name: Verify delete + ansible.builtin.assert: + that: + - result is failed + - '"is still in use" in result.msg' + +- name: Test delete with force + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + force: true + state: absent + register: result +- name: Verify delete with force + ansible.builtin.assert: + that: + - result is changed diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/aliases index 55ec821a4..55ec821a4 100644 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/aliases +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/aliases diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/defaults/main/main.yml new file mode 100644 index 000000000..441e948ed --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_server_name: "{{ hcloud_ns }}" +hcloud_firewall_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/cleanup.yml new file mode 100644 index 000000000..37fbd3413 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/cleanup.yml @@ -0,0 +1,10 @@ +--- +- name: Cleanup test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: Cleanup test_firewall + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/prepare.yml new file mode 100644 index 000000000..17d4ebcc4 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/prepare.yml @@ -0,0 +1,35 @@ +--- +- name: Create test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: ubuntu-22.04 + labels: + firewall: "{{ hcloud_firewall_name }}" + state: stopped + register: test_server + +- name: Create test_firewall + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + labels: + key: value + rules: + - description: allow icmp from anywhere + direction: in + protocol: icmp + source_ips: + - 0.0.0.0/0 + - ::/0 + state: present + register: test_firewall + +- name: Create test_firewall_resource + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + servers: + - "{{ hcloud_server_name }}" + label_selectors: + - firewall={{ hcloud_firewall_name }} + state: present + register: test_firewall_resource diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/test.yml new file mode 100644 index 000000000..fc9a38af2 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_info/tasks/test.yml @@ -0,0 +1,93 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_firewall_info + hetzner.hcloud.firewall_info: + register: result +- name: Verify hcloud_firewall_info + ansible.builtin.assert: + that: + - result.hcloud_firewall_info | list | count >= 1 + +- name: Gather hcloud_firewall_info in check mode + hetzner.hcloud.firewall_info: + check_mode: true + register: result +- name: Verify hcloud_firewall_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_firewall_info | list | count >= 1 + +- name: Gather hcloud_firewall_info with correct id + hetzner.hcloud.firewall_info: + id: "{{ test_firewall.hcloud_firewall.id }}" + register: result +- name: Verify hcloud_firewall_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_firewall_info | list | count == 1 + - result.hcloud_firewall_info[0].name == hcloud_firewall_name + - result.hcloud_firewall_info[0].labels.key == "value" + - result.hcloud_firewall_info[0].rules | list | count == 1 + - result.hcloud_firewall_info[0].rules[0].description == "allow icmp from anywhere" + - result.hcloud_firewall_info[0].rules[0].direction == "in" + - result.hcloud_firewall_info[0].rules[0].protocol == "icmp" + - result.hcloud_firewall_info[0].rules[0].source_ips == ["0.0.0.0/0", "::/0"] + - result.hcloud_firewall_info[0].applied_to | list | count == 2 + - > + result.hcloud_firewall_info[0].applied_to + | selectattr('type', 'equalto', 'label_selector') + | list | count == 1 + - > + result.hcloud_firewall_info[0].applied_to + | selectattr('type', 'equalto', 'server') + | list | count == 1 + +- name: Gather hcloud_firewall_info with wrong id + hetzner.hcloud.firewall_info: + id: "{{ test_firewall.hcloud_firewall.id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_firewall_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_firewall_info with correct name + hetzner.hcloud.firewall_info: + name: "{{ hcloud_firewall_name }}" + register: result +- name: Verify hcloud_firewall_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_firewall_info | list | count == 1 + +- name: Gather hcloud_firewall_info with wrong name + hetzner.hcloud.firewall_info: + name: "{{ hcloud_firewall_name }}-invalid" + register: result +- name: Verify hcloud_firewall_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_firewall_info | list | count == 0 + +- name: Gather hcloud_firewall_info with correct label selector + hetzner.hcloud.firewall_info: + label_selector: "key=value" + register: result +- name: Verify hcloud_firewall_info with correct label selector + ansible.builtin.assert: + that: + - > + result.hcloud_firewall_info + | selectattr('name', 'equalto', hcloud_firewall_name) + | list | count == 1 + +- name: Gather hcloud_firewall_info with wrong label selector + hetzner.hcloud.firewall_info: + label_selector: "key!=value" + register: result +- name: Verify hcloud_firewall_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_firewall_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/defaults/main/main.yml new file mode 100644 index 000000000..441e948ed --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_server_name: "{{ hcloud_ns }}" +hcloud_firewall_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/cleanup.yml new file mode 100644 index 000000000..37fbd3413 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/cleanup.yml @@ -0,0 +1,10 @@ +--- +- name: Cleanup test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: Cleanup test_firewall + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/prepare.yml new file mode 100644 index 000000000..6fb6fd2df --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/prepare.yml @@ -0,0 +1,25 @@ +--- +- name: Create test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: ubuntu-22.04 + labels: + key: value + state: stopped + register: test_server + +- name: Create test_firewall + hetzner.hcloud.firewall: + name: "{{ hcloud_firewall_name }}" + labels: + key: value + rules: + - description: allow icmp from anywhere + direction: in + protocol: icmp + source_ips: + - 0.0.0.0/0 + - ::/0 + state: present + register: test_firewall diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/test.yml new file mode 100644 index 000000000..088e78413 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/firewall_resource/tasks/test.yml @@ -0,0 +1,95 @@ +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Test missing required parameters + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + state: present + ignore_errors: true + register: result +- name: Verify missing required parameters + ansible.builtin.assert: + that: + - result is failed + - 'result.msg == "one of the following is required: servers, label_selectors"' + +- name: Test create with check mode + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + servers: ["{{ hcloud_server_name }}"] + check_mode: true + register: result +- name: Verify create with check mode + ansible.builtin.assert: + that: + - result is changed + +- name: Test create + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + servers: ["{{ hcloud_server_name }}"] + register: result +- name: Verify create + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_firewall_resource.firewall == hcloud_firewall_name + - result.hcloud_firewall_resource.servers | list | count == 1 + - result.hcloud_firewall_resource.servers[0] == hcloud_server_name + - result.hcloud_firewall_resource.label_selectors | list | count == 0 + +- name: Test create idempotency + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + servers: ["{{ hcloud_server_name }}"] + register: result +- name: Verify create idempotency + ansible.builtin.assert: + that: + - result is not changed + +- name: Test update + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + label_selectors: + - key=value + register: result +- name: Verify update + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_firewall_resource.label_selectors | list | count == 1 + - result.hcloud_firewall_resource.label_selectors[0] == "key=value" + +- name: Test update idempotency + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + label_selectors: + - key=value + register: result +- name: Verify update idempotency + ansible.builtin.assert: + that: + - result is not changed + +- name: Test delete servers + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + servers: ["{{ hcloud_server_name }}"] + state: absent + register: result +- name: Verify delete + ansible.builtin.assert: + that: + - result is changed + +- name: Test delete label_selectors + hetzner.hcloud.firewall_resource: + firewall: "{{ hcloud_firewall_name }}" + label_selectors: ["key=value"] + state: absent + register: result +- name: Verify delete + ansible.builtin.assert: + that: + - result is changed diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/aliases new file mode 100644 index 000000000..a6a90a6bf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group3 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/defaults/main/main.yml new file mode 100644 index 000000000..a2d33ff80 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_floating_ip_name: "{{ hcloud_ns }}" +hcloud_server_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/tasks/test.yml new file mode 100644 index 000000000..fba06e308 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip/tasks/test.yml @@ -0,0 +1,484 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup ensure server is absent + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: setup ensure another server is absent + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}2" + state: absent + +- name: setup ensure floating ip is absent + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + state: absent + +- name: setup server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: ubuntu-22.04 + state: started + location: "fsn1" + register: main_server +- name: verify setup server + assert: + that: + - main_server is changed + +- name: setup another server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}2" + server_type: cx11 + image: ubuntu-22.04 + state: started + register: main_server2 +- name: verify setup another server + assert: + that: + - main_server2 is changed + +- name: test missing type parameter on create Floating IP + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + register: result + ignore_errors: true +- name: verify fail test missing type parameter on create Floating IP + assert: + that: + - result is failed + - 'result.msg == "missing required arguments: type"' + +- name: test missing required parameters on create Floating IP + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + register: result + ignore_errors: true +- name: verify fail test missing required parameters on create Floating IP + assert: + that: + - result is failed + - 'result.msg == "one of the following is required: home_location, server"' + +- name: test missing type parameter on delete Floating IP + hetzner.hcloud.floating_ip: + type: ipv4 + home_location: "fsn1" + state: "absent" + register: result + ignore_errors: true +- name: verify fail test missing type parameter on delete Floating IP + assert: + that: + - result is failed + - 'result.msg == "one of the following is required: id, name"' + +- name: test invalid type + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv5 + home_location: "fsn1" + register: result + ignore_errors: true +- name: verify invalid type + assert: + that: + - result is failed + - 'result.msg == "value of type must be one of: ipv4, ipv6, got: ipv5"' + +- name: test invalid location + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + home_location: "abc" + register: result + ignore_errors: true +- name: verify invalid location + assert: + that: + - result is failed + - result.msg == "invalid input in fields 'server', 'home_location'" + +- name: test create Floating IP with check mode + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + description: "Web Server" + type: ipv4 + home_location: "fsn1" + register: floatingIP + check_mode: true +- name: verify test create Floating IP with check mode + assert: + that: + - floatingIP is changed + +- name: test create Floating IP + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + description: "Web Server" + type: ipv4 + home_location: "fsn1" + register: floatingIP +- name: verify test create Floating IP + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.name ==hcloud_floating_ip_name + - floatingIP.hcloud_floating_ip.description == "Web Server" + - floatingIP.hcloud_floating_ip.home_location == "fsn1" + +- name: test create Floating IP idempotency + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + description: "Web Server" + type: ipv4 + home_location: "fsn1" + register: floatingIP +- name: verify test create Floating IP idempotency + assert: + that: + - floatingIP is not changed + +- name: test update Floating IP with check mode + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + description: "changed-description" + type: ipv4 + home_location: "fsn1" + check_mode: true + register: floatingIP +- name: verify test create Floating IP with check mode + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.description == "Web Server" + +- name: test update Floating IP + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + description: "changed-description" + type: ipv4 + home_location: "fsn1" + labels: + key: value + register: floatingIP +- name: verify test update Floating IP + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.description == "changed-description" + +- name: test update Floating IP idempotency + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + description: "changed-description" + type: ipv4 + home_location: "fsn1" + labels: + key: value + register: floatingIP +- name: verify test update Floating IP idempotency + assert: + that: + - floatingIP is not changed + +- name: test update Floating IP with same labels + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + home_location: "fsn1" + labels: + key: value + register: floatingIP +- name: verify test update Floating IP with same labels + assert: + that: + - floatingIP is not changed + +- name: test update Floating IP with other labels + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + home_location: "fsn1" + labels: + key: value + other: label + register: floatingIP +- name: verify test update Floating IP with other labels + assert: + that: + - floatingIP is changed + +- name: test update Floating IP with other labels in different order + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + home_location: "fsn1" + labels: + other: label + key: value + register: floatingIP +- name: verify test update Floating IP with other labels in different order + assert: + that: + - floatingIP is not changed + +- name: test assign Floating IP with checkmode + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + description: "changed-description" + type: ipv4 + server: "{{ main_server.hcloud_server.name }}" + check_mode: true + register: floatingIP +- name: verify test assign Floating IP with checkmode + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.server != main_server.hcloud_server.name + +- name: test assign Floating IP + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + description: "changed-description" + type: ipv4 + server: "{{ main_server.hcloud_server.name }}" + register: floatingIP +- name: verify test assign Floating IP + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.server == main_server.hcloud_server.name + +- name: test assign Floating IP idempotency + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + description: "changed-description" + type: ipv4 + server: "{{ main_server.hcloud_server.name }}" + register: floatingIP +- name: verify test unassign Floating IPidempotency + assert: + that: + - floatingIP is not changed + +- name: test unassign Floating IP + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + home_location: "fsn1" + register: floatingIP +- name: verify test unassign Floating IP + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.server != main_server.hcloud_server.name + +- name: test unassign Floating IP idempotency + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + home_location: "fsn1" + register: floatingIP +- name: verify test unassign Floating IPidempotency + assert: + that: + - floatingIP is not changed + +- name: test assign Floating IP again + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + server: "{{ main_server.hcloud_server.name }}" + register: floatingIP +- name: verify test assign Floating IP again + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.server == main_server.hcloud_server.name + +- name: test already assigned Floating IP assign without force + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + server: "{{ main_server2.hcloud_server.name }}" + register: floatingIP +- name: verify test already assigned Floating IP assign without force + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.server == main_server.hcloud_server.name + +- name: test already assigned Floating IP assign with force + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + force: true + server: "{{ main_server2.hcloud_server.name }}" + register: floatingIP +- name: verify test already assigned Floating IP assign with force + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.server == main_server2.hcloud_server.name + +- name: test update Floating IP delete protection + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + delete_protection: true + register: floatingIP +- name: verify update Floating IP delete protection + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.delete_protection is sameas true + +- name: test update Floating IP delete protection idempotency + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + delete_protection: true + register: floatingIP +- name: verify update Floating IP delete protection idempotency + assert: + that: + - floatingIP is not changed + - floatingIP.hcloud_floating_ip.delete_protection is sameas true + +- name: test Floating IP without delete protection set to be idempotent + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + register: floatingIP +- name: verify Floating IP without delete protection set to be idempotent + assert: + that: + - floatingIP is not changed + - floatingIP.hcloud_floating_ip.delete_protection is sameas true + +- name: test delete Floating IP fails if it is protected + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + state: "absent" + register: result + ignore_errors: true +- name: verify test delete floating ip + assert: + that: + - result is failed + - 'result.msg == "Floating IP deletion is protected"' + +- name: test update Floating IP delete protection + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + delete_protection: false + register: floatingIP +- name: verify update Floating IP delete protection + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.delete_protection is sameas false + +- name: test delete floating ip + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + state: "absent" + register: result +- name: verify test delete floating ip + assert: + that: + - result is changed + +- name: test create ipv6 floating ip + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv6 + home_location: "fsn1" + state: "present" + register: result +- name: verify test create ipv6 floating ip + assert: + that: + - result is changed + +- name: test delete ipv6 floating ip + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + state: "absent" + register: result +- name: verify test delete ipv6 floating ip + assert: + that: + - result is changed + +- name: cleanup + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify cleanup + assert: + that: + - result is changed +- name: cleanup another server + hetzner.hcloud.server: + name: "{{ main_server2.hcloud_server.name }}" + state: absent + register: result +- name: verify cleanup another server + assert: + that: + - result is changed + +- name: test create Floating IP with delete protection + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + home_location: fsn1 + delete_protection: true + register: floatingIP +- name: verify create Floating IP with delete protection + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.delete_protection is sameas true + +- name: test delete Floating IP fails if it is protected + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + state: "absent" + register: result + ignore_errors: true +- name: verify test delete floating ip + assert: + that: + - result is failed + - 'result.msg == "Floating IP deletion is protected"' + +- name: test update Floating IP delete protection + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + delete_protection: false + register: floatingIP +- name: verify update Floating IP delete protection + assert: + that: + - floatingIP is changed + - floatingIP.hcloud_floating_ip.delete_protection is sameas false + +- name: test delete floating ip + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + state: "absent" + register: result +- name: verify test delete floating ip + assert: + that: + - result is changed diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/aliases new file mode 100644 index 000000000..a6a90a6bf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group3 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/defaults/main/main.yml new file mode 100644 index 000000000..ffa5e4833 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_floating_ip_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/cleanup.yml new file mode 100644 index 000000000..611d8e00c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_floating_ip + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/prepare.yml new file mode 100644 index 000000000..26230ad54 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/prepare.yml @@ -0,0 +1,9 @@ +--- +- name: Create test_floating_ip + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + home_location: fsn1 + type: ipv4 + labels: + key: value + register: test_floating_ip diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/test.yml new file mode 100644 index 000000000..51fb56eb3 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/floating_ip_info/tasks/test.yml @@ -0,0 +1,77 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_floating_ip_info + hetzner.hcloud.floating_ip_info: + register: result +- name: Verify hcloud_floating_ip_info + ansible.builtin.assert: + that: + - result.hcloud_floating_ip_info | list | count >= 1 + +- name: Gather hcloud_floating_ip_info in check mode + hetzner.hcloud.floating_ip_info: + check_mode: true + register: result +- name: Verify hcloud_floating_ip_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_floating_ip_info | list | count >= 1 + +- name: Gather hcloud_floating_ip_info with correct id + hetzner.hcloud.floating_ip_info: + id: "{{ test_floating_ip.hcloud_floating_ip.id }}" + register: result +- name: Verify hcloud_floating_ip_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_floating_ip_info | list | count == 1 + +- name: Gather hcloud_floating_ip_info with wrong id + hetzner.hcloud.floating_ip_info: + id: "{{ test_floating_ip.hcloud_floating_ip.id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_floating_ip_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_floating_ip_info with correct name + hetzner.hcloud.floating_ip_info: + name: "{{ hcloud_floating_ip_name }}" + register: result +- name: Verify hcloud_floating_ip_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_floating_ip_info | list | count == 1 + +- name: Gather hcloud_floating_ip_info with wrong name + hetzner.hcloud.floating_ip_info: + name: "{{ hcloud_floating_ip_name }}-invalid" + register: result +- name: Verify hcloud_floating_ip_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_floating_ip_info | list | count == 0 + +- name: Gather hcloud_floating_ip_info with correct label selector + hetzner.hcloud.floating_ip_info: + label_selector: "key=value" + register: result +- name: Verify hcloud_floating_ip_info with correct label selector + ansible.builtin.assert: + that: + - > + result.hcloud_floating_ip_info + | selectattr('name', 'equalto', hcloud_floating_ip_name) + | list | count == 1 + +- name: Gather hcloud_floating_ip_info with wrong label selector + hetzner.hcloud.floating_ip_info: + label_selector: "key!=value" + register: result +- name: Verify hcloud_floating_ip_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_floating_ip_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/defaults/main.yml deleted file mode 100644 index 58312aec1..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_certificate_name: "{{hcloud_prefix}}-integration" -hcloud_dns_test_domain: "{{hcloud_prefix | truncate(19, False, 'ans')}}-{{100 | random }}.hc-certs.de" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/meta/main.yml deleted file mode 100644 index e531064ca..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/meta/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -dependencies: - - setup_selfsigned_certificate -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/tasks/main.yml deleted file mode 100644 index 615c89d0e..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate/tasks/main.yml +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: test missing required parameters on create certificate - hcloud_certificate: - name: "{{ hcloud_certificate_name }}" - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create certificate - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: certificate, private_key"' - -- name: test create certificate with check mode - hcloud_certificate: - name: "{{ hcloud_certificate_name }}" - certificate: "{{ certificate_example_com }}" - private_key: "{{ certificate_example_com_key }}" - register: result - check_mode: yes -- name: test create certificate with check mode - assert: - that: - - result is changed - -- name: test create certificate - hcloud_certificate: - name: "{{ hcloud_certificate_name }}" - certificate: "{{ certificate_example_com }}" - private_key: "{{ certificate_example_com_key }}" - labels: - key: value - my-label: label - register: certificate -- name: verify create certificate - assert: - that: - - certificate is changed - - certificate.hcloud_certificate.name == "{{ hcloud_certificate_name }}" - - certificate.hcloud_certificate.domain_names[0] == "www.example.com" - - certificate.hcloud_certificate.labels.key == "value" - -- name: test create certificate idempotence - hcloud_certificate: - name: "{{ hcloud_certificate_name }}" - certificate: "{{ certificate_example_com }}" - private_key: "{{ certificate_example_com_key }}" - register: result -- name: verify create certificate idempotence - assert: - that: - - result is not changed - -- name: test update certificate with check mode - hcloud_certificate: - id: "{{ certificate.hcloud_certificate.id }}" - name: "changed-{{ hcloud_certificate_name }}" - register: result - check_mode: yes -- name: test create certificate with check mode - assert: - that: - - result is changed - -- name: test update certificate - hcloud_certificate: - id: "{{ certificate.hcloud_certificate.id }}" - name: "changed-{{ hcloud_certificate_name }}" - labels: - key: value - register: result -- name: test update certificate - assert: - that: - - result is changed - - result.hcloud_certificate.name == "changed-{{ hcloud_certificate_name }}" - -- name: test update certificate with same labels - hcloud_certificate: - id: "{{ certificate.hcloud_certificate.id }}" - name: "changed-{{ hcloud_certificate_name }}" - labels: - key: value - register: result -- name: test update certificate with same labels - assert: - that: - - result is not changed - -- name: test update certificate with other labels - hcloud_certificate: - id: "{{ certificate.hcloud_certificate.id }}" - name: "changed-{{ hcloud_certificate_name }}" - labels: - key: value - test: "val123" - register: result -- name: test update certificate with other labels - assert: - that: - - result is changed - -- name: test rename certificate - hcloud_certificate: - id: "{{ certificate.hcloud_certificate.id }}" - name: "{{ hcloud_certificate_name }}" - register: result -- name: test rename certificate - assert: - that: - - result is changed - - result.hcloud_certificate.name == "{{ hcloud_certificate_name }}" - -- name: absent certificate - hcloud_certificate: - id: "{{ certificate.hcloud_certificate.id }}" - state: absent - register: result -- name: verify absent server - assert: - that: - - result is success - -- name: generate dns domain name - set_fact: - # hcloud_dns_test_domain uses random, which generates a new random number - # on every invocation, by saving it into a fact we generate the number once - hcloud_dns_test_domain: "{{ hcloud_dns_test_domain }}" - -- name: test create managed certificate - hcloud_certificate: - name: "{{ hcloud_certificate_name }}" - domain_names: - - "{{ hcloud_dns_test_domain }}" - type: managed - labels: - HC-Use-Staging-CA: "true" - register: result -- name: verify create managed certificate - assert: - that: - - result is changed - - result.hcloud_certificate.name == "{{ hcloud_certificate_name }}" - - result.hcloud_certificate.domain_names[0] == "{{ hcloud_dns_test_domain }}" - -- name: absent certificate - hcloud_certificate: - id: "{{ result.hcloud_certificate.id }}" - state: absent - register: result -- name: verify absent certificate - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/defaults/main.yml deleted file mode 100644 index 6205b19b4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_certificate_name: "always-there-cert" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/meta/main.yml deleted file mode 100644 index 34657c1c2..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/meta/main.yml +++ /dev/null @@ -1,4 +0,0 @@ -dependencies: - - setup_selfsigned_certificate -collections: - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/tasks/main.yml deleted file mode 100644 index d7128db34..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_certificate_info/tasks/main.yml +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- - -- name: create certificate - hcloud_certificate: - name: "{{ hcloud_certificate_name }}" - certificate: "{{ certificate_example_com }}" - private_key: "{{ certificate_example_com_key }}" - labels: - key: value - my-label: label - register: certificate -- name: verify create certificate - assert: - that: - - certificate is changed - - certificate.hcloud_certificate.name == "{{ hcloud_certificate_name }}" - - certificate.hcloud_certificate.domain_names[0] == "www.example.com" - - certificate.hcloud_certificate.labels.key == "value" - -- name: test gather hcloud certificate infos in check mode - hcloud_certificate_info: - register: hcloud_certificate - check_mode: yes -- name: verify test gather hcloud certificate infos in check mode - assert: - that: - - hcloud_certificate.hcloud_certificate_info| list | count >= 1 - -- name: test gather hcloud certificate infos - hcloud_certificate_info: - register: hcloud_certificate - check_mode: yes -- name: verify test gather hcloud certificate infos - assert: - that: - - hcloud_certificate.hcloud_certificate_info| list | count >= 1 - -- name: test gather hcloud certificate infos with correct label selector - hcloud_certificate_info: - label_selector: "key=value" - register: hcloud_certificate -- name: verify test gather hcloud certificate infos with correct label selector - assert: - that: - - hcloud_certificate.hcloud_certificate_info|selectattr('name','equalto','{{ hcloud_certificate_name }}') | list | count == 1 - -- name: test gather hcloud certificate infos with wrong label selector - hcloud_certificate_info: - label_selector: "key!=value" - register: hcloud_certificate -- name: verify test gather hcloud certificate infos with wrong label selector - assert: - that: - - hcloud_certificate.hcloud_certificate_info | list | count == 0 - -- name: absent certificate - hcloud_certificate: - id: "{{ certificate.hcloud_certificate.id }}" - state: absent - register: result -- name: verify absent certificate - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/defaults/main.yml deleted file mode 100644 index b9e045f40..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_datacenter_name: "fsn1-dc14" -hcloud_datacenter_id: 4 -hcloud_location_name: "fsn1" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/tasks/main.yml deleted file mode 100644 index 3d144ae47..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_datacenter_info/tasks/main.yml +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: test gather hcloud datacenter infos - hcloud_datacenter_info: - register: hcloud_datacenters - -- name: verify test gather hcloud datacenter infos - assert: - that: - - hcloud_datacenters.hcloud_datacenter_info| list | count >= 5 - -- name: test gather hcloud datacenter infos in check mode - hcloud_datacenter_info: - register: hcloud_datacenters - check_mode: yes - -- name: verify test gather hcloud datacenter infos in check mode - assert: - that: - - hcloud_datacenters.hcloud_datacenter_info| list | count >= 5 - -- name: test gather hcloud datacenter infos with correct name - hcloud_datacenter_info: - name: "{{hcloud_datacenter_name}}" - register: hcloud_datacenter -- name: verify test gather hcloud datacenter with correct name - assert: - that: - - hcloud_datacenter.hcloud_datacenter_info|selectattr('name','equalto','{{ hcloud_datacenter_name }}') |selectattr('location','equalto','{{ hcloud_location_name }}') | list | count == 1 - -- name: test gather hcloud datacenter infos with correct id - hcloud_datacenter_info: - id: "{{hcloud_datacenter_id}}" - register: hcloud_datacenter -- name: verify test gather hcloud datacenter with correct id - assert: - that: - - hcloud_datacenter.hcloud_datacenter_info|selectattr('name','equalto','{{ hcloud_datacenter_name }}') | list | count == 1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/defaults/main.yml deleted file mode 100644 index e7eff02a7..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_firewall_name: "{{hcloud_prefix}}-integration" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/tasks/main.yml deleted file mode 100644 index f54d351b2..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_firewall/tasks/main.yml +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup firewall to be absent - hcloud_firewall: - name: "{{ hcloud_firewall_name }}" - state: absent - -- name: test missing required parameters on create firewall - hcloud_firewall: - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create firewall - assert: - that: - - result is failed - - 'result.msg == "one of the following is required: id, name"' - -- name: test create firewall with check mode - hcloud_firewall: - name: "{{ hcloud_firewall_name }}" - register: result - check_mode: yes -- name: test create firewall with check mode - assert: - that: - - result is changed - -- name: test create firewall - hcloud_firewall: - name: "{{ hcloud_firewall_name }}" - rules: - - direction: in - protocol: icmp - source_ips: - - 0.0.0.0/0 - - ::/0 - description: "allow icmp in" - labels: - key: value - my-label: label - register: firewall -- name: verify create firewall - assert: - that: - - firewall is changed - - firewall.hcloud_firewall.name == "{{ hcloud_firewall_name }}" - - firewall.hcloud_firewall.rules | list | count == 1 - - firewall.hcloud_firewall.rules | selectattr('direction','equalto','in') | list | count == 1 - - firewall.hcloud_firewall.rules | selectattr('protocol','equalto','icmp') | list | count == 1 - - firewall.hcloud_firewall.rules | selectattr('description', 'equalto', 'allow icmp in') | list | count == 1 - -- name: test create firewall idempotence - hcloud_firewall: - name: "{{ hcloud_firewall_name }}" - rules: - - direction: in - protocol: icmp - source_ips: - - 0.0.0.0/0 - - ::/0 - description: "allow icmp in" - labels: - key: value - my-label: label - register: result -- name: verify create firewall idempotence - assert: - that: - - result is not changed - -- name: test update firewall rules - hcloud_firewall: - name: "{{ hcloud_firewall_name }}" - rules: - - direction: in - protocol: icmp - source_ips: - - 0.0.0.0/0 - - ::/0 - - direction: in - protocol: tcp - port: 80 - source_ips: - - 0.0.0.0/0 - - ::/0 - - direction: out - protocol: tcp - port: 80 - destination_ips: - - 0.0.0.0/0 - - ::/0 - description: allow tcp out - labels: - key: value - my-label: label - register: firewall -- name: verify update firewall rules - assert: - that: - - firewall is changed - - firewall.hcloud_firewall.name == "{{ hcloud_firewall_name }}" - - firewall.hcloud_firewall.rules | list | count == 3 - - firewall.hcloud_firewall.rules | selectattr('direction','equalto','in') | list | count == 2 - - firewall.hcloud_firewall.rules | selectattr('direction','equalto','out') | list | count == 1 - - firewall.hcloud_firewall.rules | selectattr('protocol','equalto','icmp') | list | count == 1 - - firewall.hcloud_firewall.rules | selectattr('protocol','equalto','tcp') | list | count == 2 - - firewall.hcloud_firewall.rules | selectattr('port','equalto','80') | list | count == 2 - - firewall.hcloud_firewall.rules | selectattr('description', 'equalto', 'allow tcp out') | list | count == 1 - -- name: test update firewall rules idempotence - hcloud_firewall: - name: "{{ hcloud_firewall_name }}" - rules: - - direction: in - protocol: icmp - source_ips: - - 0.0.0.0/0 - - ::/0 - - direction: in - protocol: tcp - port: 80 - source_ips: - - 0.0.0.0/0 - - ::/0 - - direction: out - protocol: tcp - port: 80 - destination_ips: - - 0.0.0.0/0 - - ::/0 - description: allow tcp out - labels: - key: value - my-label: label - register: result -- name: verify update firewall rules idempotence - assert: - that: - - result is not changed - -- name: test update firewall with check mode - hcloud_firewall: - id: "{{ firewall.hcloud_firewall.id }}" - name: "changed-{{ hcloud_firewall_name }}" - register: result - check_mode: yes -- name: test create firewall with check mode - assert: - that: - - result is changed - -- name: test update firewall - hcloud_firewall: - id: "{{ firewall.hcloud_firewall.id }}" - name: "changed-{{ hcloud_firewall_name }}" - labels: - key: value - register: result -- name: test update firewall - assert: - that: - - result is changed - - result.hcloud_firewall.name == "changed-{{ hcloud_firewall_name }}" - -- name: test update firewall with same labels - hcloud_firewall: - id: "{{ firewall.hcloud_firewall.id }}" - name: "changed-{{ hcloud_firewall_name }}" - labels: - key: value - register: result -- name: test update firewall with same labels - assert: - that: - - result is not changed - -- name: test update firewall with other labels - hcloud_firewall: - id: "{{ firewall.hcloud_firewall.id }}" - name: "changed-{{ hcloud_firewall_name }}" - labels: - key: value - test: "val123" - register: result -- name: test update firewall with other labels - assert: - that: - - result is changed - -- name: test rename firewall - hcloud_firewall: - id: "{{ firewall.hcloud_firewall.id }}" - name: "{{ hcloud_firewall_name }}" - register: result -- name: test rename firewall - assert: - that: - - result is changed - - result.hcloud_firewall.name == "{{ hcloud_firewall_name }}" - -- name: absent firewall - hcloud_firewall: - id: "{{ firewall.hcloud_firewall.id }}" - state: absent - register: result -- name: verify absent server - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/defaults/main.yml deleted file mode 100644 index ebd5ccc38..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_floating_ip_name: "{{hcloud_prefix}}-i" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}-fip-t" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/tasks/main.yml deleted file mode 100644 index 8ada4172f..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip/tasks/main.yml +++ /dev/null @@ -1,470 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup server - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cx11 - image: ubuntu-18.04 - state: started - location: "fsn1" - register: main_server -- name: verify setup server - assert: - that: - - main_server is changed - -- name: setup another server - hcloud_server: - name: "{{ hcloud_server_name }}2" - server_type: cx11 - image: ubuntu-18.04 - state: started - register: main_server2 -- name: verify setup another server - assert: - that: - - main_server2 is changed - -- name: test missing type parameter on create Floating IP - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - register: result - ignore_errors: yes -- name: verify fail test missing type parameter on create Floating IP - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: type"' - -- name: test missing required parameters on create Floating IP - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create Floating IP - assert: - that: - - result is failed - - 'result.msg == "one of the following is required: home_location, server"' - -- name: test missing type parameter on delete Floating IP - hcloud_floating_ip: - type: ipv4 - home_location: "fsn1" - state: "absent" - register: result - ignore_errors: yes -- name: verify fail test missing type parameter on delete Floating IP - assert: - that: - - result is failed - - 'result.msg == "one of the following is required: id, name"' - - -- name: test invalid type - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv5 - home_location: "fsn1" - register: result - ignore_errors: yes -- name: verify invalid type - assert: - that: - - result is failed - - 'result.msg == "value of type must be one of: ipv4, ipv6, got: ipv5"' - -- name: test invalid location - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - home_location: "abc" - register: result - ignore_errors: yes -- name: verify invalid location - assert: - that: - - result is failed - - result.msg == "invalid input in fields 'server', 'home_location'" - -- name: test create Floating IP with check mode - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - description: "Web Server" - type: ipv4 - home_location: "fsn1" - register: floatingIP - check_mode: yes -- name: verify test create Floating IP with check mode - assert: - that: - - floatingIP is changed - -- name: test create Floating IP - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - description: "Web Server" - type: ipv4 - home_location: "fsn1" - register: floatingIP -- name: verify test create Floating IP - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.name ==hcloud_floating_ip_name - - floatingIP.hcloud_floating_ip.description == "Web Server" - - floatingIP.hcloud_floating_ip.home_location == "fsn1" - -- name: test create Floating IP idempotency - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - description: "Web Server" - type: ipv4 - home_location: "fsn1" - register: floatingIP -- name: verify test create Floating IP idempotency - assert: - that: - - floatingIP is not changed - -- name: test update Floating IP with check mode - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - description: "changed-description" - type: ipv4 - home_location: "fsn1" - check_mode: yes - register: floatingIP -- name: verify test create Floating IP with check mode - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.description == "Web Server" - -- name: test update Floating IP - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - description: "changed-description" - type: ipv4 - home_location: "fsn1" - labels: - key: value - register: floatingIP -- name: verify test update Floating IP - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.description == "changed-description" - -- name: test update Floating IP idempotency - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - description: "changed-description" - type: ipv4 - home_location: "fsn1" - labels: - key: value - register: floatingIP -- name: verify test update Floating IP idempotency - assert: - that: - - floatingIP is not changed - -- name: test update Floating IP with same labels - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - home_location: "fsn1" - labels: - key: value - register: floatingIP -- name: verify test update Floating IP with same labels - assert: - that: - - floatingIP is not changed - -- name: test update Floating IP with other labels - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - home_location: "fsn1" - labels: - key: value - other: label - register: floatingIP -- name: verify test update Floating IP with other labels - assert: - that: - - floatingIP is changed - -- name: test update Floating IP with other labels in different order - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - home_location: "fsn1" - labels: - other: label - key: value - register: floatingIP -- name: verify test update Floating IP with other labels in different order - assert: - that: - - floatingIP is not changed - -- name: test assign Floating IP with checkmode - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - description: "changed-description" - type: ipv4 - server: "{{ main_server.hcloud_server.name }}" - check_mode: yes - register: floatingIP -- name: verify test assign Floating IP with checkmode - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.server != "{{ main_server.hcloud_server.name }}" - -- name: test assign Floating IP - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - description: "changed-description" - type: ipv4 - server: "{{ main_server.hcloud_server.name }}" - register: floatingIP -- name: verify test assign Floating IP - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.server == "{{ main_server.hcloud_server.name }}" - -- name: test assign Floating IP idempotency - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - description: "changed-description" - type: ipv4 - server: "{{ main_server.hcloud_server.name }}" - register: floatingIP -- name: verify test unassign Floating IPidempotency - assert: - that: - - floatingIP is not changed - -- name: test unassign Floating IP - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - home_location: "fsn1" - register: floatingIP -- name: verify test unassign Floating IP - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.server != "{{ main_server.hcloud_server.name }}" - -- name: test unassign Floating IP idempotency - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - home_location: "fsn1" - register: floatingIP -- name: verify test unassign Floating IPidempotency - assert: - that: - - floatingIP is not changed - -- name: test assign Floating IP again - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - server: "{{ main_server.hcloud_server.name }}" - register: floatingIP -- name: verify test assign Floating IP again - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.server == "{{ main_server.hcloud_server.name }}" - -- name: test already assigned Floating IP assign without force - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - server: "{{ main_server2.hcloud_server.name }}" - register: floatingIP -- name: verify test already assigned Floating IP assign without force - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.server == "{{ main_server.hcloud_server.name }}" - -- name: test already assigned Floating IP assign with force - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - force: yes - server: "{{ main_server2.hcloud_server.name }}" - register: floatingIP -- name: verify test already assigned Floating IP assign with force - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.server == "{{ main_server2.hcloud_server.name }}" - -- name: test update Floating IP delete protection - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - delete_protection: true - register: floatingIP -- name: verify update Floating IP delete protection - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.delete_protection is sameas true - -- name: test update Floating IP delete protection idempotency - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - delete_protection: true - register: floatingIP -- name: verify update Floating IP delete protection idempotency - assert: - that: - - floatingIP is not changed - - floatingIP.hcloud_floating_ip.delete_protection is sameas true - -- name: test Floating IP without delete protection set to be idempotent - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - register: floatingIP -- name: verify Floating IP without delete protection set to be idempotent - assert: - that: - - floatingIP is not changed - - floatingIP.hcloud_floating_ip.delete_protection is sameas true - -- name: test delete Floating IP fails if it is protected - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - state: "absent" - register: result - ignore_errors: yes -- name: verify test delete floating ip - assert: - that: - - result is failed - - 'result.msg == "Floating IP deletion is protected"' - -- name: test update Floating IP delete protection - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - delete_protection: false - register: floatingIP -- name: verify update Floating IP delete protection - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.delete_protection is sameas false - -- name: test delete floating ip - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - state: "absent" - register: result -- name: verify test delete floating ip - assert: - that: - - result is changed - -- name: test create ipv6 floating ip - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv6 - home_location: "fsn1" - state: "present" - register: result -- name: verify test create ipv6 floating ip - assert: - that: - - result is changed - -- name: test delete ipv6 floating ip - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - state: "absent" - register: result -- name: verify test delete ipv6 floating ip - assert: - that: - - result is changed - -- name: cleanup - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is changed -- name: cleanup another server - hcloud_server: - name: "{{ main_server2.hcloud_server.name }}" - state: absent - register: result -- name: verify cleanup another server - assert: - that: - - result is changed - -- name: test create Floating IP with delete protection - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - home_location: fsn1 - delete_protection: true - register: floatingIP -- name: verify create Floating IP with delete protection - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.delete_protection is sameas true - -- name: test delete Floating IP fails if it is protected - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - state: "absent" - register: result - ignore_errors: yes -- name: verify test delete floating ip - assert: - that: - - result is failed - - 'result.msg == "Floating IP deletion is protected"' - -- name: test update Floating IP delete protection - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - delete_protection: false - register: floatingIP -- name: verify update Floating IP delete protection - assert: - that: - - floatingIP is changed - - floatingIP.hcloud_floating_ip.delete_protection is sameas false - -- name: test delete floating ip - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - state: "absent" - register: result -- name: verify test delete floating ip - assert: - that: - - result is changed diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/defaults/main.yml deleted file mode 100644 index f4c6a9fc9..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_floating_ip_name: "{{hcloud_prefix}}-i" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/tasks/main.yml deleted file mode 100644 index 9ca1c2a4a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_floating_ip_info/tasks/main.yml +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup ensure floating ip is absent - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - state: absent - -- name: setup floating ip - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - home_location: "fsn1" - type: ipv4 - labels: - key: value - register: test_floating_ip - -- name: verify setup floating ip - assert: - that: - - test_floating_ip is changed - -- name: test gather hcloud floating ip infos - hcloud_floating_ip_info: - register: hcloud_floating_ips -- name: verify test gather hcloud floating ip infos - assert: - that: - - hcloud_floating_ips.hcloud_floating_ip_info| list | count >= 1 - -- name: test gather hcloud floating ip infos in check mode - hcloud_floating_ip_info: - check_mode: yes - register: hcloud_floating_ips - -- name: verify test gather hcloud floating ip infos in check mode - assert: - that: - - hcloud_floating_ips.hcloud_floating_ip_info| list | count >= 1 - - -- name: test gather hcloud floating ip infos with correct label selector - hcloud_floating_ip_info: - label_selector: "key=value" - register: hcloud_floating_ips -- name: verify test gather hcloud floating ip with correct label selector - assert: - that: - - hcloud_floating_ips.hcloud_floating_ip_info|selectattr('name','equalto','{{ test_floating_ip.hcloud_floating_ip.name }}') | list | count == 1 - -- name: test gather hcloud floating ip infos with wrong label selector - hcloud_floating_ip_info: - label_selector: "key!=value" - register: hcloud_floating_ips -- name: verify test gather hcloud floating ip with wrong label selector - assert: - that: - - hcloud_floating_ips.hcloud_floating_ip_info | list | count == 0 - -- name: test gather hcloud floating ip infos with correct id - hcloud_floating_ip_info: - id: "{{test_floating_ip.hcloud_floating_ip.id}}" - register: hcloud_floating_ips -- name: verify test gather hcloud floating ip with correct id - assert: - that: - - hcloud_floating_ips.hcloud_floating_ip_info|selectattr('name','equalto','{{ test_floating_ip.hcloud_floating_ip.name }}') | list | count == 1 - -- name: test gather hcloud floating ip infos with wrong id - hcloud_floating_ip_info: - id: "{{test_floating_ip.hcloud_floating_ip.id}}1" - register: result - ignore_errors: yes -- name: verify test gather hcloud floating ip with wrong id - assert: - that: - - result is failed - -- name: cleanup - hcloud_floating_ip: - id: "{{ test_floating_ip.hcloud_floating_ip.id }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/defaults/main.yml deleted file mode 100644 index 7c25d171d..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/defaults/main.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_test_image_name: "always-there-snapshot" -hcloud_test_image_id: 10164049 -hcloud_test_image_name_os: "ubuntu-22.04" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/tasks/main.yml deleted file mode 100644 index 16ed44a28..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_image_info/tasks/main.yml +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: test gather hcloud image infos with type system - hcloud_image_info: - register: hcloud_images -- name: verify test gather hcloud image infos in check mode - assert: - that: - - hcloud_images.hcloud_image_info| list | count > 2 - -- name: test gather hcloud image infos in check mode - hcloud_image_info: - check_mode: yes - register: hcloud_images - -- name: verify test gather hcloud image infos in check mode - assert: - that: - - hcloud_images.hcloud_image_info| list | count > 2 - - -- name: test gather hcloud image infos with correct label selector - hcloud_image_info: - label_selector: "key=value" - type: snapshot - register: hcloud_images -- name: verify test gather hcloud image with correct label selector - assert: - that: - - hcloud_images.hcloud_image_info|selectattr('description','equalto','{{ hcloud_test_image_name }}') | list | count == 1 - -- name: test gather hcloud image infos with wrong label selector - hcloud_image_info: - label_selector: "key!=value" - type: snapshot - register: hcloud_images -- name: verify test gather hcloud image with wrong label selector - assert: - that: - - hcloud_images.hcloud_image_info | list | count == 0 - -- name: test gather hcloud image infos with correct id - hcloud_image_info: - id: "{{hcloud_test_image_id}}" - type: snapshot - register: hcloud_images -- name: verify test gather hcloud image with correct id - assert: - that: - - hcloud_images.hcloud_image_info|selectattr('description','equalto','{{ hcloud_test_image_name }}') | list | count == 1 - -- name: test gather hcloud image infos with wrong id - hcloud_image_info: - id: "{{hcloud_test_image_id}}1" - type: snapshot - ignore_errors: yes - register: result -- name: verify test gather hcloud image with wrong id - assert: - that: - - result is failed - -- name: test gather hcloud image infos with name - hcloud_image_info: - name: "{{ hcloud_test_image_name_os }}" - register: hcloud_images -- name: verify test gather hcloud image infos with name - assert: - that: - - hcloud_images.hcloud_image_info | list | count == 1 - - hcloud_images.hcloud_image_info[0].architecture == "x86" - -- name: test gather hcloud image infos with name and architecture - hcloud_image_info: - name: "{{ hcloud_test_image_name_os }}" - architecture: arm - register: hcloud_images -- name: verify test gather hcloud image infos with name - assert: - that: - - hcloud_images.hcloud_image_info | list | count == 1 - - hcloud_images.hcloud_image_info[0].architecture == "arm" - -- name: test gather hcloud image infos with architecture - hcloud_image_info: - architecture: arm - register: hcloud_images -- name: verify test gather hcloud image infos with name - assert: - that: - - hcloud_images.hcloud_image_info | selectattr('architecture','equalto','x86') | list | count == 0 - - hcloud_images.hcloud_image_info | selectattr('architecture','equalto','arm') | list | count > 2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/aliases deleted file mode 100644 index 18dc30b6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/defaults/main.yml deleted file mode 100644 index 7f431cd8d..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_load_balancer_name: "{{hcloud_prefix}}-i" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/tasks/main.yml deleted file mode 100644 index a25e550d0..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer/tasks/main.yml +++ /dev/null @@ -1,247 +0,0 @@ -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: absent - register: result -- name: verify setup - assert: - that: - - result is success -- name: test missing required parameters on create Load Balancer - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create Load Balancer - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: load_balancer_type"' - -- name: test create Load Balancer with check mode - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - load_balancer_type: lb11 - network_zone: eu-central - state: present - register: result - check_mode: yes -- name: test create Load Balancer with check mode - assert: - that: - - result is changed - -- name: test create Load Balancer - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name}}" - load_balancer_type: lb11 - network_zone: eu-central - state: present - register: main_load_balancer -- name: verify create Load Balancer - assert: - that: - - main_load_balancer is changed - - main_load_balancer.hcloud_load_balancer.name == "{{ hcloud_load_balancer_name }}" - - main_load_balancer.hcloud_load_balancer.load_balancer_type == "lb11" - -- name: test create Load Balancer idempotence - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - load_balancer_type: lb11 - network_zone: eu-central - state: present - register: result -- name: verify create Load Balancer idempotence - assert: - that: - - result is not changed - -- name: test change Load Balancer type - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - load_balancer_type: lb21 - state: present - register: result_after_test - ignore_errors: true -- name: verify change Load Balancer type - assert: - that: - - result_after_test is changed - - result_after_test.hcloud_load_balancer.load_balancer_type == "lb21" - -- name: test Load Balancer without type set to be idempotent - hcloud_load_balancer: - name: "{{hcloud_load_balancer_name}}" - register: result_after_test -- name: verify test Load Balancer without type set to be idempotent - assert: - that: - - result_after_test is not changed - - result_after_test.hcloud_load_balancer.load_balancer_type == "lb21" - -- name: test update Load Balancer protection - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - delete_protection: true - state: present - register: result_after_test - ignore_errors: true -- name: verify update Load Balancer protection - assert: - that: - - result_after_test is changed - - result_after_test.hcloud_load_balancer.delete_protection is sameas true - -- name: test Load Balancer without protection set to be idempotent - hcloud_load_balancer: - name: "{{hcloud_load_balancer_name}}" - register: result_after_test -- name: verify test Load Balancer without protection set to be idempotent - assert: - that: - - result_after_test is not changed - - result_after_test.hcloud_load_balancer.delete_protection is sameas true - -- name: test delete Load Balancer fails if it is protected - hcloud_load_balancer: - name: "{{hcloud_load_balancer_name}}" - state: absent - ignore_errors: yes - register: result -- name: verify delete Load Balancer fails if it is protected - assert: - that: - - result is failed - - 'result.msg == "load balancer deletion is protected"' - -- name: test remove Load Balancer protection - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - delete_protection: false - state: present - register: result_after_test - ignore_errors: true -- name: verify remove Load Balancer protection - assert: - that: - - result_after_test is changed - - result_after_test.hcloud_load_balancer.delete_protection is sameas false - -- name: absent Load Balancer - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: absent - register: result -- name: verify absent Load Balancer - assert: - that: - - result is success - -- name: test create Load Balancer with labels - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name}}" - load_balancer_type: lb11 - network_zone: eu-central - labels: - key: value - mylabel: "val123" - state: present - register: main_load_balancer -- name: verify create Load Balancer with labels - assert: - that: - - main_load_balancer is changed - - main_load_balancer.hcloud_load_balancer.labels.key == "value" - - main_load_balancer.hcloud_load_balancer.labels.mylabel == "val123" - -- name: test update Load Balancer with labels - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name}}" - load_balancer_type: lb11 - network_zone: eu-central - labels: - key: other - mylabel: "val123" - state: present - register: main_load_balancer -- name: verify update Load Balancer with labels - assert: - that: - - main_load_balancer is changed - - main_load_balancer.hcloud_load_balancer.labels.key == "other" - - main_load_balancer.hcloud_load_balancer.labels.mylabel == "val123" - -- name: test update Load Balancer with labels in other order - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name}}" - load_balancer_type: lb11 - network_zone: eu-central - labels: - mylabel: "val123" - key: other - state: present - register: main_load_balancer -- name: verify update Load Balancer with labels in other order - assert: - that: - - main_load_balancer is not changed - -- name: cleanup with labels - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success - -- name: test create Load Balancer with delete protection - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - load_balancer_type: lb11 - network_zone: eu-central - delete_protection: true - register: main_load_balancer -- name: verify create Load Balancer with delete protection - assert: - that: - - main_load_balancer is changed - - main_load_balancer.hcloud_load_balancer.delete_protection is sameas true - -- name: test delete Load Balancer fails if it is protected - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: "absent" - register: result - ignore_errors: yes -- name: verify test delete Load Balancer - assert: - that: - - result is failed - - 'result.msg == "load balancer deletion is protected"' - -- name: test update Load Balancer delete protection - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - delete_protection: false - register: main_load_balancer -- name: verify update Load Balancer delete protection - assert: - that: - - main_load_balancer is changed - - main_load_balancer.hcloud_load_balancer.delete_protection is sameas false - -- name: test delete Load Balancer - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: "absent" - register: result -- name: verify test delete Load Balancer - assert: - that: - - result is changed diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/aliases deleted file mode 100644 index 18dc30b6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/defaults/main.yml deleted file mode 100644 index 326973a78..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_load_balancer_name: "{{hcloud_prefix}}-i" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}-lb-i" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/tasks/main.yml deleted file mode 100644 index 9e6528858..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_info/tasks/main.yml +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup ensure Load Balancer is absent - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: absent -- name: setup server - hcloud_server: - name: "{{hcloud_server_name}}" - server_type: cx11 - image: ubuntu-20.04 - state: started - register: server -- name: verify setup server - assert: - that: - - server is success -- name: setup Load Balancer - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - load_balancer_type: lb11 - network_zone: eu-central - labels: - key: value - register: test_load_balancer - -- name: verify setup Load Balancer - assert: - that: - - test_load_balancer is changed - -- name: test create load_balancer target - hcloud_load_balancer_target: - type: "server" - load_balancer: "{{hcloud_load_balancer_name}}" - server: "{{hcloud_server_name}}" - state: present - register: load_balancer_target -- name: verify create load_balancer target - assert: - that: - - load_balancer_target is success -- name: test create load_balancer service - hcloud_load_balancer_service: - load_balancer: "{{hcloud_load_balancer_name}}" - protocol: "http" - listen_port: 80 - state: present - register: load_balancer_service -- name: verify create load_balancer service - assert: - that: - - load_balancer_service is success - -- name: test gather hcloud Load Balancer infos - hcloud_load_balancer_info: - id: "{{test_load_balancer.hcloud_load_balancer.id}}" - register: hcloud_load_balancers -- name: verify test gather hcloud Load Balancer infos - assert: - that: - - hcloud_load_balancers.hcloud_load_balancer_info| list | count >= 1 - - hcloud_load_balancers.hcloud_load_balancer_info[0].targets | list | count == 1 - - hcloud_load_balancers.hcloud_load_balancer_info[0].targets | selectattr('type','equalto','server') | list | count == 1 - - hcloud_load_balancers.hcloud_load_balancer_info[0].targets | selectattr('server','equalto','{{ hcloud_server_name }}') | list | count == 1 - - hcloud_load_balancers.hcloud_load_balancer_info[0].services | list | count == 1 - - hcloud_load_balancers.hcloud_load_balancer_info[0].services | selectattr('protocol','equalto','http') | list | count == 1 - - hcloud_load_balancers.hcloud_load_balancer_info[0].services | selectattr('listen_port','equalto',80) | list | count == 1 - - hcloud_load_balancers.hcloud_load_balancer_info[0].services | selectattr('destination_port','equalto',80) | list | count == 1 - -- name: test gather hcloud Load Balancer infos in check mode - hcloud_load_balancer_info: - check_mode: yes - register: hcloud_load_balancers - -- name: verify test gather hcloud Load Balancer infos in check mode - assert: - that: - - hcloud_load_balancers.hcloud_load_balancer_info| list | count >= 1 - - -- name: test gather hcloud Load Balancer infos with correct label selector - hcloud_load_balancer_info: - label_selector: "key=value" - register: hcloud_load_balancers -- name: verify test gather hcloud Load Balancer with correct label selector - assert: - that: - - hcloud_load_balancers.hcloud_load_balancer_info|selectattr('name','equalto','{{ test_load_balancer.hcloud_load_balancer.name }}') | list | count == 1 - -- name: test gather hcloud Load Balancer infos with wrong label selector - hcloud_load_balancer_info: - label_selector: "key!=value" - register: hcloud_load_balancers -- name: verify test gather hcloud Load Balancer with wrong label selector - assert: - that: - - hcloud_load_balancers.hcloud_load_balancer_info | list | count == 0 - -- name: test gather hcloud Load Balancer infos with correct id - hcloud_load_balancer_info: - id: "{{test_load_balancer.hcloud_load_balancer.id}}" - register: hcloud_load_balancers -- name: verify test gather hcloud Load Balancer with correct id - assert: - that: - - hcloud_load_balancers.hcloud_load_balancer_info|selectattr('name','equalto','{{ test_load_balancer.hcloud_load_balancer.name }}') | list | count == 1 - -- name: test gather hcloud Load Balancer infos with wrong id - hcloud_load_balancer_info: - id: "{{test_load_balancer.hcloud_load_balancer.id}}1" - register: result - ignore_errors: yes -- name: verify test gather hcloud Load Balancer with wrong id - assert: - that: - - result is failed - -- name: cleanup - hcloud_load_balancer: - id: "{{ test_load_balancer.hcloud_load_balancer.id }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/aliases deleted file mode 100644 index 18dc30b6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/defaults/main.yml deleted file mode 100644 index 6abf9ceec..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_network_name: "{{hcloud_prefix}}-lb-n" -hcloud_load_balancer_name: "{{hcloud_prefix}}-lb-n" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/tasks/main.yml deleted file mode 100644 index 9a1bf5175..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_network/tasks/main.yml +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup network - hcloud_network: - name: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/8" - state: present - register: network -- name: verify setup network - assert: - that: - - network is success - -- name: setup subnetwork - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "cloud" - network_zone: "eu-central" - state: present - register: subnetwork -- name: verify subnetwork - assert: - that: - - subnetwork is success - -- name: setup load_balancer - hcloud_load_balancer: - name: "{{hcloud_load_balancer_name}}" - load_balancer_type: lb11 - state: present - location: "fsn1" - register: load_balancer -- name: verify setup load_balancer - assert: - that: - - load_balancer is success - -- name: test missing required parameters on create load_balancer network - hcloud_load_balancer_network: - state: present - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create load_balancer network - assert: - that: - - result is failed - - '"missing required arguments:" in result.msg' - -- name: test fail load balancer does not exist - hetzner.hcloud.hcloud_load_balancer_network: - network: "{{ hcloud_network_name }}" - load_balancer: does-not-exist - state: present - register: result - ignore_errors: true -- name: verify test fail load_balancer does not exist - assert: - that: - - result is failed - - "result.msg == 'Load balancer does not exist: does-not-exist'" - -- name: test fail network does not exist - hetzner.hcloud.hcloud_load_balancer_network: - network: does-not-exist - load_balancer: "{{ hcloud_load_balancer_name }}" - state: present - register: result - ignore_errors: true -- name: verify test fail network does not exist - assert: - that: - - result is failed - - "result.msg == 'Network does not exist: does-not-exist'" - -- name: test create load_balancer network with checkmode - hcloud_load_balancer_network: - network: "{{ hcloud_network_name }}" - load_balancer: "{{hcloud_load_balancer_name}}" - state: present - register: result - check_mode: yes -- name: verify test create load_balancer network with checkmode - assert: - that: - - result is changed - -- name: test create load_balancer network - hcloud_load_balancer_network: - network: "{{ hcloud_network_name }}" - load_balancer: "{{hcloud_load_balancer_name}}" - state: present - register: load_balancerNetwork -- name: verify create load_balancer network - assert: - that: - - load_balancerNetwork is changed - - load_balancerNetwork.hcloud_load_balancer_network.network == hcloud_network_name - - load_balancerNetwork.hcloud_load_balancer_network.load_balancer == hcloud_load_balancer_name - -- name: test create load_balancer network idempotency - hcloud_load_balancer_network: - network: "{{ hcloud_network_name }}" - load_balancer: "{{hcloud_load_balancer_name}}" - state: present - register: load_balancerNetwork -- name: verify create load_balancer network idempotency - assert: - that: - - load_balancerNetwork is not changed - -- name: test absent load_balancer network - hcloud_load_balancer_network: - network: "{{ hcloud_network_name }}" - load_balancer: "{{hcloud_load_balancer_name}}" - state: absent - register: result -- name: verify test absent load_balancer network - assert: - that: - - result is changed - -- name: test create load_balancer network with specified ip - hcloud_load_balancer_network: - network: "{{ hcloud_network_name }}" - load_balancer: "{{hcloud_load_balancer_name}}" - ip: "10.0.0.2" - state: present - register: load_balancerNetwork -- name: verify create load_balancer network with specified ip - assert: - that: - - load_balancerNetwork is changed - - load_balancerNetwork.hcloud_load_balancer_network.network == hcloud_network_name - - load_balancerNetwork.hcloud_load_balancer_network.load_balancer == hcloud_load_balancer_name - - load_balancerNetwork.hcloud_load_balancer_network.ip == "10.0.0.2" - -- name: cleanup create load_balancer network with specified ip - hcloud_load_balancer_network: - network: "{{ hcloud_network_name }}" - load_balancer: "{{hcloud_load_balancer_name}}" - state: absent - register: result -- name: verify cleanup create load_balancer network with specified ip - assert: - that: - - result is changed - -- name: cleanup load_balancer - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: absent - register: result -- name: verify cleanup load_balancer - assert: - that: - - result is success - -- name: cleanup subnetwork - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "cloud" - network_zone: "eu-central" - state: absent - register: result -- name: verify cleanup subnetwork - assert: - that: - - result is changed - -- name: cleanup - hcloud_network: - name: "{{hcloud_network_name}}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/aliases deleted file mode 100644 index 18dc30b6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/defaults/main.yml deleted file mode 100644 index ebf456312..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_load_balancer_name: "{{hcloud_prefix}}-lb-target" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/tasks/main.yml deleted file mode 100644 index b0db6bb63..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_service/tasks/main.yml +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup load_balancer - hcloud_load_balancer: - name: "{{hcloud_load_balancer_name}}" - load_balancer_type: lb11 - state: present - location: "fsn1" - register: load_balancer -- name: verify setup load_balancer - assert: - that: - - load_balancer is success - -- name: test fail load balancer does not exist - hetzner.hcloud.hcloud_load_balancer_service: - load_balancer: does-not-exist - protocol: http - listen_port: 80 - state: present - register: result - ignore_errors: true -- name: verify test fail load_balancer does not exist - assert: - that: - - result is failed - - "result.msg == 'Load balancer does not exist: does-not-exist'" - -- name: test create load_balancer service with checkmode - hcloud_load_balancer_service: - load_balancer: "{{hcloud_load_balancer_name}}" - protocol: "http" - listen_port: 80 - state: present - register: result - check_mode: yes -- name: verify test create load_balancer service with checkmode - assert: - that: - - result is changed - -- name: test create load_balancer service - hcloud_load_balancer_service: - load_balancer: "{{hcloud_load_balancer_name}}" - protocol: "http" - listen_port: 80 - state: present - register: load_balancer_service -- name: verify create load_balancer service - assert: - that: - - load_balancer_service is changed - - load_balancer_service.hcloud_load_balancer_service.protocol == "http" - - load_balancer_service.hcloud_load_balancer_service.listen_port == 80 - - load_balancer_service.hcloud_load_balancer_service.destination_port == 80 - - load_balancer_service.hcloud_load_balancer_service.proxyprotocol is sameas false - -- name: test create load_balancer service idempotency - hcloud_load_balancer_service: - load_balancer: "{{hcloud_load_balancer_name}}" - protocol: "http" - listen_port: 80 - state: present - register: load_balancer_service -- name: verify create load_balancer service idempotency - assert: - that: - - load_balancer_service is not changed - -- name: test update load_balancer service - hcloud_load_balancer_service: - load_balancer: "{{hcloud_load_balancer_name}}" - protocol: "tcp" - listen_port: 80 - state: present - register: load_balancer_service -- name: verify create load_balancer service - assert: - that: - - load_balancer_service is changed - - load_balancer_service.hcloud_load_balancer_service.protocol == "tcp" - - load_balancer_service.hcloud_load_balancer_service.listen_port == 80 - - load_balancer_service.hcloud_load_balancer_service.destination_port == 80 - - load_balancer_service.hcloud_load_balancer_service.proxyprotocol is sameas false - -- name: test absent load_balancer service - hcloud_load_balancer_service: - load_balancer: "{{hcloud_load_balancer_name}}" - protocol: "http" - listen_port: 80 - state: absent - register: result -- name: verify test absent load_balancer service - assert: - that: - - result is changed - -- name: test create load_balancer service with http - hcloud_load_balancer_service: - load_balancer: "{{hcloud_load_balancer_name}}" - protocol: "http" - listen_port: 80 - http: - cookie_name: "Test" - sticky_sessions: yes - state: present - register: load_balancer_service -- name: verify create load_balancer service - assert: - that: - - load_balancer_service is changed - - load_balancer_service.hcloud_load_balancer_service.protocol == "http" - - load_balancer_service.hcloud_load_balancer_service.listen_port == 80 - - load_balancer_service.hcloud_load_balancer_service.destination_port == 80 - - load_balancer_service.hcloud_load_balancer_service.proxyprotocol is sameas false - -- name: cleanup load_balancer - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: absent - register: result -- name: verify cleanup load_balancer - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/aliases deleted file mode 100644 index 18dc30b6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/defaults/main.yml deleted file mode 100644 index 180133fde..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/defaults/main.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}-lb-t" -hcloud_load_balancer_name: "{{hcloud_prefix}}-lb-target" -hcloud_testing_ip: "176.9.59.39" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/tasks/main.yml deleted file mode 100644 index bd96c1a54..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_target/tasks/main.yml +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup server - hcloud_server: - name: "{{hcloud_server_name}}" - server_type: cx11 - image: ubuntu-20.04 - state: started - location: "fsn1" - register: server -- name: verify setup server - assert: - that: - - server is success - -- name: setup load_balancer - hcloud_load_balancer: - name: "{{hcloud_load_balancer_name}}" - load_balancer_type: lb11 - state: present - location: "fsn1" - register: load_balancer -- name: verify setup load_balancer - assert: - that: - - load_balancer is success - -- name: test fail load balancer does not exist - hetzner.hcloud.hcloud_load_balancer_target: - type: server - load_balancer: does-not-exist - server: "{{ hcloud_server_name }}" - register: result - ignore_errors: true -- name: verify test fail load_balancer does not exist - assert: - that: - - result is failed - - "result.msg == 'Load balancer does not exist: does-not-exist'" - -- name: test fail server does not exist - hetzner.hcloud.hcloud_load_balancer_target: - type: server - load_balancer: "{{ hcloud_load_balancer_name }}" - server: does-not-exist - register: result - ignore_errors: true -- name: verify test fail server does not exist - assert: - that: - - result is failed - - "result.msg == 'Server not found: does-not-exist'" - -- name: test create load_balancer target with checkmode - hcloud_load_balancer_target: - type: "server" - load_balancer: "{{hcloud_load_balancer_name}}" - server: "{{hcloud_server_name}}" - state: present - register: result - check_mode: yes -- name: verify test create load_balancer target with checkmode - assert: - that: - - result is changed - -- name: test create load_balancer target - hcloud_load_balancer_target: - type: "server" - load_balancer: "{{hcloud_load_balancer_name}}" - server: "{{hcloud_server_name}}" - state: present - register: load_balancer_target -- name: verify create load_balancer target - assert: - that: - - load_balancer_target is changed - - load_balancer_target.hcloud_load_balancer_target.type == "server" - - load_balancer_target.hcloud_load_balancer_target.server == hcloud_server_name - - load_balancer_target.hcloud_load_balancer_target.load_balancer == hcloud_load_balancer_name - -- name: test create load_balancer target idempotency - hcloud_load_balancer_target: - type: "server" - load_balancer: "{{hcloud_load_balancer_name}}" - server: "{{hcloud_server_name}}" - state: present - register: load_balancer_target -- name: verify create load_balancer target idempotency - assert: - that: - - load_balancer_target is not changed - -- name: test absent load_balancer target - hcloud_load_balancer_target: - type: "server" - load_balancer: "{{hcloud_load_balancer_name}}" - server: "{{hcloud_server_name}}" - state: absent - register: result -- name: verify test absent load_balancer target - assert: - that: - - result is changed - -- name: test create label_selector target - hcloud_load_balancer_target: - type: "label_selector" - load_balancer: "{{hcloud_load_balancer_name}}" - label_selector: "application=backend" - state: present - register: load_balancer_target -- name: verify create label_selector target - assert: - that: - - load_balancer_target is changed - - load_balancer_target.hcloud_load_balancer_target.type == "label_selector" - - load_balancer_target.hcloud_load_balancer_target.label_selector == "application=backend" - - load_balancer_target.hcloud_load_balancer_target.load_balancer == hcloud_load_balancer_name - -- name: test create ip target - hcloud_load_balancer_target: - type: "ip" - load_balancer: "{{hcloud_load_balancer_name}}" - ip: "{{hcloud_testing_ip}}" - state: present - register: load_balancer_target -- name: verify create ip target - assert: - that: - - load_balancer_target is changed - - load_balancer_target.hcloud_load_balancer_target.type == "ip" - - load_balancer_target.hcloud_load_balancer_target.ip == hcloud_testing_ip - - load_balancer_target.hcloud_load_balancer_target.load_balancer == hcloud_load_balancer_name - -- name: cleanup load_balancer - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: absent - register: result - until: result is not failed - retries: 5 - delay: 2 - -- name: cleanup - hcloud_server: - name: "{{hcloud_server_name}}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/defaults/main.yml deleted file mode 100644 index b7fd86316..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_load_balancer_type_name: "lb11" -hcloud_load_balancer_type_id: 1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/tasks/main.yml deleted file mode 100644 index bcd805a83..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_load_balancer_type_info/tasks/main.yml +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: test gather hcloud Load Balancer type infos - hcloud_load_balancer_type_info: - register: hcloud_load_balancer_types -- name: verify test gather hcloud Load Balancer type infos - assert: - that: - - hcloud_load_balancer_types.hcloud_load_balancer_type_info| list | count >= 1 - -- name: test gather hcloud Load Balancer type infos in check mode - hcloud_load_balancer_type_info: - check_mode: yes - register: hcloud_load_balancer_types - -- name: verify test gather hcloud Load Balancer type infos in check mode - assert: - that: - - hcloud_load_balancer_types.hcloud_load_balancer_type_info| list | count >= 1 - -- name: test gather hcloud Load Balancer type infos with name - hcloud_load_balancer_type_info: - name: "{{hcloud_load_balancer_type_name}}" - register: hcloud_load_balancer_types -- name: verify test gather hcloud Load Balancer type with name - assert: - that: - - hcloud_load_balancer_types.hcloud_load_balancer_type_info|selectattr('name','equalto','{{ hcloud_load_balancer_type_name }}') | list | count == 1 - -- name: test gather hcloud Load Balancer type infos with correct id - hcloud_load_balancer_type_info: - id: "{{hcloud_load_balancer_type_id}}" - register: hcloud_load_balancer_types -- name: verify test gather hcloud Load Balancer type with correct id - assert: - that: - - hcloud_load_balancer_types.hcloud_load_balancer_type_info|selectattr('name','equalto','{{ hcloud_load_balancer_type_name }}') | list | count == 1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/defaults/main.yml deleted file mode 100644 index 0d72a75c2..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_location_name: "fsn1" -hcloud_location_id: 1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/tasks/main.yml deleted file mode 100644 index 99d5880ab..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_location_info/tasks/main.yml +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: test gather hcloud location infos - hcloud_location_info: - register: hcloud_location - -- name: verify test gather hcloud location infos - assert: - that: - - hcloud_location.hcloud_location_info | list | count >= 5 - -- name: test gather hcloud location infos in check mode - hcloud_location_info: - check_mode: yes - register: hcloud_location - -- name: verify test gather hcloud location infos in check mode - assert: - that: - - hcloud_location.hcloud_location_info | list | count >= 5 - -- name: test gather hcloud location infos with correct name - hcloud_location_info: - name: "{{hcloud_location_name}}" - register: hcloud_location -- name: verify test gather hcloud location with correct name - assert: - that: - - hcloud_location.hcloud_location_info|selectattr('name','equalto','{{ hcloud_location_name }}') | list | count == 1 - -- name: test gather hcloud location infos with wrong name - hcloud_location_info: - name: "{{hcloud_location_name}}1" - register: hcloud_location -- name: verify test gather hcloud location with wrong name - assert: - that: - - hcloud_location.hcloud_location_info | list | count == 0 - -- name: test gather hcloud location infos with correct id - hcloud_location_info: - id: "{{hcloud_location_id}}" - register: hcloud_location -- name: verify test gather hcloud location with correct id - assert: - that: - - hcloud_location.hcloud_location_info|selectattr('name','equalto','{{ hcloud_location_name }}') | list | count == 1 - -- name: test gather hcloud location infos with wrong id - hcloud_location_info: - name: "4711" - register: hcloud_location -- name: verify test gather hcloud location with wrong id - assert: - that: - - hcloud_location.hcloud_location_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/aliases deleted file mode 100644 index 4b3a9b36f..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/aliases +++ /dev/null @@ -1,3 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 -disabled diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/defaults/main.yml deleted file mode 100644 index 081eb1472..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_network_name: "{{hcloud_prefix}}-i" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/tasks/main.yml deleted file mode 100644 index 6c40e4e01..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network/tasks/main.yml +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup - hcloud_network: - name: "{{ hcloud_network_name }}" - state: absent - register: result -- name: verify setup - assert: - that: - - result is success - -- name: test missing ip_range parameter on create Network - hcloud_network: - name: "{{hcloud_network_name}}" - register: result - ignore_errors: yes -- name: verify fail missing ip_range parameter on create Network result - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: ip_range"' - -- name: test create Network with check mode - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/16" - register: result - check_mode: yes -- name: verify create Network with check mode result - assert: - that: - - result is changed - -- name: test create Network - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/16" - register: network -- name: verify test create Network result - assert: - that: - - network is changed - - network.hcloud_network.name == "{{hcloud_network_name}}" - - network.hcloud_network.ip_range == "10.0.0.0/16" - -- name: test create Network idempotence - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/16" - register: network -- name: verify test create network - assert: - that: - - network is not changed - -- name: test update Network label - hcloud_network: - name: "{{hcloud_network_name}}" - labels: - key: value - register: network -- name: verify test update Network label - assert: - that: - - network is changed - - network.hcloud_network.labels.key == "value" - -- name: test update Network label idempotency - hcloud_network: - name: "{{hcloud_network_name}}" - labels: - key: value - register: network -- name: verify test update Network label idempotency - assert: - that: - - network is not changed - -- name: test update Network ip range - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/8" - register: network -- name: verify test update Network ip range - assert: - that: - - network is changed - - network.hcloud_network.ip_range == "10.0.0.0/8" - -- name: test update Network ip range idempotency - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/8" - register: network -- name: verify test update Network ip range idempotency - assert: - that: - - network is not changed - -- name: test update Network delete protection - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/8" - delete_protection: true - register: network -- name: verify test update Network delete protection - assert: - that: - - network is changed - - network.hcloud_network.delete_protection is sameas true - -- name: test update Network delete protection idempotency - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/8" - delete_protection: true - register: network -- name: verify test update Network delete protection idempotency - assert: - that: - - network is not changed - - network.hcloud_network.delete_protection is sameas true - -- name: test Network without delete protection set to be idempotent - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/8" - register: network -- name: verify test Network without delete protection set to be idempotent - assert: - that: - - network is not changed - - network.hcloud_network.delete_protection is sameas true - -- name: test delete Network fails if it is protected - hcloud_network: - name: "{{hcloud_network_name}}" - state: absent - ignore_errors: yes - register: result -- name: verify delete Network - assert: - that: - - result is failed - - 'result.msg == "network deletion is protected"' - -- name: test update Network delete protection - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/8" - delete_protection: false - register: network -- name: verify test update Network delete protection - assert: - that: - - network is changed - - network.hcloud_network.delete_protection is sameas false - -- name: test delete Network - hcloud_network: - name: "{{hcloud_network_name}}" - state: absent - register: result -- name: verify delete Network - assert: - that: - - result is success - - -- name: test create Network with delete protection - hcloud_network: - name: "{{hcloud_network_name}}" - ip_range: "10.0.0.0/8" - delete_protection: true - register: network -- name: verify create Network with delete protection - assert: - that: - - network is changed - - network.hcloud_network.delete_protection is sameas true - -- name: test delete Network fails if it is protected - hcloud_network: - name: "{{hcloud_network_name}}" - state: absent - ignore_errors: yes - register: result -- name: verify delete Network - assert: - that: - - result is failed - - 'result.msg == "network deletion is protected"' - -- name: test update Network delete protection - hcloud_network: - name: "{{hcloud_network_name}}" - delete_protection: false - register: network -- name: verify test update Network delete protection - assert: - that: - - network is changed - - network.hcloud_network.delete_protection is sameas false - -- name: test delete Network - hcloud_network: - name: "{{hcloud_network_name}}" - state: absent - register: result -- name: verify delete Network - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/aliases deleted file mode 100644 index 18dc30b6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/defaults/main.yml deleted file mode 100644 index f8a5279fb..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_network_name: "{{hcloud_prefix}}-integration" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/tasks/main.yml deleted file mode 100644 index e7924a8d0..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_network_info/tasks/main.yml +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- - -- name: setup ensure network is absent - hcloud_network: - name: "{{ hcloud_network_name }}" - state: absent - register: result - -- name: create network - hcloud_network: - name: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - labels: - key: value - register: main_network -- name: verify create network - assert: - that: - - main_network is changed - - main_network.hcloud_network.name == "{{ hcloud_network_name }}" - - main_network.hcloud_network.ip_range == "10.0.0.0/16" -- name: create subnetwork - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - type: server - network_zone: eu-central - ip_range: "10.0.1.0/24" - register: main_subnetwork -- name: verify create subnetwork - assert: - that: - - main_subnetwork is changed - - main_subnetwork.hcloud_subnetwork.network == "{{ hcloud_network_name }}" -- name: create route - hcloud_route: - network: "{{ hcloud_network_name }}" - destination: "10.0.3.0/24" - gateway: "10.0.2.1" - register: main_route -- name: verify create route - assert: - that: - - main_route is changed - - main_route.hcloud_route.network == "{{ hcloud_network_name }}" - -- name: test gather hcloud network info in check mode - hcloud_network_info: - check_mode: yes - register: hcloud_network -- name: verify test gather hcloud network info in check mode - assert: - that: - - hcloud_network.hcloud_network_info | selectattr('name','equalto','{{ hcloud_network_name }}') | list | count >= 1 - - -- name: test gather hcloud network info with correct label selector - hcloud_network_info: - label_selector: "key=value" - register: hcloud_network -- name: verify test gather hcloud network with correct label selector - assert: - that: - - hcloud_network.hcloud_network_info | selectattr('name','equalto','{{ hcloud_network_name }}') | list | count >= 1 - -- name: test gather hcloud network info with wrong label selector - hcloud_network_info: - label_selector: "key!=value" - register: hcloud_network -- name: verify test gather hcloud network with wrong label selector - assert: - that: - - hcloud_network.hcloud_network_info | list | count == 0 - -- name: test gather hcloud network info with correct name - hcloud_network_info: - name: "{{hcloud_network_name}}" - register: hcloud_network -- name: verify test gather hcloud network with correct name - assert: - that: - - hcloud_network.hcloud_network_info | selectattr('name','equalto','{{ hcloud_network_name }}') | list | count == 1 - - hcloud_network.hcloud_network_info[0].subnetworks | list | count >= 1 - - hcloud_network.hcloud_network_info[0].routes | list | count >= 1 - -- name: test gather hcloud network info with wrong name - hcloud_network_info: - name: "{{hcloud_network_name}}1" - register: hcloud_network -- name: verify test gather hcloud network with wrong name - assert: - that: - - hcloud_network.hcloud_network_info | list | count == 0 - -- name: test gather hcloud network info with correct id - hcloud_network_info: - id: "{{main_network.hcloud_network.id}}" - register: hcloud_network -- name: verify test gather hcloud network with correct id - assert: - that: - - hcloud_network.hcloud_network_info | selectattr('name','equalto','{{ hcloud_network_name }}') | list | count == 1 - -- name: test gather hcloud network info with wrong id - hcloud_network_info: - name: "4711" - register: hcloud_network -- name: verify test gather hcloud network with wrong id - assert: - that: - - hcloud_network.hcloud_network_info | list | count == 0 - -- name: cleanup - hcloud_network: - name: "{{ hcloud_network_name }}" - state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/defaults/main.yml deleted file mode 100644 index 21ce3429a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_placement_group_name: "{{hcloud_prefix}}-i" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}-i" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/tasks/main.yml deleted file mode 100644 index d79aa0c35..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_placement_group/tasks/main.yml +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup placement group to be absent - hcloud_placement_group: - name: "{{ hcloud_placement_group_name }}" - state: absent - -- name: setup server to be absent - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - -- name: test missing required parameters on create placement group - hcloud_placement_group: - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create placement group - assert: - that: - - result is failed - - 'result.msg == "one of the following is required: id, name"' - -- name: test create placement group with check mode - hcloud_placement_group: - name: "{{ hcloud_placement_group_name }}" - type: spread - register: result - check_mode: yes -- name: test create placement group with check mode - assert: - that: - - result is changed - -- name: test create placement group - hcloud_placement_group: - name: "{{ hcloud_placement_group_name }}" - type: spread - labels: - key: value - my-label: label - register: placement_group -- name: verify create placement group - assert: - that: - - placement_group is changed - - placement_group.hcloud_placement_group.name == "{{ hcloud_placement_group_name }}" - - placement_group.hcloud_placement_group.type == "spread" - - placement_group.hcloud_placement_group.servers | list | count == 0 - -- name: test create placement group idempotence - hcloud_placement_group: - name: "{{ hcloud_placement_group_name }}" - type: spread - labels: - key: value - my-label: label - register: result -- name: verify create placement group idempotence - assert: - that: - - result is not changed - -- name: test create server with placement group - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - placement_group: "{{ hcloud_placement_group_name }}" - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: server -- name: verify create server with placement group - assert: - that: - - server is changed - - server.hcloud_server.placement_group == "{{ hcloud_placement_group_name }}" - -- name: test remove server from placement group - hcloud_server: - name: "{{ hcloud_server_name }}" - placement_group: null - state: present - register: result -- name: verify remove server from placement group - assert: - that: - - result is changed - - result.hcloud_server.placement_group == None - -- name: test add server to placement group - hcloud_server: - name: "{{ hcloud_server_name }}" - placement_group: "{{ hcloud_placement_group_name }}" - force: True - state: present - register: result -- name: verify add server to placement group - assert: - that: - - result is changed - - result.hcloud_server.placement_group == "{{ hcloud_placement_group_name }}" - - result.hcloud_server.status == "running" - -- name: test add server to placement group idempotence - hcloud_server: - name: "{{ hcloud_server_name }}" - placement_group: "{{ hcloud_placement_group_name }}" - force: True - state: present - register: result -- name: verify add server to placement group idempotence - assert: - that: - - result is not changed - - result.hcloud_server.placement_group == "{{ hcloud_placement_group_name }}" - - result.hcloud_server.status == "running" - -- name: test update placement group with check mode - hcloud_placement_group: - id: "{{ placement_group.hcloud_placement_group.id }}" - name: "changed-{{ hcloud_placement_group_name }}" - register: result - check_mode: yes -- name: verify update placement group with check mode - assert: - that: - - result is changed - -- name: test update placement group - hcloud_placement_group: - id: "{{ placement_group.hcloud_placement_group.id }}" - name: "changed-{{ hcloud_placement_group_name }}" - labels: - key: value - register: result -- name: verify update placement group - assert: - that: - - result is changed - - result.hcloud_placement_group.name == "changed-{{ hcloud_placement_group_name }}" - -- name: test update placement group idempotence - hcloud_placement_group: - id: "{{ placement_group.hcloud_placement_group.id }}" - name: "changed-{{ hcloud_placement_group_name }}" - labels: - key: value - register: result -- name: verify update placement group idempotence - assert: - that: - - result is not changed - -- name: absent server - hcloud_server: - id: "{{ server.hcloud_server.id }}" - state: absent - -- name: absent placement group - hcloud_placement_group: - id: "{{ placement_group.hcloud_placement_group.id }}" - state: absent - register: result -- name: verify absent placement group - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/defaults/main.yml deleted file mode 100644 index 98aa28eea..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_primary_ip_name: "{{hcloud_prefix}}-i" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}-fip-t" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/tasks/main.yml deleted file mode 100644 index d4efc606b..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_primary_ip/tasks/main.yml +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: test create Primary IP with check mode - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: "fsn1-dc14" - register: primaryIP - check_mode: yes -- name: verify test create Primary IP with check mode - assert: - that: - - primaryIP is changed - -- name: test create Primary IP - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: "fsn1-dc14" - register: primaryIP -- name: verify test create Primary IP - assert: - that: - - primaryIP is changed - - primaryIP.hcloud_primary_ip.name ==hcloud_primary_ip_name - - primaryIP.hcloud_primary_ip.datacenter == "fsn1-dc14" - -- name: test create Primary IP idempotency - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: "fsn1-dc14" - register: primaryIP -- name: verify test create Primary IP idempotency - assert: - that: - - primaryIP is not changed - -- name: test update Primary IP - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: "fsn1-dc14" - labels: - key: value - register: primaryIP -- name: verify test update Primary IP - assert: - that: - - primaryIP is changed - -- name: test update Primary IP idempotency - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: "fsn1-dc14" - labels: - key: value - register: primaryIP -- name: verify test update Primary IP idempotency - assert: - that: - - primaryIP is not changed - -- name: test update Primary IP with same labels - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: "fsn1-dc14" - labels: - key: value - register: primaryIP -- name: verify test update Primary IP with same labels - assert: - that: - - primaryIP is not changed - -- name: test update Primary IP with other labels - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: "fsn1-dc14" - labels: - key: value - other: label - register: primaryIP -- name: verify test update Primary IP with other labels - assert: - that: - - primaryIP is changed - -- name: test update Primary IP with other labels in different order - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: "fsn1-dc14" - labels: - other: label - key: value - register: primaryIP -- name: verify test update Primary IP with other labels in different order - assert: - that: - - primaryIP is not changed - -- name: test update Primary IP delete protection - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - delete_protection: true - register: primaryIP -- name: verify update Primary IP delete protection - assert: - that: - - primaryIP is changed - - primaryIP.hcloud_primary_ip.delete_protection is sameas true - -- name: test update Primary IP delete protection idempotency - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - delete_protection: true - register: primaryIP -- name: verify update Primary IP delete protection idempotency - assert: - that: - - primaryIP is not changed - - primaryIP.hcloud_primary_ip.delete_protection is sameas true - -- name: test Primary IP without delete protection set to be idempotent - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - register: primaryIP -- name: verify Primary IP without delete protection set to be idempotent - assert: - that: - - primaryIP is not changed - - primaryIP.hcloud_primary_ip.delete_protection is sameas true - -- name: test delete Primary IP fails if it is protected - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - state: "absent" - register: result - ignore_errors: yes -- name: verify test delete primary ip - assert: - that: - - result is failed - - 'result.msg == "Primary IP deletion is protected"' - -- name: test update Primary IP delete protection - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - delete_protection: false - register: primaryIP -- name: verify update Primary IP delete protection - assert: - that: - - primaryIP is changed - - primaryIP.hcloud_primary_ip.delete_protection is sameas false - -- name: test delete primary ip - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - state: "absent" - register: result -- name: verify test delete primary ip - assert: - that: - - result is changed - -- name: test create ipv6 primary ip - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv6 - datacenter: "fsn1-dc14" - state: "present" - register: result -- name: verify test create ipv6 primary ip - assert: - that: - - result is changed - -- name: test delete ipv6 primary ip - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - state: "absent" - register: result -- name: verify test delete ipv6 primary ip - assert: - that: - - result is changed - -- name: test create Primary IP with delete protection - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: fsn1-dc14 - delete_protection: true - register: primaryIP -- name: verify create Primary IP with delete protection - assert: - that: - - primaryIP is changed - - primaryIP.hcloud_primary_ip.delete_protection is sameas true - -- name: test delete Primary IP fails if it is protected - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - state: "absent" - register: result - ignore_errors: yes -- name: verify test delete primary ip - assert: - that: - - result is failed - - 'result.msg == "Primary IP deletion is protected"' - -- name: test update Primary IP delete protection - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - delete_protection: false - register: primaryIP -- name: verify update Primary IP delete protection - assert: - that: - - primaryIP is changed - - primaryIP.hcloud_primary_ip.delete_protection is sameas false - -- name: test delete primary ip - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - state: "absent" - register: result -- name: verify test delete primary ip - assert: - that: - - result is changed diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/aliases deleted file mode 100644 index 18dc30b6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/defaults/main.yml deleted file mode 100644 index 50117a8a5..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/defaults/main.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}" -hcloud_floating_ip_name: "{{hcloud_prefix}}" -hcloud_primary_ip_name: "{{hcloud_prefix}}" -hcloud_load_balancer_name: "{{hcloud_prefix}}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/meta/main.yml deleted file mode 100644 index 67d54d732..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - ansible.netcommon - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/tasks/main.yml deleted file mode 100644 index dddbac0d0..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_rdns/tasks/main.yml +++ /dev/null @@ -1,224 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cx11 - image: "ubuntu-22.04" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: setup -- name: verify setup - assert: - that: - - setup is success - -- name: setup Floating IP - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - type: ipv4 - home_location: "fsn1" - register: floatingIP -- name: verify setup Floating IP - assert: - that: - - floatingIP is success - -- name: setup Load Balancer - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name}}" - load_balancer_type: lb11 - network_zone: eu-central - state: present - register: load_balancer -- name: verify setup - assert: - that: - - load_balancer is success - -- name: setup Primary IP - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - type: ipv4 - datacenter: "fsn1-dc14" - register: primaryIP -- name: verify setup Primary IP - assert: - that: - - primaryIP is success - -- name: test missing required parameter - hcloud_rdns: - state: present - register: result - ignore_errors: yes -- name: verify fail test missing required parameters - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: ip_address"' -- name: test fail on not existing resource - hcloud_rdns: - server: "not-existing" - ip_address: "127.0.0.1" - state: present - register: result - ignore_errors: yes -- name: verify fail on not existing resou - assert: - that: - - result is failed - - 'result.msg == "The selected server does not exist"' -- name: test create rdns - hcloud_rdns: - server: "{{ hcloud_server_name }}" - ip_address: "{{ setup.hcloud_server.ipv6 | ansible.netcommon.ipaddr('next_usable') }}" - dns_ptr: "example.com" - state: present - register: rdns -- name: verify create rdns - assert: - that: - - rdns is changed - - rdns.hcloud_rdns.server == "{{ hcloud_server_name }}" - - rdns.hcloud_rdns.ip_address == "{{ setup.hcloud_server.ipv6 | ansible.netcommon.ipaddr('next_usable') }}" - - rdns.hcloud_rdns.dns_ptr == "example.com" - -- name: test create rdns idempotency - hcloud_rdns: - server: "{{ hcloud_server_name }}" - ip_address: "{{ setup.hcloud_server.ipv6 | ansible.netcommon.ipaddr('next_usable') }}" - dns_ptr: "example.com" - state: present - register: result -- name: verify create rdns idempotency - assert: - that: - - result is not changed - -- name: test absent rdns - hcloud_rdns: - server: "{{ hcloud_server_name }}" - ip_address: "{{ setup.hcloud_server.ipv6 | ansible.netcommon.ipaddr('next_usable') }}" - state: absent - register: result -- name: verify test absent rdns - assert: - that: - - result is changed - -- name: test update rdns - hcloud_rdns: - server: "{{ hcloud_server_name }}" - ip_address: "{{ setup.hcloud_server.ipv4_address }}" - dns_ptr: "example.com" - state: present - register: rdns -- name: verify update rdns - assert: - that: - - rdns is changed - - rdns.hcloud_rdns.server == "{{ hcloud_server_name }}" - - rdns.hcloud_rdns.ip_address == "{{ setup.hcloud_server.ipv4_address }}" - - rdns.hcloud_rdns.dns_ptr == "example.com" - -- name: test reset rdns - hcloud_rdns: - server: "{{ hcloud_server_name }}" - ip_address: "{{ setup.hcloud_server.ipv4_address }}" - state: present - register: rdns -- name: verify reset rdns - assert: - that: - - rdns is changed - - rdns.hcloud_rdns.server == "{{ hcloud_server_name }}" - - rdns.hcloud_rdns.ip_address == "{{ setup.hcloud_server.ipv4_address }}" - - rdns.hcloud_rdns.dns_ptr != "example.com" - -- name: test create rdns with floating IP - hcloud_rdns: - floating_ip: "{{ hcloud_floating_ip_name }}" - ip_address: "{{ floatingIP.hcloud_floating_ip.ip}}" - dns_ptr: "example.com" - state: present - register: rdns -- name: verify create rdns - assert: - that: - - rdns is changed - - rdns.hcloud_rdns.floating_ip == "{{ hcloud_floating_ip_name }}" - - rdns.hcloud_rdns.ip_address == "{{ floatingIP.hcloud_floating_ip.ip}}" - - rdns.hcloud_rdns.dns_ptr == "example.com" - -- name: test create rdns with primary IP - hcloud_rdns: - primary_ip: "{{ hcloud_primary_ip_name }}" - ip_address: "{{ primaryIP.hcloud_primary_ip.ip}}" - dns_ptr: "example.com" - state: present - register: rdns -- name: verify create rdns - assert: - that: - - rdns is changed - - rdns.hcloud_rdns.primary_ip == "{{ hcloud_primary_ip_name }}" - - rdns.hcloud_rdns.ip_address == "{{ primaryIP.hcloud_primary_ip.ip}}" - - rdns.hcloud_rdns.dns_ptr == "example.com" - -- name: test create rdns with load balancer - hcloud_rdns: - load_balancer: "{{ hcloud_load_balancer_name }}" - ip_address: "{{ load_balancer.hcloud_load_balancer.ipv4_address }}" - dns_ptr: "example.com" - state: present - register: rdns -- name: verify create rdns with load balancer - assert: - that: - - rdns is changed - - rdns.hcloud_rdns.load_balancer == "{{ hcloud_load_balancer_name }}" - - rdns.hcloud_rdns.ip_address == "{{ load_balancer.hcloud_load_balancer.ipv4_address }}" - - rdns.hcloud_rdns.dns_ptr == "example.com" - -- name: cleanup - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success - -- name: cleanup - hcloud_floating_ip: - name: "{{ hcloud_floating_ip_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success - -- name: cleanup - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success - -- name: cleanup - hcloud_load_balancer: - name: "{{ hcloud_load_balancer_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/aliases deleted file mode 100644 index 18dc30b6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/defaults/main.yml deleted file mode 100644 index c93c7495e..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_network_name: "{{hcloud_prefix}}-ro" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/meta/main.yml deleted file mode 100644 index 67d54d732..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - ansible.netcommon - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/tasks/main.yml deleted file mode 100644 index 7d816bf5c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_route/tasks/main.yml +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup - hcloud_network: - name: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/8" - state: present - register: network -- name: verify setup - assert: - that: - - network is success - -- name: test missing required parameters on create route - hcloud_route: - state: present - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create route - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: destination, gateway, network"' - -- name: test create route with checkmode - hcloud_route: - network: "{{ hcloud_network_name }}" - destination: "10.100.1.0/24" - gateway: "10.0.1.1" - state: present - register: result - check_mode: yes -- name: verify test create route with checkmode - assert: - that: - - result is changed - -- name: test create route - hcloud_route: - network: "{{ hcloud_network_name }}" - destination: "10.100.1.0/24" - gateway: "10.0.1.1" - state: present - register: route -- name: verify create route - assert: - that: - - route is changed - - route.hcloud_route.network == "{{ hcloud_network_name }}" - - route.hcloud_route.destination == "10.100.1.0/24" - - route.hcloud_route.gateway == "10.0.1.1" - -- name: test create route idempotency - hcloud_route: - network: "{{ hcloud_network_name }}" - destination: "10.100.1.0/24" - gateway: "10.0.1.1" - state: present - register: result -- name: verify create route idempotency - assert: - that: - - result is not changed - -- name: test fail create route with wrong gateway - hcloud_route: - network: "{{ hcloud_network_name }}" - destination: "10.100.1.0/24" - gateway: "10.0.1.2" - state: present - register: route - ignore_errors: yes -- name: verfiy fail create route with wrong gateway - assert: - that: - - route is failed - -- name: test absent route - hcloud_route: - network: "{{ hcloud_network_name }}" - destination: "10.100.1.0/24" - gateway: "10.0.1.1" - state: absent - register: result -- name: verify test absent route - assert: - that: - - result is changed - -- name: cleanup - hcloud_network: - name: "{{hcloud_network_name}}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/aliases deleted file mode 100644 index 18dc30b6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/defaults/main.yml deleted file mode 100644 index 4e1c4dc45..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/defaults/main.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}-i" -hcloud_firewall_name: "{{hcloud_prefix}}-i" -hcloud_primary_ip_name: "{{hcloud_prefix}}-i" -hcloud_network_name: "{{hcloud_prefix}}-i" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/basic.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/basic.yml deleted file mode 100644 index ac609fc6c..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/basic.yml +++ /dev/null @@ -1,615 +0,0 @@ -- name: test create server with check mode - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cx11 - image: ubuntu-20.04 - state: present - register: result - check_mode: yes -- name: test create server server - assert: - that: - - result is changed - -- name: test create server - hcloud_server: - name: "{{ hcloud_server_name}}" - server_type: cx11 - image: ubuntu-20.04 - enable_ipv6: False - state: started - register: main_server -- name: verify create server - assert: - that: - - main_server is changed - - main_server.hcloud_server.name == "{{ hcloud_server_name }}" - - main_server.hcloud_server.server_type == "cx11" - - main_server.hcloud_server.status == "running" - - main_server.root_password != "" - -- name: test create server idempotence - hcloud_server: - name: "{{ hcloud_server_name }}" - state: started - register: result -- name: verify create server idempotence - assert: - that: - - result is not changed - -- name: test stop server with check mode - hcloud_server: - name: "{{ hcloud_server_name }}" - state: stopped - register: result - check_mode: yes -- name: verify stop server with check mode - assert: - that: - - result is changed - - result.hcloud_server.status == "running" - -- name: test stop server - hcloud_server: - name: "{{ hcloud_server_name }}" - state: stopped - register: result -- name: verify stop server - assert: - that: - - result is changed - - result.hcloud_server.status == "off" - -- name: test start server with check mode - hcloud_server: - name: "{{ hcloud_server_name }}" - state: started - register: result - check_mode: true -- name: verify start server with check mode - assert: - that: - - result is changed - -- name: test start server - hcloud_server: - name: "{{ hcloud_server_name }}" - state: started - register: result -- name: verify start server - assert: - that: - - result is changed - - result.hcloud_server.status == "running" - -- name: test start server idempotence - hcloud_server: - name: "{{ hcloud_server_name }}" - state: started - register: result -- name: verify start server idempotence - assert: - that: - - result is not changed - - result.hcloud_server.status == "running" - -- name: test stop server by its id - hcloud_server: - id: "{{ main_server.hcloud_server.id }}" - state: stopped - register: result -- name: verify stop server by its id - assert: - that: - - result is changed - - result.hcloud_server.status == "off" - -- name: test resize server running without force - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: "cx21" - state: present - register: result - check_mode: true -- name: verify test resize server running without force - assert: - that: - - result is changed - - result.hcloud_server.server_type == "cx11" - -- name: test resize server with check mode - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: "cx21" - state: stopped - register: result - check_mode: true -- name: verify resize server with check mode - assert: - that: - - result is changed - -- name: test resize server without disk - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: "cx21" - state: stopped - register: result -- name: verify resize server without disk - assert: - that: - - result is changed - - result.hcloud_server.server_type == "cx21" - -- name: test resize server idempotence - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: "cx21" - state: stopped - register: result -- name: verify resize server idempotence - assert: - that: - - result is not changed - -- name: test resize server to smaller plan - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: "cx11" - state: stopped - register: result -- name: verify resize server to smaller plan - assert: - that: - - result is changed - - result.hcloud_server.server_type == "cx11" - -- name: test resize server with disk - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: "cx21" - upgrade_disk: true - state: stopped - register: result -- name: verify resize server with disk - assert: - that: - - result is changed - - result.hcloud_server.server_type == "cx21" - -- name: test enable backups with check mode - hcloud_server: - name: "{{ hcloud_server_name }}" - backups: true - state: stopped - register: result - check_mode: true -- name: verify enable backups with check mode - assert: - that: - - result is changed - -- name: test enable backups - hcloud_server: - name: "{{ hcloud_server_name }}" - backups: true - state: stopped - register: result -- name: verify enable backups - assert: - that: - - result is changed - - result.hcloud_server.backup_window != "" - -- name: test enable backups idempotence - hcloud_server: - name: "{{ hcloud_server_name }}" - backups: true - state: stopped - register: result -- name: verify enable backups idempotence - assert: - that: - - result is not changed - - result.hcloud_server.backup_window != "" - -- name: test backups are not accidentally disabled - hcloud_server: - name: "{{ hcloud_server_name }}" - # Make sure that backups are not disabled because a partial server object without "backups" was supplied somewhere - # to update some unrelated properties. - # Regression test for https://github.com/ansible-collections/hetzner.hcloud/pull/196 - # backups: true - state: stopped - register: result -- name: verify backups are not accidentally disabled - assert: - that: - - result is not changed - - result.hcloud_server.backup_window != "" - -- name: test rebuild server - hcloud_server: - name: "{{ hcloud_server_name }}" - image: ubuntu-20.04 - state: rebuild - register: result_after_test -- name: verify rebuild server - assert: - that: - - result_after_test is changed - - result.hcloud_server.id == result_after_test.hcloud_server.id - -- name: test rebuild server with check mode - hcloud_server: - name: "{{ hcloud_server_name }}" - image: ubuntu-20.04 - state: rebuild - register: result_after_test - check_mode: true -- name: verify rebuild server with check mode - assert: - that: - - result_after_test is changed - -- name: test update server protection booth protection arguments are required - hcloud_server: - name: "{{ hcloud_server_name }}" - delete_protection: true - state: present - register: result_after_test - ignore_errors: true -- name: verify update server protection booth protection arguments are required - assert: - that: - - result_after_test is failed - - 'result_after_test.msg == "parameters are required together: delete_protection, rebuild_protection"' - -- name: test update server protection fails if they are not the same - hcloud_server: - name: "{{ hcloud_server_name }}" - delete_protection: true - rebuild_protection: false - state: present - register: result_after_test - ignore_errors: true -- name: verify update server protection fails if they are not the same - assert: - that: - - result_after_test is failed - -- name: test update server protection - hcloud_server: - name: "{{ hcloud_server_name }}" - delete_protection: true - rebuild_protection: true - state: present - register: result_after_test - ignore_errors: true -- name: verify update server protection - assert: - that: - - result_after_test is changed - - result_after_test.hcloud_server.delete_protection is sameas true - - result_after_test.hcloud_server.rebuild_protection is sameas true - -- name: test server without protection set to be idempotent - hcloud_server: - name: "{{hcloud_server_name}}" - register: result_after_test -- name: verify test server without protection set to be idempotent - assert: - that: - - result_after_test is not changed - - result_after_test.hcloud_server.delete_protection is sameas true - - result_after_test.hcloud_server.rebuild_protection is sameas true - -- name: test delete server fails if it is protected - hcloud_server: - name: "{{hcloud_server_name}}" - state: absent - ignore_errors: yes - register: result -- name: verify delete server fails if it is protected - assert: - that: - - result is failed - - 'result.msg == "server deletion is protected"' - -- name: test rebuild server fails if it is protected - hcloud_server: - name: "{{hcloud_server_name}}" - image: ubuntu-20.04 - state: rebuild - ignore_errors: yes - register: result -- name: verify rebuild server fails if it is protected - assert: - that: - - result is failed - - 'result.msg == "server rebuild is protected"' - -- name: test remove server protection - hcloud_server: - name: "{{ hcloud_server_name }}" - delete_protection: false - rebuild_protection: false - state: present - register: result_after_test - ignore_errors: true -- name: verify remove server protection - assert: - that: - - result_after_test is changed - - result_after_test.hcloud_server.delete_protection is sameas false - - result_after_test.hcloud_server.rebuild_protection is sameas false - -- name: absent server - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify absent server - assert: - that: - - result is success - -- name: test create server with ssh key - hcloud_server: - name: "{{ hcloud_server_name}}" - server_type: cx11 - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - state: started - register: main_server -- name: verify create server with ssh key - assert: - that: - - main_server is changed - - main_server.hcloud_server.name == "{{ hcloud_server_name }}" - - main_server.hcloud_server.server_type == "cx11" - - main_server.hcloud_server.status == "running" - - main_server.root_password != "" - - -- name: test activate rescue mode with check_mode - hcloud_server: - name: "{{ hcloud_server_name }}" - rescue_mode: "linux64" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: main_server - check_mode: true -- name: verify activate rescue mode - assert: - that: - - main_server is changed - -- name: test activate rescue mode - hcloud_server: - name: "{{ hcloud_server_name }}" - rescue_mode: "linux64" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: main_server -- name: verify activate rescue mode - assert: - that: - - main_server is changed - - main_server.hcloud_server.rescue_enabled is sameas true - -- name: test disable rescue mode - hcloud_server: - name: "{{ hcloud_server_name }}" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: main_server -- name: verify activate rescue mode - assert: - that: - - main_server is changed - - main_server.hcloud_server.rescue_enabled is sameas false - -- name: test activate rescue mode without ssh keys - hcloud_server: - name: "{{ hcloud_server_name }}" - rescue_mode: "linux64" - state: present - register: main_server -- name: verify activate rescue mode without ssh keys - assert: - that: - - main_server is changed - - main_server.hcloud_server.rescue_enabled is sameas true - -- name: absent server - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify absent server - assert: - that: - - result is success - -- name: test create server with rescue_mode - hcloud_server: - name: "{{ hcloud_server_name}}" - server_type: cx11 - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - rescue_mode: "linux64" - state: started - register: main_server -- name: verify create server with rescue_mode - assert: - that: - - main_server is changed - - main_server.hcloud_server.name == "{{ hcloud_server_name }}" - - main_server.hcloud_server.server_type == "cx11" - - main_server.hcloud_server.status == "running" - - main_server.root_password != "" - - main_server.hcloud_server.rescue_enabled is sameas true - - -- name: absent server - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify absent server - assert: - that: - - result is success -- name: test create server with labels - hcloud_server: - name: "{{ hcloud_server_name}}" - server_type: cx11 - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - labels: - key: value - mylabel: "val123" - state: started - register: main_server -- name: verify create server with labels - assert: - that: - - main_server is changed - - main_server.hcloud_server.labels.key == "value" - - main_server.hcloud_server.labels.mylabel == "val123" - -- name: test update server with labels - hcloud_server: - name: "{{ hcloud_server_name}}" - server_type: cx11 - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - labels: - key: other - mylabel: "val123" - state: started - register: main_server -- name: verify update server with labels - assert: - that: - - main_server is changed - - main_server.hcloud_server.labels.key == "other" - - main_server.hcloud_server.labels.mylabel == "val123" - -- name: test update server with labels in other order - hcloud_server: - name: "{{ hcloud_server_name}}" - server_type: cx11 - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - labels: - mylabel: "val123" - key: other - state: started - register: main_server -- name: verify update server with labels in other order - assert: - that: - - main_server is not changed - -- name: cleanup with labels - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success - -- name: test create server with enabled backups - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - backups: true - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: result -- name: verify enable backups - assert: - that: - - result is changed - - result.hcloud_server.backup_window != "" - -- name: cleanup test create server with enabled backups - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success - -- name: test create server with protection - hcloud_server: - name: "{{ hcloud_server_name }}" - delete_protection: true - rebuild_protection: true - server_type: cpx11 - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: result_after_test - ignore_errors: true -- name: verify create server with protection - assert: - that: - - result_after_test is changed - - result_after_test.hcloud_server.delete_protection is sameas true - - result_after_test.hcloud_server.rebuild_protection is sameas true - -- name: test delete server fails if it is protected - hcloud_server: - name: "{{hcloud_server_name}}" - state: absent - ignore_errors: yes - register: result -- name: verify delete server fails if it is protected - assert: - that: - - result is failed - - 'result.msg == "server deletion is protected"' - -- name: remove protection from server - hcloud_server: - name: "{{ hcloud_server_name }}" - delete_protection: false - rebuild_protection: false - state: present - register: result_after_test - ignore_errors: true -- name: verify update server protection - assert: - that: - - result_after_test is changed - - result_after_test.hcloud_server.delete_protection is sameas false - - result_after_test.hcloud_server.rebuild_protection is sameas false - -- name: cleanup - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/firewalls.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/firewalls.yml deleted file mode 100644 index 18fa89e25..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/firewalls.yml +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: test add not existing firewall should fail - hcloud_server: - name: "{{ hcloud_server_name }}" - firewalls: - - not-existing - state: present - ignore_errors: yes - register: result -- name: verify add not existing firewall should fail - assert: - that: - - result is failed - - 'result.msg == "firewall not-existing was not found"' -- name: setup create firewalls - hcloud_firewall: - name: "{{ item }}" - rules: - - direction: in - protocol: icmp - source_ips: - - 0.0.0.0/0 - - ::/0 - with_items: - - "{{ hcloud_firewall_name }}" - - "{{ hcloud_firewall_name }}2" - -- name: test create server with firewalls - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - firewalls: - - "{{ hcloud_firewall_name }}" - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: result -- name: verify test create server with firewalls - assert: - that: - - result is changed - -- name: test create server with firewalls idempotence - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - firewalls: - - "{{ hcloud_firewall_name }}" - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: result -- name: verify test create server with firewalls idempotence - assert: - that: - - result is not changed - -- name: test update server with firewalls - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - firewalls: - - "{{ hcloud_firewall_name }}2" - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: result -- name: verify test update server with firewalls - assert: - that: - - result is changed - -- name: test update server with firewalls idempotence - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - firewalls: - - "{{ hcloud_firewall_name }}2" - image: "ubuntu-20.04" - ssh_keys: - - ci@ansible.hetzner.cloud - state: present - register: result -- name: verify test update server with firewalls idempotence - assert: - that: - - result is not changed - -- name: cleanup server with firewalls - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - -- name: cleanup test create firewall - hcloud_firewall: - name: "{{ item }}" - state: absent - with_items: - - "{{ hcloud_firewall_name }}" - - "{{ hcloud_firewall_name }}2" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/main.yml deleted file mode 100644 index 209d9bd48..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/main.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright: (c) 2022, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -#- ansible.builtin.include_tasks: validation.yml -- ansible.builtin.include_tasks: basic.yml -#- ansible.builtin.include_tasks: firewalls.yml -- ansible.builtin.include_tasks: primary_ips.yml -- ansible.builtin.include_tasks: private_network_only.yml diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/primary_ips.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/primary_ips.yml deleted file mode 100644 index 000c294de..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/primary_ips.yml +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright: (c) 2022, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup create primary ipv4 - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}v4" - type: ipv4 - datacenter: "fsn1-dc14" - register: primaryIPv4 - -- name: setup create second primary ipv4 - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}v42" - type: ipv4 - datacenter: "fsn1-dc14" - register: secondPrimaryIPv4 - -- name: setup create primary ipv6 - hcloud_primary_ip: - name: "{{ hcloud_primary_ip_name }}v6" - type: ipv6 - datacenter: "fsn1-dc14" - register: primaryIPv6 - -- name: test create server with primary ips - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - datacenter: "fsn1-dc14" - image: "ubuntu-20.04" - ipv4: "{{primaryIPv4.hcloud_primary_ip.id}}" - ipv6: "{{primaryIPv6.hcloud_primary_ip.id}}" - ssh_keys: - - ci@ansible.hetzner.cloud - state: stopped - register: result -- name: verify test create server with primary ips - assert: - that: - - result is changed - -- name: test update server with primary ips - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - datacenter: "fsn1-dc14" - image: "ubuntu-20.04" - ipv4: "{{secondPrimaryIPv4.hcloud_primary_ip.id}}" - ipv6: "" - enable_ipv6: no - ssh_keys: - - ci@ansible.hetzner.cloud - state: stopped - register: result -- name: verify test create server with primary ips - assert: - that: - - result is changed - -- name: cleanup server with primary ips - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - -- name: cleanup test create primary ips - hcloud_primary_ip: - name: "{{ hcloud_server_name }}v4" - state: absent -- name: cleanup test create primary ips - hcloud_primary_ip: - name: "{{ hcloud_server_name }}v42" - state: absent - until: result is not failed - retries: 5 - delay: 2 -- name: cleanup test create primary ips - hcloud_primary_ip: - name: "{{ hcloud_server_name }}v6" - state: absent - until: result is not failed - retries: 5 - delay: 2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/private_network_only.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/private_network_only.yml deleted file mode 100644 index a56832873..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/private_network_only.yml +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright: (c) 2022, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup network 1 to be absent - hcloud_network: - name: "{{ hcloud_network_name }}-1" - state: absent - -- name: setup network 2 to be absent - hcloud_network: - name: "{{ hcloud_network_name }}-2" - state: absent - -- name: setup server to be absent - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - -- name: setup create network - hcloud_network: - name: "{{ hcloud_network_name }}-1" - ip_range: 192.168.0.0/23 - register: primaryNetwork - -- name: setup create network subnet 1 - hcloud_subnetwork: - network: "{{ hcloud_network_name }}-1" - ip_range: 192.168.0.0/24 - network_zone: eu-central - type: cloud - state: present - -- name: setup create network subnet 2 - hcloud_subnetwork: - network: "{{ hcloud_network_name }}-1" - ip_range: 192.168.1.0/24 - network_zone: eu-central - type: cloud - state: present - -- name: setup create secondary network - hcloud_network: - name: "{{ hcloud_network_name }}-2" - ip_range: 192.168.2.0/23 - register: secondaryNetwork - -- name: setup create secondary network subnet 1 - hcloud_subnetwork: - network: "{{ hcloud_network_name }}-2" - ip_range: 192.168.2.0/24 - network_zone: eu-central - type: cloud - state: present - -- name: setup create secondary network subnet 2 - hcloud_subnetwork: - network: "{{ hcloud_network_name }}-2" - ip_range: 192.168.3.0/24 - network_zone: eu-central - type: cloud - state: present - -- name: test create server with primary network and no internet - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - datacenter: "fsn1-dc14" - image: "ubuntu-20.04" - enable_ipv4: no - enable_ipv6: no - private_networks: - - "{{ primaryNetwork.hcloud_network.name }}" - ssh_keys: - - ci@ansible.hetzner.cloud - state: stopped - register: result -- name: verify test create server with primary network - assert: - that: - - result is changed - -- name: test update server by adding secondary network - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - datacenter: "fsn1-dc14" - image: "ubuntu-20.04" - enable_ipv4: no - enable_ipv6: no - private_networks: - - "{{ primaryNetwork.hcloud_network.name }}" - - "{{ secondaryNetwork.hcloud_network.id }}" - ssh_keys: - - ci@ansible.hetzner.cloud - state: stopped - register: result -- name: verify test update server by adding secondary network - assert: - that: - - result is changed - -- name: test update server idem - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cpx11 - datacenter: "fsn1-dc14" - image: "ubuntu-20.04" - enable_ipv4: no - enable_ipv6: no - private_networks: - - "{{ primaryNetwork.hcloud_network.name }}" - - "{{ secondaryNetwork.hcloud_network.id }}" - ssh_keys: - - ci@ansible.hetzner.cloud - state: stopped - register: result -- name: verify test update server idem - assert: - that: - - result is not changed - -- name: cleanup server - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent -- name: cleanup networks - hcloud_network: - name: "{{ item }}" - state: absent - with_items: - - "{{ primaryNetwork.hcloud_network.name }}" - - "{{ secondaryNetwork.hcloud_network.id }}" - until: result is not failed - retries: 5 - delay: 2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/validation.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/validation.yml deleted file mode 100644 index f507e87cf..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server/tasks/validation.yml +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify setup - assert: - that: - - result is success - -- name: test missing required parameters on create server - hcloud_server: - name: "{{ hcloud_server_name }}" - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create server - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: server_type, image"' - -- name: test create server with not existing server type - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: not-existing-server-type - image: ubuntu-20.04 - state: present - register: result - ignore_errors: yes -- name: verify fail test create server with not existing server type - assert: - that: - - result is failed - - 'result.msg == "server_type not-existing-server-type was not found"' - -- name: test create server with not existing image - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cx11 - image: my-not-existing-image-20.04 - state: present - register: result - ignore_errors: yes -- name: verify fail test create server with not existing image - assert: - that: - - result is failed - - 'result.msg == "Image my-not-existing-image-20.04 was not found"' diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/defaults/main.yml deleted file mode 100644 index aa27d6452..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}-ii" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/tasks/main.yml deleted file mode 100644 index b425b4127..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_info/tasks/main.yml +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup ensure server is absent - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result - -- name: create server - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cx11 - image: ubuntu-22.04 - state: started - labels: - key: value - register: main_server -- name: verify create server - assert: - that: - - main_server is changed - - main_server.hcloud_server.name == "{{ hcloud_server_name }}" - - main_server.hcloud_server.server_type == "cx11" - - main_server.hcloud_server.status == "running" - - main_server.root_password != "" - - -- name: test gather hcloud server infos in check mode - hcloud_server_info: - register: server - check_mode: yes - -- name: verify test gather hcloud server infos in check mode - assert: - that: - - server.hcloud_server_info|selectattr('name','equalto','{{ hcloud_server_name }}') | list | count == 1 - - -- name: test gather hcloud server infos with correct label selector - hcloud_server_info: - label_selector: "key=value" - register: server -- name: verify test gather hcloud server infos with correct label selector - assert: - that: - - server.hcloud_server_info|selectattr('name','equalto','{{ hcloud_server_name }}') | list | count == 1 - -- name: test gather hcloud server infos with wrong label selector - hcloud_server_info: - label_selector: "key!=value" - register: server -- name: verify test gather hcloud server infos with wrong label selector - assert: - that: - - server.hcloud_server_info | list | count == 0 - -- name: test gather hcloud server infos with correct name - hcloud_server_info: - name: "{{hcloud_server_name}}" - register: server -- name: verify test gather hcloud server infos with correct name - assert: - that: - - server.hcloud_server_info|selectattr('name','equalto','{{ hcloud_server_name }}') | list | count == 1 - -- name: test gather hcloud server infos with wrong name - hcloud_server_info: - name: "{{hcloud_server_name}}1" - register: server -- name: verify test gather hcloud server infos with wrong name - assert: - that: - - server.hcloud_server_info | list | count == 0 - -- name: test gather hcloud server infos with correct id - hcloud_server_info: - id: "{{main_server.hcloud_server.id}}" - register: server -- name: verify test gather hcloud server infos with correct id - assert: - that: - - server.hcloud_server_info|selectattr('name','equalto','{{ hcloud_server_name }}') | list | count == 1 - -- name: test gather hcloud server infos with wrong id - hcloud_server_info: - name: "4711" - register: server -- name: verify test gather hcloud server infos with wrong id - assert: - that: - - server.hcloud_server_info | list | count == 0 - -- name: cleanup - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - -- name: create server without ips - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cx11 - image: ubuntu-22.04 - state: stopped - labels: - key: value - enable_ipv4: no - enable_ipv6: no - register: main_server -- name: verify create server - assert: - that: - - main_server is changed - - main_server.hcloud_server.name == "{{ hcloud_server_name }}" - - main_server.hcloud_server.server_type == "cx11" - - main_server.root_password != "" -- name: test gather hcloud server infos with correct id - hcloud_server_info: - id: "{{main_server.hcloud_server.id}}" - register: server -- name: verify test gather hcloud server infos with correct id - assert: - that: - - server.hcloud_server_info|selectattr('name','equalto','{{ hcloud_server_name }}') | list | count == 1 -- name: cleanup - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/aliases deleted file mode 100644 index 7f17468b0..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/aliases +++ /dev/null @@ -1,3 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 -disabled diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/defaults/main.yml deleted file mode 100644 index 2e020c495..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_network_name: "{{hcloud_prefix}}-sn" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}-sn" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/tasks/main.yml deleted file mode 100644 index 754018a66..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_network/tasks/main.yml +++ /dev/null @@ -1,222 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup network - hcloud_network: - name: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/8" - state: present - register: network -- name: verify setup network - assert: - that: - - network is success - -- name: setup subnetwork - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "server" - network_zone: "eu-central" - state: present - register: subnetwork -- name: verify subnetwork - assert: - that: - - subnetwork is success - -- name: setup server - hcloud_server: - name: "{{hcloud_server_name}}" - server_type: cx11 - image: ubuntu-18.04 - state: started - location: "fsn1" - register: server -- name: verify setup server - assert: - that: - - server is success - -- name: test missing required parameters on create server network - hcloud_server_network: - state: present - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create server network - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: network, server"' - -- name: test create server network with checkmode - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - state: present - register: result - check_mode: yes -- name: verify test create server network with checkmode - assert: - that: - - result is changed - -- name: test create server network - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - state: present - register: serverNetwork -- name: verify create server network - assert: - that: - - serverNetwork is changed - - serverNetwork.hcloud_server_network.network == hcloud_network_name - - serverNetwork.hcloud_server_network.server == hcloud_server_name - -- name: test create server network idempotency - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - state: present - register: serverNetwork -- name: verify create server network idempotency - assert: - that: - - serverNetwork is not changed - -- name: test absent server network - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - state: absent - register: result -- name: verify test absent server network - assert: - that: - - result is changed - -- name: test create server network with specified ip - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - ip: "10.0.0.2" - state: present - register: serverNetwork -- name: verify create server network with specified ip - assert: - that: - - serverNetwork is changed - - serverNetwork.hcloud_server_network.network == hcloud_network_name - - serverNetwork.hcloud_server_network.server == hcloud_server_name - - serverNetwork.hcloud_server_network.ip == "10.0.0.2" - -- name: cleanup create server network with specified ip - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - state: absent - register: result -- name: verify cleanup create server network with specified ip - assert: - that: - - result is changed - -- name: test create server network with alias ips - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - ip: "10.0.0.2" - alias_ips: - - "10.0.1.2" - - "10.0.2.3" - state: present - register: serverNetwork -- name: verify create server network with alias ips - assert: - that: - - serverNetwork is changed - - serverNetwork.hcloud_server_network.network == hcloud_network_name - - serverNetwork.hcloud_server_network.server == hcloud_server_name - - serverNetwork.hcloud_server_network.ip == "10.0.0.2" - - 'serverNetwork.hcloud_server_network.alias_ips[0] == "10.0.2.3"' - - 'serverNetwork.hcloud_server_network.alias_ips[1] == "10.0.1.2"' - -- name: test update server network with alias ips - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - ip: "10.0.0.2" - alias_ips: - - "10.0.2.3" - - "10.0.3.1" - state: present - register: serverNetwork -- name: verify create server network with alias ips - assert: - that: - - serverNetwork is changed - - serverNetwork.hcloud_server_network.network == hcloud_network_name - - serverNetwork.hcloud_server_network.server == hcloud_server_name - - serverNetwork.hcloud_server_network.ip == "10.0.0.2" - - 'serverNetwork.hcloud_server_network.alias_ips[0] == "10.0.2.3"' - - 'serverNetwork.hcloud_server_network.alias_ips[1] == "10.0.3.1"' - -- name: test update server network with alias ips idempotency - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - ip: "10.0.0.2" - alias_ips: - - "10.0.2.3" - - "10.0.3.1" - state: present - register: serverNetwork -- name: verify create server network with alias ips idempotency - assert: - that: - - serverNetwork is not changed - -- name: cleanup create server network with alias ips - hcloud_server_network: - network: "{{ hcloud_network_name }}" - server: "{{hcloud_server_name}}" - state: absent - register: result -- name: verify cleanup create server network with alias ips - assert: - that: - - result is changed - -- name: cleanup server - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify cleanup server - assert: - that: - - result is success - -- name: cleanup subnetwork - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "server" - network_zone: "eu-central" - state: absent - register: result -- name: verify cleanup subnetwork - assert: - that: - - result is changed - -- name: cleanup - hcloud_network: - name: "{{hcloud_network_name}}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/defaults/main.yml deleted file mode 100644 index 05502aa91..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_server_type_name: "cx11" -hcloud_server_type_id: 1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/tasks/main.yml deleted file mode 100644 index 3c1fce8c0..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_server_type_info/tasks/main.yml +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: test gather hcloud server type infos - hcloud_server_type_info: - register: hcloud_server_types -- name: verify test gather hcloud server type infos - assert: - that: - - hcloud_server_types.hcloud_server_type_info| list | count > 2 - -- name: test gather hcloud server type infos in check mode - hcloud_server_type_info: - check_mode: yes - register: hcloud_server_types - -- name: verify test gather hcloud server type infos in check mode - assert: - that: - - hcloud_server_types.hcloud_server_type_info| list | count > 2 - -- name: test gather hcloud server type infos with name - hcloud_server_type_info: - name: "{{hcloud_server_type_name}}" - register: hcloud_server_types -- name: verify test gather hcloud server type with name - assert: - that: - - hcloud_server_types.hcloud_server_type_info|selectattr('name','equalto','{{ hcloud_server_type_name }}') | list | count == 1 - -- name: test gather hcloud server type infos with correct id - hcloud_server_type_info: - id: "{{hcloud_server_type_id}}" - register: hcloud_server_types -- name: verify test gather hcloud server type with correct id - assert: - that: - - hcloud_server_types.hcloud_server_type_info|selectattr('name','equalto','{{ hcloud_server_type_name }}') | list | count == 1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/defaults/main.yml deleted file mode 100644 index cee1d4691..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/defaults/main.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}" -hcloud_ssh_key_name: "{{hcloud_prefix}}" -hcloud_ssh_key_public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDnaTPfKaX1QKcRLOfr34buVLh5FhJAThI9NYB0xNdXsMd4Y0zLyyCQzHbx4eWCVZxym/s6csWSeLaAhO1GOHeAw3hQFMqf1oTBx6Y8g0pKpeotKPa/PDSUzdZF9Lc+DadtpQd8kFVHAu1Kd3zoEUnk1u6kP7I4qu4Z/6F9qBDF+M3aobiPVxdS7GwaVRW3nZu+FcQDLiBiNOjuRDyjHcDfEUkoh2SOu25RrFtGPzFu5mGmBJwotKpWAocLGfHzyn/fAHxgw3jKZVH/t+XWQFnl82Ie8yE3Z1EZ7oDkNRqFQT9AdXEQOLycTTYTQMJZpgeFTv3sAo6lPRCusiFmmLcf ci@ansible.hetzner.cloud" -hcloud_ssh_key_fingerprint: "56:89:c4:d6:a7:4a:79:82:f4:c2:58:9c:e1:d2:2d:4e" - -hcloud_doubled_ssh_key_public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1AiuN3UMQKzOs4tNudmlDSkSebC+savc6CivoHGflUKeli7nKb5pKgGiqH+zeWZc+8+flUa2BxsJWmi7d1nGJ++W4BnzmqW78ApelpJnGtuX8IKNcq/trhVTQyaShPiLluoBs7bXyyZpAKNGkk3jHrgwwYD/QQDN0CJnQUM18fjH5CUes2vmaG/kkhn7ctuVHDOvDcEy8KdBX3fYyrtXw5GgWDC5borG6yT1f3E9AXfRPL9OQjMTeC+G4FHscJAZjNnYav+jLrQLdV1xJ0JgbjRyBgTAfBszx9oKIjzCUPvpj4npju0WFGu10pIh0w7bluMoVn1tS6Y3gxE/Cepwt ci@ansible.hetzner.cloud" -hcloud_doubled_ssh_key_fingerprint: "f9:33:40:ff:77:f3:3e:85:f2:9e:8f:98:71:fd:a0:58" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/meta/main.yml deleted file mode 100644 index 5dcc0725d..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/meta/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -dependencies: - - setup_sshkey -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/tasks/main.yml deleted file mode 100644 index 9208e143d..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key/tasks/main.yml +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: test missing required parameters on create ssh_key - hcloud_ssh_key: - name: "{{ hcloud_ssh_key_name }}" - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create ssh_key - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: public_key"' - -- name: test create ssh key with check mode - hcloud_ssh_key: - name: "{{ hcloud_ssh_key_name }}" - public_key: "{{ key_material }}" - register: result - check_mode: yes -- name: test create ssh key with check mode - assert: - that: - - result is changed - -- name: test create ssh key - hcloud_ssh_key: - name: "{{ hcloud_ssh_key_name }}" - public_key: "{{ key_material }}" - labels: - key: value - my-label: label - register: sshKey -- name: verify create ssh key - assert: - that: - - sshKey is changed - - sshKey.hcloud_ssh_key.name == "{{ hcloud_ssh_key_name }}" - - sshKey.hcloud_ssh_key.public_key == "{{ key_material }}" - - sshKey.hcloud_ssh_key.labels.key == "value" - -- name: test create ssh key idempotence - hcloud_ssh_key: - name: "{{ hcloud_ssh_key_name }}" - public_key: "{{ key_material }}" - register: result -- name: verify create ssh key idempotence - assert: - that: - - result is not changed - -- name: test update ssh key with check mode - hcloud_ssh_key: - id: "{{ sshKey.hcloud_ssh_key.id }}" - name: "changed-{{ hcloud_ssh_key_name }}" - register: result - check_mode: yes -- name: test create ssh key with check mode - assert: - that: - - result is changed - -- name: test update ssh key - hcloud_ssh_key: - id: "{{ sshKey.hcloud_ssh_key.id }}" - name: "changed-{{ hcloud_ssh_key_name }}" - labels: - key: value - register: result -- name: test update ssh key - assert: - that: - - result is changed - - result.hcloud_ssh_key.name == "changed-{{ hcloud_ssh_key_name }}" - -- name: test update ssh key with same labels - hcloud_ssh_key: - id: "{{ sshKey.hcloud_ssh_key.id }}" - name: "changed-{{ hcloud_ssh_key_name }}" - labels: - key: value - register: result -- name: test update ssh key with same labels - assert: - that: - - result is not changed - -- name: test update ssh key with other labels - hcloud_ssh_key: - id: "{{ sshKey.hcloud_ssh_key.id }}" - name: "changed-{{ hcloud_ssh_key_name }}" - labels: - key: value - test: "val123" - register: result -- name: test update ssh key with other labels - assert: - that: - - result is changed - -- name: test rename ssh key - hcloud_ssh_key: - id: "{{ sshKey.hcloud_ssh_key.id }}" - name: "{{ hcloud_ssh_key_name }}" - register: result -- name: test rename ssh key - assert: - that: - - result is changed - - result.hcloud_ssh_key.name == "{{ hcloud_ssh_key_name }}" - -- name: test create server with ssh key - hcloud_server: - name: "{{ hcloud_server_name }}" - server_type: cx11 - image: "ubuntu-20.04" - ssh_keys: - - "{{ hcloud_ssh_key_name }}" - state: started - register: main_server -- name: verify create server with ssh key - assert: - that: - - main_server is changed - -- name: absent ssh key - hcloud_ssh_key: - id: "{{ sshKey.hcloud_ssh_key.id }}" - state: absent - register: result -- name: verify absent sshkey - assert: - that: - - result is success - -- name: test fail cleanly on double created ssh key - hcloud_ssh_key: - name: "{{ hcloud_ssh_key_name }}othername" - public_key: "{{ hcloud_doubled_ssh_key_public_key }}" - register: result - ignore_errors: yes -- name: verify failed correctly - assert: - that: - - result is failed - - 'result.msg == "SSH key with the same fingerprint already exists"' - -- name: cleanup - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/defaults/main.yml deleted file mode 100644 index 15188e181..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_ssh_key_name: "{{hcloud_prefix}}-f" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/meta/main.yml deleted file mode 100644 index 5dcc0725d..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/meta/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -dependencies: - - setup_sshkey -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/tasks/main.yml deleted file mode 100644 index 87cbd2626..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_ssh_key_info/tasks/main.yml +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- - -- name: setup ensure ssh key is absent - hcloud_ssh_key: - name: "{{ hcloud_ssh_key_name }}" - state: absent - register: result - -- name: setup test ssh_key - hcloud_ssh_key: - name: "{{hcloud_ssh_key_name}}" - public_key: "{{ key_material }}" - labels: - key: value - register: result -- name: verify create test ssh_key - assert: - that: - - result is changed - - result.hcloud_ssh_key.public_key == "{{ key_material }}" - -- name: test gather hcloud ssh key infos in check mode - hcloud_ssh_key_info: - register: hcloud_ssh_key - check_mode: yes -- name: verify test gather hcloud ssh key infos in check mode - assert: - that: - - hcloud_ssh_key.hcloud_ssh_key_info| list | count >= 1 - -- name: test gather hcloud ssh key infos - hcloud_ssh_key_info: - register: hcloud_ssh_key - check_mode: yes -- name: verify test gather hcloud ssh key infos - assert: - that: - - hcloud_ssh_key.hcloud_ssh_key_info| list | count >= 1 - -- name: test gather hcloud ssh key infos with correct label selector - hcloud_ssh_key_info: - label_selector: "key=value" - register: hcloud_ssh_key -- name: verify test gather hcloud ssh key infos with correct label selector - assert: - that: - - hcloud_ssh_key.hcloud_ssh_key_info|selectattr('name','equalto','{{ hcloud_ssh_key_name }}') | list | count == 1 - -- name: test gather hcloud ssh key infos with wrong label selector - hcloud_ssh_key_info: - label_selector: "key!=value" - register: hcloud_ssh_key -- name: verify test gather hcloud ssh key infos with wrong label selector - assert: - that: - - hcloud_ssh_key.hcloud_ssh_key_info | list | count == 0 - -- name: cleanup - hcloud_ssh_key: - name: "{{hcloud_ssh_key_name}}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/aliases deleted file mode 100644 index af1d98c3d..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group3 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/defaults/main.yml deleted file mode 100644 index 79f0d8783..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_network_name: "{{hcloud_prefix}}-s" -hetzner_vswitch_id: 15311 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/tasks/main.yml deleted file mode 100644 index 0453f9d13..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_subnetwork/tasks/main.yml +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup - hcloud_network: - name: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/8" - state: present - register: network -- name: verify setup - assert: - that: - - network is success - -- name: test missing required parameters on create route - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - state: present - register: result - ignore_errors: yes -- name: verify fail test missing required parameters on create route - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: ip_range, network_zone, type"' - -- name: test create subnetwork with checkmode - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "server" - network_zone: "eu-central" - state: present - register: result - check_mode: yes -- name: verify test create subnetwork with checkmode - assert: - that: - - result is changed - -- name: test create subnetwork - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "cloud" - network_zone: "eu-central" - state: present - register: subnet -- name: verify create subnetwork - assert: - that: - - subnet is changed - - subnet.hcloud_subnetwork.network == "{{ hcloud_network_name }}" - - subnet.hcloud_subnetwork.ip_range == "10.0.0.0/16" - - subnet.hcloud_subnetwork.type == "cloud" - - subnet.hcloud_subnetwork.network_zone == "eu-central" - -- name: test create subnetwork idempotency - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "cloud" - network_zone: "eu-central" - state: present - register: result -- name: verify create subnetwork idempotency - assert: - that: - - result is not changed - -- name: test absent subnetwork - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "cloud" - network_zone: "eu-central" - state: absent - register: result -- name: verify test absent subnetwork - assert: - that: - - result is changed - -- name: test vswitch subnetwork - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "vswitch" - network_zone: "eu-central" - vswitch_id: "{{ hetzner_vswitch_id }}" - state: present - register: subnet -- name: verify test vswitch subnetwork - assert: - that: - - subnet is changed - - subnet.hcloud_subnetwork.network == "{{ hcloud_network_name }}" - - subnet.hcloud_subnetwork.ip_range == "10.0.0.0/16" - - subnet.hcloud_subnetwork.type == "vswitch" - - subnet.hcloud_subnetwork.network_zone == "eu-central" - - subnet.hcloud_subnetwork.vswitch_id == hetzner_vswitch_id - -- name: test absent subnetwork - hcloud_subnetwork: - network: "{{ hcloud_network_name }}" - ip_range: "10.0.0.0/16" - type: "vswitch" - network_zone: "eu-central" - vswitch_id: "{{ hetzner_vswitch_id }}" - state: absent - register: subnet -- name: verify test absent subnetwork - assert: - that: - - result is changed - -- name: cleanup - hcloud_network: - name: "{{hcloud_network_name}}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/defaults/main.yml deleted file mode 100644 index ff16ce28f..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_volume_name: "{{ hcloud_prefix | truncate(60, True, '', 0) }}-i" -hcloud_server_name: "{{ hcloud_prefix | truncate(45, True, '', 0) }}-vs" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/tasks/main.yml deleted file mode 100644 index f763a52f2..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume/tasks/main.yml +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup server - hcloud_server: - name: "{{hcloud_server_name}}" - server_type: cx11 - image: ubuntu-18.04 - state: started - location: "fsn1" - register: vol_server -- name: verify setup server - assert: - that: - - vol_server is changed - -- name: test missing size parameter on create Volume - hcloud_volume: - name: "{{hcloud_volume_name}}" - server: "{{hcloud_server_name}}" - register: result - ignore_errors: yes -- name: verify fail test missing size parameter on create Volume - assert: - that: - - result is failed - - 'result.msg == "missing required arguments: size"' - -- name: test create Volume with check mode - hcloud_volume: - name: "{{hcloud_volume_name}}" - size: 10 - location: "fsn1" - register: result - check_mode: yes -- name: verify create Volume with check mode result - assert: - that: - - result is changed - -- name: test create Volume - hcloud_volume: - name: "{{hcloud_volume_name}}" - size: 10 - location: "fsn1" - register: volume -- name: verify test create Volume - assert: - that: - - volume is changed - - volume.hcloud_volume.name == "{{hcloud_volume_name}}" - - volume.hcloud_volume.location == "fsn1" - - volume.hcloud_volume.size == 10 - - volume.hcloud_volume.server != "{{hcloud_server_name}}" - - volume.hcloud_volume.linux_device is defined - -- name: test create Volume idempotence - hcloud_volume: - name: "{{hcloud_volume_name}}" - size: 10 - location: "fsn1" - register: volume -- name: verify test create Volume - assert: - that: - - volume is not changed - -- name: test attach Volume with checkmode - hcloud_volume: - name: "{{hcloud_volume_name}}" - server: "{{hcloud_server_name}}" - check_mode: yes - register: volume -- name: verify test attach Volume with checkmode - assert: - that: - - volume is changed - - volume.hcloud_volume.server != "{{hcloud_server_name}}" - -- name: test attach Volume - hcloud_volume: - name: "{{hcloud_volume_name}}" - server: "{{hcloud_server_name}}" - register: volume -- name: verify attach volume - assert: - that: - - volume is changed - - volume.hcloud_volume.server == "{{hcloud_server_name}}" - -- name: test attach Volume idempotence - hcloud_volume: - name: "{{hcloud_volume_name}}" - server: "{{hcloud_server_name}}" - register: volume -- name: verify attach Volume idempotence - assert: - that: - - volume is not changed - - volume.hcloud_volume.server == "{{hcloud_server_name}}" - -- name: test detach Volume with checkmode - hcloud_volume: - name: "{{hcloud_volume_name}}" - check_mode: yes - register: volume -- name: verify detach Volume with checkmode - assert: - that: - - volume is changed - - volume.hcloud_volume.server == "{{hcloud_server_name}}" - -- name: test detach Volume - hcloud_volume: - name: "{{hcloud_volume_name}}" - register: volume -- name: verify detach volume - assert: - that: - - volume is changed - - volume.hcloud_volume.location == "fsn1" - - volume.hcloud_volume.server != "{{hcloud_server_name}}" - -- name: test update Volume label - hcloud_volume: - name: "{{hcloud_volume_name}}" - labels: - key: value - register: volume -- name: verify test update Volume label - assert: - that: - - volume is changed - - volume.hcloud_volume.labels.key == "value" - -- name: test update Volume label with the same label - hcloud_volume: - name: "{{hcloud_volume_name}}" - labels: - key: value - register: volume -- name: verify test update Volume lable with the same label - assert: - that: - - volume is not changed - -- name: test increase Volume size - hcloud_volume: - name: "{{hcloud_volume_name}}" - size: 11 - register: volume -- name: verify test increase Volume size - assert: - that: - - volume is changed - - volume.hcloud_volume.size == 11 - -- name: test decreace Volume size - hcloud_volume: - name: "{{hcloud_volume_name}}" - size: 10 - register: volume -- name: verify test decreace Volume size - assert: - that: - - volume is not changed - - volume.hcloud_volume.size == 11 - -- name: test update Volume delete protection - hcloud_volume: - name: "{{hcloud_volume_name}}" - delete_protection: true - register: volume -- name: verify test update Volume delete protection - assert: - that: - - volume is changed - - volume.hcloud_volume.delete_protection is sameas true - -- name: test update Volume delete protection idempotency - hcloud_volume: - name: "{{hcloud_volume_name}}" - delete_protection: true - register: volume -- name: verify test update Volume delete protection idempotency - assert: - that: - - volume is not changed - - volume.hcloud_volume.delete_protection is sameas true - -- name: test Volume without delete protection set to be idempotent - hcloud_volume: - name: "{{hcloud_volume_name}}" - register: volume -- name: verify test Volume without delete protection set to be idempotent - assert: - that: - - volume is not changed - - volume.hcloud_volume.delete_protection is sameas true - -- name: test delete Volume fails if it is protected - hcloud_volume: - name: "{{hcloud_volume_name}}" - state: absent - ignore_errors: yes - register: result -- name: verify delete Volume fails if it is protected - assert: - that: - - result is failed - - 'result.msg == "volume deletion is protected"' - -- name: test update Volume delete protection - hcloud_volume: - name: "{{hcloud_volume_name}}" - delete_protection: false - register: volume -- name: verify test update Volume delete protection - assert: - that: - - volume is changed - - volume.hcloud_volume.delete_protection is sameas false - -- name: test delete Volume - hcloud_volume: - name: "{{hcloud_volume_name}}" - state: absent - register: result -- name: verify delete Volume - assert: - that: - - result is success - - -- name: test create Volume with delete protection - hcloud_volume: - name: "{{hcloud_volume_name}}" - size: 10 - location: "fsn1" - delete_protection: true - register: volume -- name: verify create Volume with delete protection - assert: - that: - - volume is changed - - volume.hcloud_volume.delete_protection is sameas true - -- name: test delete Volume fails if it is protected - hcloud_volume: - name: "{{hcloud_volume_name}}" - state: absent - ignore_errors: yes - register: result -- name: verify delete Volume fails if it is protected - assert: - that: - - result is failed - - 'result.msg == "volume deletion is protected"' - -- name: test update Volume delete protection - hcloud_volume: - name: "{{hcloud_volume_name}}" - delete_protection: false - register: volume -- name: verify test update Volume delete protection - assert: - that: - - volume is changed - - volume.hcloud_volume.delete_protection is sameas false - -- name: test delete Volume - hcloud_volume: - name: "{{hcloud_volume_name}}" - state: absent - register: result -- name: verify delete Volume - assert: - that: - - result is success - -- name: cleanup - hcloud_server: - name: "{{ hcloud_server_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/aliases deleted file mode 100644 index 55ec821a4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/hcloud -shippable/hcloud/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/defaults/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/defaults/main.yml deleted file mode 100644 index 52c468eeb..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -hcloud_prefix: "tests" -hcloud_volume_name: "{{ hcloud_prefix | truncate(60, True, '', 0) }}-i" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/meta/main.yml deleted file mode 100644 index 407c9018a..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - community.general.ipfilter - - hetzner.cloud diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/tasks/main.yml deleted file mode 100644 index ecea7ad31..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/hcloud_volume_info/tasks/main.yml +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -- name: setup ensure volume is absent - hcloud_volume: - name: "{{ hcloud_volume_name }}" - state: absent - register: result - -- name: setup volume - hcloud_volume: - name: "{{hcloud_volume_name}}" - size: 10 - location: "fsn1" - labels: - key: value - register: main_volume -- name: verify setup volume - assert: - that: - - main_volume is changed - -- name: test gather hcloud volume infos in check mode - hcloud_volume_info: - register: hcloud_volume - check_mode: yes - -- name: verify test gather hcloud volume infos in check mode - vars: - volume: "{{ hcloud_volume.hcloud_volume_info|selectattr('name','equalto',hcloud_volume_name) | first }}" - assert: - that: - - hcloud_volume.hcloud_volume_info|selectattr('name','equalto','{{ hcloud_volume_name }}') | list | count == 1 - - volume.name == "{{hcloud_volume_name}}" - - volume.location == "fsn1" - - volume.size == 10 - - volume.linux_device is defined - -- name: test gather hcloud volume infos with correct label selector - hcloud_volume_info: - label_selector: "key=value" - register: hcloud_volume -- name: verify test gather hcloud volume infos with correct label selector - assert: - that: - - hcloud_volume.hcloud_volume_info|selectattr('name','equalto','{{ hcloud_volume_name }}') | list | count == 1 - -- name: test gather hcloud volume infos with wrong label selector - hcloud_volume_info: - label_selector: "key!=value" - register: hcloud_volume -- name: verify test gather hcloud volume infos with wrong label selector - assert: - that: - - hcloud_volume.hcloud_volume_info | list | count == 0 - -- name: test gather hcloud volume infos with correct name - hcloud_volume_info: - name: "{{hcloud_volume_name}}" - register: hcloud_volume -- name: verify test gather hcloud volume infos with correct name - assert: - that: - - hcloud_volume.hcloud_volume_info|selectattr('name','equalto','{{ hcloud_volume_name }}') | list | count == 1 - -- name: test gather hcloud volume infos with wrong name - hcloud_volume_info: - name: "{{hcloud_volume_name}}1" - register: hcloud_volume -- name: verify test gather hcloud volume infos with wrong name - assert: - that: - - hcloud_volume.hcloud_volume_info | list | count == 0 - -- name: test gather hcloud volume facts with correct id - hcloud_volume_info: - id: "{{main_volume.hcloud_volume.id}}" - register: hcloud_volume -- name: verify test gather hcloud volume with correct id - assert: - that: - - hcloud_volume.hcloud_volume_info|selectattr('name','equalto','{{ hcloud_volume_name }}') | list | count == 1 - -- name: test gather hcloud volume infos with wrong id - hcloud_volume_info: - name: "4711" - register: hcloud_volume -- name: verify test gather hcloud volume infos with wrong id - assert: - that: - - hcloud_volume.hcloud_volume_info | list | count == 0 - -- name: cleanup - hcloud_volume: - name: "{{ hcloud_volume_name }}" - state: absent - register: result -- name: verify cleanup - assert: - that: - - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/defaults/main/main.yml new file mode 100644 index 000000000..d7a350577 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/defaults/main/main.yml @@ -0,0 +1,6 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_server_name: "{{ hcloud_ns }}" +hcloud_snapshot_name: "{{ hcloud_ns }}" +hcloud_image_name: ubuntu-22.04 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/meta/main.yml new file mode 100644 index 000000000..49f5c0c4b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_hcloud_cli diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/cleanup.yml new file mode 100644 index 000000000..a99c36549 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/prepare.yml new file mode 100644 index 000000000..9c65ae86d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/prepare.yml @@ -0,0 +1,23 @@ +--- +- name: Create test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: ubuntu-22.04 + state: stopped + register: test_server + +- name: Create test_snapshot + ansible.builtin.script: + cmd: > + {{ hcloud_cli_path }} server create-image + --type snapshot + --description "{{ hcloud_snapshot_name }}" + --label key=value + "{{ test_server.hcloud_server.id }}" + | awk '{print $2}' + register: test_snapshot + +- name: Set test_snapshot_id + ansible.builtin.set_fact: + test_snapshot_id: "{{ test_snapshot.stdout_lines[0] }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/test.yml new file mode 100644 index 000000000..270285e9c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/image_info/tasks/test.yml @@ -0,0 +1,104 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_image_info + hetzner.hcloud.image_info: + register: result +- name: Verify hcloud_image_info + ansible.builtin.assert: + that: + - result.hcloud_image_info | list | count >= 3 + +- name: Gather hcloud_image_info with architecture + hetzner.hcloud.image_info: + architecture: arm + register: result +- name: Verify hcloud_image_info with architecture + ansible.builtin.assert: + that: + - result.hcloud_image_info | selectattr('architecture', 'equalto', 'x86') | list | count == 0 + - result.hcloud_image_info | selectattr('architecture', 'equalto', 'arm') | list | count > 2 + +- name: Gather hcloud_image_info in check mode + hetzner.hcloud.image_info: + check_mode: true + register: result +- name: Verify hcloud_image_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_image_info | list | count >= 3 + +- name: Gather hcloud_image_info with correct id + hetzner.hcloud.image_info: + id: "{{ test_snapshot_id }}" + type: snapshot + register: result +- name: Verify hcloud_image_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_image_info | list | count == 1 + +- name: Gather hcloud_image_info with wrong id + hetzner.hcloud.image_info: + id: "{{ test_snapshot_id }}4321" + type: snapshot + ignore_errors: true + register: result +- name: Verify hcloud_image_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_image_info with correct name + hetzner.hcloud.image_info: + name: "{{ hcloud_image_name }}" + register: result +- name: Verify hcloud_image_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_image_info | list | count == 1 + - result.hcloud_image_info[0].architecture == "x86" + +- name: Gather hcloud_image_info with wrong name + hetzner.hcloud.image_info: + name: "{{ hcloud_image_name }}-invalid" + register: result +- name: Verify hcloud_image_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_image_info | list | count == 0 + +- name: Gather hcloud_image_info with correct name and architecture + hetzner.hcloud.image_info: + name: "{{ hcloud_image_name }}" + architecture: arm + register: result +- name: Verify hcloud_image_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_image_info | list | count == 1 + - result.hcloud_image_info[0].architecture == "arm" + +- name: Gather hcloud_image_info with correct label selector + hetzner.hcloud.image_info: + label_selector: "key=value" + type: snapshot + register: result +- name: Verify hcloud_image_info with correct label selector + ansible.builtin.assert: + that: + # Snapshot names are stored in the description field + - > + result.hcloud_image_info + | selectattr('description', 'equalto', hcloud_snapshot_name) + | list | count == 1 + +- name: Gather hcloud_image_info with wrong label selector + hetzner.hcloud.image_info: + label_selector: "key!=value" + type: snapshot + register: result +- name: Verify hcloud_image_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_image_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/aliases new file mode 100644 index 000000000..a6a90a6bf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group3 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/defaults/main/main.yml new file mode 100644 index 000000000..9e3c30d01 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/defaults/main/main.yml @@ -0,0 +1,7 @@ +# Copyright: (c) 2023, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_iso_id: 551 +hcloud_iso_name: systemrescuecd-x86-5.2.2.iso +hcloud_iso_type: public +hcloud_iso_architecture: x86 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/tasks/test.yml new file mode 100644 index 000000000..5a4a42da1 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/iso_info/tasks/test.yml @@ -0,0 +1,73 @@ +# Copyright: (c) 2023, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_iso_info + hetzner.hcloud.iso_info: + register: result +- name: Verify hcloud_iso_info + ansible.builtin.assert: + that: + - result.hcloud_iso_info | list | count >= 1 + +- name: Gather hcloud_iso_info in check mode + hetzner.hcloud.iso_info: + check_mode: true + register: result +- name: Verify hcloud_iso_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_iso_info | list | count >= 1 + +- name: Gather hcloud_iso_info with correct id + hetzner.hcloud.iso_info: + id: "{{ hcloud_iso_id }}" + register: result +- name: Verify hcloud_iso_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_iso_info | list | count == 1 + - result.hcloud_iso_info[0].id == hcloud_iso_id | string + - result.hcloud_iso_info[0].name == hcloud_iso_name + - result.hcloud_iso_info[0].architecture == hcloud_iso_architecture + - result.hcloud_iso_info[0].type == hcloud_iso_type + - result.hcloud_iso_info[0].deprecated is none + - result.hcloud_iso_info[0].deprecation is none + +- name: Gather hcloud_iso_info with wrong id + hetzner.hcloud.iso_info: + id: "{{ hcloud_iso_id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_iso_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_iso_info with correct name + hetzner.hcloud.iso_info: + name: "{{ hcloud_iso_name }}" + register: result +- name: Verify hcloud_iso_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_iso_info | list | count == 1 + +- name: Gather hcloud_iso_info with wrong name + hetzner.hcloud.iso_info: + name: "{{ hcloud_iso_name }}-invalid" + register: result +- name: Verify hcloud_iso_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_iso_info | list | count == 0 + +- name: Gather hcloud_iso_info with architecture + hetzner.hcloud.iso_info: + architecture: arm + register: result +- name: Verify hcloud_iso_info with architecture + ansible.builtin.assert: + that: + - result.hcloud_iso_info | list | count > 2 + - result.hcloud_iso_info | selectattr('architecture', 'equalto', 'x86') | list | count == 0 + - result.hcloud_iso_info | selectattr('architecture', 'equalto', 'arm') | list | count > 2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/aliases new file mode 100644 index 000000000..62828d1e9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/defaults/main/main.yml new file mode 100644 index 000000000..458094e95 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_load_balancer_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/tasks/cleanup.yml new file mode 100644 index 000000000..b9ad6d254 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_load_balancer + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/tasks/test.yml new file mode 100644 index 000000000..343f15672 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer/tasks/test.yml @@ -0,0 +1,145 @@ +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Test missing required parameters + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + state: present + ignore_errors: true + register: result +- name: Verify missing required parameters + ansible.builtin.assert: + that: + - result is failed + - 'result.msg == "missing required arguments: load_balancer_type"' + +- name: Test create with check mode + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + load_balancer_type: lb11 + network_zone: eu-central + state: present + check_mode: true + register: result +- name: Verify create with check mode + ansible.builtin.assert: + that: + - result is changed + +- name: Test create + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + load_balancer_type: lb11 + network_zone: eu-central + labels: + key: value + label: value123 + state: present + register: result +- name: Verify create + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_load_balancer.name == hcloud_load_balancer_name + - result.hcloud_load_balancer.load_balancer_type == "lb11" + - result.hcloud_load_balancer.algorithm == "round_robin" + - result.hcloud_load_balancer.labels.key == "value" + - result.hcloud_load_balancer.labels.label == "value123" + - result.hcloud_load_balancer.delete_protection == false + +- name: Test create idempotency + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + load_balancer_type: lb11 + network_zone: eu-central + state: present + register: result +- name: Verify create idempotency + ansible.builtin.assert: + that: + - result is not changed + +- name: Test update algorithm + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + algorithm: least_connections + state: present + register: result +- name: Verify update algorithm + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_load_balancer.algorithm == "least_connections" + +- name: Test update load_balancer_type + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + load_balancer_type: lb21 + state: present + register: result +- name: Verify update load_balancer_type + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_load_balancer.load_balancer_type == "lb21" + +- name: Test update labels + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + labels: + key: changed + label: changed123 + state: present + register: result +- name: Verify update load_balancer_type + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_load_balancer.labels.key == "changed" + - result.hcloud_load_balancer.labels.label == "changed123" + +- name: Test update delete_protection + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + delete_protection: true + state: present + register: result +- name: Verify update load_balancer_type + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_load_balancer.delete_protection == true + +- name: Test delete with protection + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + state: absent + register: result + ignore_errors: true +- name: Verify delete with protection + ansible.builtin.assert: + that: + - result is failed + - 'result.msg == "load balancer deletion is protected"' + +- name: Test update delete_protection + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + delete_protection: false + state: present + register: result +- name: Verify update delete_protection + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_load_balancer.delete_protection == false + +- name: Test delete + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + state: absent + register: result +- name: Verify delete + ansible.builtin.assert: + that: + - result is changed diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/aliases new file mode 100644 index 000000000..62828d1e9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/defaults/main/main.yml new file mode 100644 index 000000000..63c342baf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_load_balancer_name: "{{ hcloud_ns }}" +hcloud_server_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/meta/main.yml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/meta/main.yml diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/cleanup.yml new file mode 100644 index 000000000..b99d2cd58 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/cleanup.yml @@ -0,0 +1,10 @@ +--- +- name: Cleanup test_load_balancer + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + state: absent + +- name: Cleanup test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/prepare.yml new file mode 100644 index 000000000..bfad755df --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/prepare.yml @@ -0,0 +1,33 @@ +--- +- name: Create test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: ubuntu-22.04 + state: started + register: test_server + +- name: Create test_load_balancer + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + load_balancer_type: lb11 + network_zone: eu-central + labels: + key: value + register: test_load_balancer + +- name: Create test_load_balancer_target + hetzner.hcloud.load_balancer_target: + type: "server" + load_balancer: "{{ hcloud_load_balancer_name }}" + server: "{{ hcloud_server_name }}" + state: present + register: test_load_balancer_target + +- name: Create test_load_balancer_service + hetzner.hcloud.load_balancer_service: + load_balancer: "{{ hcloud_load_balancer_name }}" + protocol: "http" + listen_port: 80 + state: present + register: test_load_balancer_service diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/test.yml new file mode 100644 index 000000000..36ad43b40 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_info/tasks/test.yml @@ -0,0 +1,86 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_load_balancer_info + hetzner.hcloud.load_balancer_info: + register: result +- name: Verify hcloud_load_balancer_info + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_info | list | count >= 1 + +- name: Gather hcloud_load_balancer_info in check mode + hetzner.hcloud.load_balancer_info: + check_mode: true + register: result +- name: Verify hcloud_load_balancer_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_info | list | count >= 1 + +- name: Gather hcloud_load_balancer_info with correct id + hetzner.hcloud.load_balancer_info: + id: "{{ test_load_balancer.hcloud_load_balancer.id }}" + register: result +- name: Verify hcloud_load_balancer_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_info | list | count == 1 + - result.hcloud_load_balancer_info[0].targets | list | count == 1 + - result.hcloud_load_balancer_info[0].targets | selectattr('type', 'equalto', 'server') | list | count == 1 + - result.hcloud_load_balancer_info[0].targets | selectattr('server', 'equalto', hcloud_server_name) | list | count == 1 + - result.hcloud_load_balancer_info[0].targets[0].health_status[0].listen_port == 80 + - result.hcloud_load_balancer_info[0].targets[0].health_status[0].status in ['healthy', 'unhealthy', 'unknown'] + - result.hcloud_load_balancer_info[0].services | list | count == 1 + - result.hcloud_load_balancer_info[0].services | selectattr('protocol', 'equalto', 'http') | list | count == 1 + - result.hcloud_load_balancer_info[0].services | selectattr('listen_port', 'equalto', 80) | list | count == 1 + - result.hcloud_load_balancer_info[0].services | selectattr('destination_port', 'equalto', 80) | list | count == 1 + +- name: Gather hcloud_load_balancer_info with wrong id + hetzner.hcloud.load_balancer_info: + id: "{{ test_load_balancer.hcloud_load_balancer.id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_load_balancer_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_load_balancer_info with correct name + hetzner.hcloud.load_balancer_info: + name: "{{ hcloud_load_balancer_name }}" + register: result +- name: Verify hcloud_load_balancer_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_info | list | count == 1 + +- name: Gather hcloud_load_balancer_info with wrong name + hetzner.hcloud.load_balancer_info: + name: "{{ hcloud_load_balancer_name }}-invalid" + register: result +- name: Verify hcloud_load_balancer_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_info | list | count == 0 + +- name: Gather hcloud_load_balancer_info with correct label selector + hetzner.hcloud.load_balancer_info: + label_selector: "key=value" + register: result +- name: Verify hcloud_load_balancer_info with correct label selector + ansible.builtin.assert: + that: + - > + result.hcloud_load_balancer_info + | selectattr('name', 'equalto', hcloud_load_balancer_name) + | list | count == 1 + +- name: Gather hcloud_load_balancer_info with wrong label selector + hetzner.hcloud.load_balancer_info: + label_selector: "key!=value" + register: result +- name: Verify hcloud_load_balancer_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/aliases new file mode 100644 index 000000000..62828d1e9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/defaults/main/main.yml new file mode 100644 index 000000000..d7a9d233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_network_name: "{{ hcloud_ns }}" +hcloud_load_balancer_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/tasks/test.yml new file mode 100644 index 000000000..06e469619 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_network/tasks/test.yml @@ -0,0 +1,181 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup network + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}" + ip_range: "10.0.0.0/8" + state: present + register: network +- name: verify setup network + assert: + that: + - network is success + +- name: setup subnetwork + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + ip_range: "10.0.0.0/16" + type: "cloud" + network_zone: "eu-central" + state: present + register: subnetwork +- name: verify subnetwork + assert: + that: + - subnetwork is success + +- name: setup load_balancer + hetzner.hcloud.load_balancer: + name: "{{hcloud_load_balancer_name}}" + load_balancer_type: lb11 + state: present + location: "fsn1" + register: load_balancer +- name: verify setup load_balancer + assert: + that: + - load_balancer is success + +- name: test missing required parameters on create load_balancer network + hetzner.hcloud.load_balancer_network: + state: present + register: result + ignore_errors: true +- name: verify fail test missing required parameters on create load_balancer network + assert: + that: + - result is failed + - '"missing required arguments:" in result.msg' + +- name: test fail load balancer does not exist + hetzner.hcloud.load_balancer_network: + network: "{{ hcloud_network_name }}" + load_balancer: does-not-exist + state: present + register: result + ignore_errors: true +- name: verify test fail load_balancer does not exist + assert: + that: + - result is failed + - "result.msg == 'resource (load_balancer) does not exist: does-not-exist'" + +- name: test fail network does not exist + hetzner.hcloud.load_balancer_network: + network: does-not-exist + load_balancer: "{{ hcloud_load_balancer_name }}" + state: present + register: result + ignore_errors: true +- name: verify test fail network does not exist + assert: + that: + - result is failed + - "result.msg == 'resource (network) does not exist: does-not-exist'" + +- name: test create load_balancer network with checkmode + hetzner.hcloud.load_balancer_network: + network: "{{ hcloud_network_name }}" + load_balancer: "{{hcloud_load_balancer_name}}" + state: present + register: result + check_mode: true +- name: verify test create load_balancer network with checkmode + assert: + that: + - result is changed + +- name: test create load_balancer network + hetzner.hcloud.load_balancer_network: + network: "{{ hcloud_network_name }}" + load_balancer: "{{hcloud_load_balancer_name}}" + state: present + register: load_balancerNetwork +- name: verify create load_balancer network + assert: + that: + - load_balancerNetwork is changed + - load_balancerNetwork.hcloud_load_balancer_network.network == hcloud_network_name + - load_balancerNetwork.hcloud_load_balancer_network.load_balancer == hcloud_load_balancer_name + +- name: test create load_balancer network idempotency + hetzner.hcloud.load_balancer_network: + network: "{{ hcloud_network_name }}" + load_balancer: "{{hcloud_load_balancer_name}}" + state: present + register: load_balancerNetwork +- name: verify create load_balancer network idempotency + assert: + that: + - load_balancerNetwork is not changed + +- name: test absent load_balancer network + hetzner.hcloud.load_balancer_network: + network: "{{ hcloud_network_name }}" + load_balancer: "{{hcloud_load_balancer_name}}" + state: absent + register: result +- name: verify test absent load_balancer network + assert: + that: + - result is changed + +- name: test create load_balancer network with specified ip + hetzner.hcloud.load_balancer_network: + network: "{{ hcloud_network_name }}" + load_balancer: "{{hcloud_load_balancer_name}}" + ip: "10.0.0.2" + state: present + register: load_balancerNetwork +- name: verify create load_balancer network with specified ip + assert: + that: + - load_balancerNetwork is changed + - load_balancerNetwork.hcloud_load_balancer_network.network == hcloud_network_name + - load_balancerNetwork.hcloud_load_balancer_network.load_balancer == hcloud_load_balancer_name + - load_balancerNetwork.hcloud_load_balancer_network.ip == "10.0.0.2" + +- name: cleanup create load_balancer network with specified ip + hetzner.hcloud.load_balancer_network: + network: "{{ hcloud_network_name }}" + load_balancer: "{{hcloud_load_balancer_name}}" + state: absent + register: result +- name: verify cleanup create load_balancer network with specified ip + assert: + that: + - result is changed + +- name: cleanup load_balancer + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + state: absent + register: result +- name: verify cleanup load_balancer + assert: + that: + - result is success + +- name: cleanup subnetwork + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + ip_range: "10.0.0.0/16" + type: "cloud" + network_zone: "eu-central" + state: absent + register: result +- name: verify cleanup subnetwork + assert: + that: + - result is changed + +- name: cleanup + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + state: absent + register: result +- name: verify cleanup + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/aliases new file mode 100644 index 000000000..62828d1e9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/defaults/main/main.yml new file mode 100644 index 000000000..f6a05e8d1 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_load_balancer_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/tasks/test.yml new file mode 100644 index 000000000..173709a67 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_service/tasks/test.yml @@ -0,0 +1,126 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup load_balancer + hetzner.hcloud.load_balancer: + name: "{{hcloud_load_balancer_name}}" + load_balancer_type: lb11 + state: present + location: "fsn1" + register: load_balancer +- name: verify setup load_balancer + assert: + that: + - load_balancer is success + +- name: test fail load balancer does not exist + hetzner.hcloud.load_balancer_service: + load_balancer: does-not-exist + protocol: http + listen_port: 80 + state: present + register: result + ignore_errors: true +- name: verify test fail load_balancer does not exist + assert: + that: + - result is failed + - "result.msg == 'resource (load_balancer) does not exist: does-not-exist'" + +- name: test create load_balancer service with checkmode + hetzner.hcloud.load_balancer_service: + load_balancer: "{{hcloud_load_balancer_name}}" + protocol: "http" + listen_port: 80 + state: present + register: result + check_mode: true +- name: verify test create load_balancer service with checkmode + assert: + that: + - result is changed + +- name: test create load_balancer service + hetzner.hcloud.load_balancer_service: + load_balancer: "{{hcloud_load_balancer_name}}" + protocol: "http" + listen_port: 80 + state: present + register: load_balancer_service +- name: verify create load_balancer service + assert: + that: + - load_balancer_service is changed + - load_balancer_service.hcloud_load_balancer_service.protocol == "http" + - load_balancer_service.hcloud_load_balancer_service.listen_port == 80 + - load_balancer_service.hcloud_load_balancer_service.destination_port == 80 + - load_balancer_service.hcloud_load_balancer_service.proxyprotocol is sameas false + +- name: test create load_balancer service idempotency + hetzner.hcloud.load_balancer_service: + load_balancer: "{{hcloud_load_balancer_name}}" + protocol: "http" + listen_port: 80 + state: present + register: load_balancer_service +- name: verify create load_balancer service idempotency + assert: + that: + - load_balancer_service is not changed + +- name: test update load_balancer service + hetzner.hcloud.load_balancer_service: + load_balancer: "{{hcloud_load_balancer_name}}" + protocol: "tcp" + listen_port: 80 + state: present + register: load_balancer_service +- name: verify create load_balancer service + assert: + that: + - load_balancer_service is changed + - load_balancer_service.hcloud_load_balancer_service.protocol == "tcp" + - load_balancer_service.hcloud_load_balancer_service.listen_port == 80 + - load_balancer_service.hcloud_load_balancer_service.destination_port == 80 + - load_balancer_service.hcloud_load_balancer_service.proxyprotocol is sameas false + +- name: test absent load_balancer service + hetzner.hcloud.load_balancer_service: + load_balancer: "{{hcloud_load_balancer_name}}" + protocol: "http" + listen_port: 80 + state: absent + register: result +- name: verify test absent load_balancer service + assert: + that: + - result is changed + +- name: test create load_balancer service with http + hetzner.hcloud.load_balancer_service: + load_balancer: "{{hcloud_load_balancer_name}}" + protocol: "http" + listen_port: 80 + http: + cookie_name: "Test" + sticky_sessions: true + state: present + register: load_balancer_service +- name: verify create load_balancer service + assert: + that: + - load_balancer_service is changed + - load_balancer_service.hcloud_load_balancer_service.protocol == "http" + - load_balancer_service.hcloud_load_balancer_service.listen_port == 80 + - load_balancer_service.hcloud_load_balancer_service.destination_port == 80 + - load_balancer_service.hcloud_load_balancer_service.proxyprotocol is sameas false + +- name: cleanup load_balancer + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + state: absent + register: result +- name: verify cleanup load_balancer + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/aliases new file mode 100644 index 000000000..62828d1e9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/defaults/main/main.yml new file mode 100644 index 000000000..2d60910e9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/defaults/main/main.yml @@ -0,0 +1,7 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_server_name: "{{ hcloud_ns }}" +hcloud_load_balancer_name: "{{ hcloud_ns }}" + +hetzner_server_ip: 142.132.203.104 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/tasks/test.yml new file mode 100644 index 000000000..19fd0e9af --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_target/tasks/test.yml @@ -0,0 +1,154 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup server + hetzner.hcloud.server: + name: "{{hcloud_server_name}}" + server_type: cx11 + image: ubuntu-22.04 + state: started + location: "fsn1" + register: server +- name: verify setup server + assert: + that: + - server is success + +- name: setup load_balancer + hetzner.hcloud.load_balancer: + name: "{{hcloud_load_balancer_name}}" + load_balancer_type: lb11 + state: present + location: "fsn1" + register: load_balancer +- name: verify setup load_balancer + assert: + that: + - load_balancer is success + +- name: test fail load balancer does not exist + hetzner.hcloud.load_balancer_target: + type: server + load_balancer: does-not-exist + server: "{{ hcloud_server_name }}" + register: result + ignore_errors: true +- name: verify test fail load_balancer does not exist + assert: + that: + - result is failed + - "result.msg == 'resource (load_balancer) does not exist: does-not-exist'" + +- name: test fail server does not exist + hetzner.hcloud.load_balancer_target: + type: server + load_balancer: "{{ hcloud_load_balancer_name }}" + server: does-not-exist + register: result + ignore_errors: true +- name: verify test fail server does not exist + assert: + that: + - result is failed + - "result.msg == 'resource (server) does not exist: does-not-exist'" + +- name: test create load_balancer target with checkmode + hetzner.hcloud.load_balancer_target: + type: "server" + load_balancer: "{{hcloud_load_balancer_name}}" + server: "{{hcloud_server_name}}" + state: present + register: result + check_mode: true +- name: verify test create load_balancer target with checkmode + assert: + that: + - result is changed + +- name: test create load_balancer target + hetzner.hcloud.load_balancer_target: + type: "server" + load_balancer: "{{hcloud_load_balancer_name}}" + server: "{{hcloud_server_name}}" + state: present + register: load_balancer_target +- name: verify create load_balancer target + assert: + that: + - load_balancer_target is changed + - load_balancer_target.hcloud_load_balancer_target.type == "server" + - load_balancer_target.hcloud_load_balancer_target.server == hcloud_server_name + - load_balancer_target.hcloud_load_balancer_target.load_balancer == hcloud_load_balancer_name + +- name: test create load_balancer target idempotency + hetzner.hcloud.load_balancer_target: + type: "server" + load_balancer: "{{hcloud_load_balancer_name}}" + server: "{{hcloud_server_name}}" + state: present + register: load_balancer_target +- name: verify create load_balancer target idempotency + assert: + that: + - load_balancer_target is not changed + +- name: test absent load_balancer target + hetzner.hcloud.load_balancer_target: + type: "server" + load_balancer: "{{hcloud_load_balancer_name}}" + server: "{{hcloud_server_name}}" + state: absent + register: result +- name: verify test absent load_balancer target + assert: + that: + - result is changed + +- name: test create label_selector target + hetzner.hcloud.load_balancer_target: + type: "label_selector" + load_balancer: "{{hcloud_load_balancer_name}}" + label_selector: "application=backend" + state: present + register: load_balancer_target +- name: verify create label_selector target + assert: + that: + - load_balancer_target is changed + - load_balancer_target.hcloud_load_balancer_target.type == "label_selector" + - load_balancer_target.hcloud_load_balancer_target.label_selector == "application=backend" + - load_balancer_target.hcloud_load_balancer_target.load_balancer == hcloud_load_balancer_name + +- name: test create ip target + hetzner.hcloud.load_balancer_target: + type: "ip" + load_balancer: "{{ hcloud_load_balancer_name }}" + ip: "{{ hetzner_server_ip }}" + state: present + register: load_balancer_target +- name: verify create ip target + assert: + that: + - load_balancer_target is changed + - load_balancer_target.hcloud_load_balancer_target.type == "ip" + - load_balancer_target.hcloud_load_balancer_target.ip == hetzner_server_ip + - load_balancer_target.hcloud_load_balancer_target.load_balancer == hcloud_load_balancer_name + +- name: cleanup load_balancer + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + state: absent + register: result + until: result is not failed + retries: 5 + delay: 2 + +- name: cleanup + hetzner.hcloud.server: + name: "{{hcloud_server_name}}" + state: absent + register: result +- name: verify cleanup + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/defaults/main/main.yml new file mode 100644 index 000000000..473035974 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_load_balancer_type_id: 1 +hcloud_load_balancer_type_name: lb11 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/tasks/test.yml new file mode 100644 index 000000000..980b8a274 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/load_balancer_type_info/tasks/test.yml @@ -0,0 +1,56 @@ +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_load_balancer_type_info + hetzner.hcloud.load_balancer_type_info: + register: result +- name: Verify hcloud_load_balancer_type_info + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_type_info | list | count >= 1 + +- name: Gather hcloud_load_balancer_type_info in check mode + hetzner.hcloud.load_balancer_type_info: + check_mode: true + register: result +- name: Verify hcloud_load_balancer_type_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_type_info | list | count >= 1 + +- name: Gather hcloud_load_balancer_type_info with correct id + hetzner.hcloud.load_balancer_type_info: + id: "{{ hcloud_load_balancer_type_id }}" + register: result +- name: Verify hcloud_load_balancer_type_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_type_info | list | count == 1 + +- name: Gather hcloud_load_balancer_type_info with wrong id + hetzner.hcloud.load_balancer_type_info: + id: "{{ hcloud_load_balancer_type_id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_load_balancer_type_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_load_balancer_type_info with correct name + hetzner.hcloud.load_balancer_type_info: + name: "{{ hcloud_load_balancer_type_name }}" + register: result +- name: Verify hcloud_load_balancer_type_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_type_info | list | count == 1 + +- name: Gather hcloud_load_balancer_type_info with wrong name + hetzner.hcloud.load_balancer_type_info: + name: "{{ hcloud_load_balancer_type_name }}-invalid" + register: result +- name: Verify hcloud_load_balancer_type_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_load_balancer_type_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/aliases new file mode 100644 index 000000000..a6a90a6bf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group3 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/defaults/main/main.yml new file mode 100644 index 000000000..72f0db5bc --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_location_id: 1 +hcloud_location_name: fsn1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/tasks/test.yml new file mode 100644 index 000000000..12ad679b8 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/location_info/tasks/test.yml @@ -0,0 +1,56 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_location_info + hetzner.hcloud.location_info: + register: result +- name: Verify hcloud_location_info + ansible.builtin.assert: + that: + - result.hcloud_location_info | list | count >= 5 + +- name: Gather hcloud_location_info in check mode + hetzner.hcloud.location_info: + check_mode: true + register: result +- name: Verify hcloud_location_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_location_info | list | count >= 5 + +- name: Gather hcloud_location_info with correct id + hetzner.hcloud.location_info: + id: "{{ hcloud_location_id }}" + register: result +- name: Verify hcloud_location_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_location_info | list | count == 1 + +- name: Gather hcloud_location_info with wrong id + hetzner.hcloud.location_info: + id: "{{ hcloud_location_id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_location_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_location_info with correct name + hetzner.hcloud.location_info: + name: "{{ hcloud_location_name }}" + register: result +- name: Verify hcloud_location_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_location_info | list | count == 1 + +- name: Gather hcloud_location_info with wrong name + hetzner.hcloud.location_info: + name: "{{ hcloud_location_name }}-invalid" + register: result +- name: Verify hcloud_location_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_location_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/aliases new file mode 100644 index 000000000..f150235ee --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/aliases @@ -0,0 +1,3 @@ +cloud/hcloud +azp/group1 +disabled diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/defaults/main/main.yml new file mode 100644 index 000000000..a8320602c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_network_name: "{{ hcloud_ns }}" +hcloud_network_name_with_vswitch: "{{ hcloud_ns }}-vswitch" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/tasks/test.yml new file mode 100644 index 000000000..b4f3de284 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network/tasks/test.yml @@ -0,0 +1,268 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup ensure network is absent + hetzner.hcloud.network: + name: "{{ item }}" + state: absent + with_items: + - "{{ hcloud_network_name }}" + - "{{ hcloud_network_name_with_vswitch }}" + +- name: test missing ip_range parameter on create Network + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + register: result + ignore_errors: true +- name: verify fail missing ip_range parameter on create Network result + assert: + that: + - result is failed + - 'result.msg == "missing required arguments: ip_range"' + +- name: test create Network with check mode + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/16" + register: result + check_mode: true +- name: verify create Network with check mode result + assert: + that: + - result is changed + +- name: test create Network + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/16" + register: network +- name: verify test create Network result + assert: + that: + - network is changed + - network.hcloud_network.name == hcloud_network_name + - network.hcloud_network.ip_range == "10.0.0.0/16" + +- name: test create Network idempotence + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/16" + register: network +- name: verify test create network + assert: + that: + - network is not changed + +- name: test create Network with expose_routes_to_vswitch + hetzner.hcloud.network: + name: "{{hcloud_network_name_with_vswitch}}" + ip_range: "10.0.0.0/16" + expose_routes_to_vswitch: true + register: network +- name: verify test create Network with vSwitch result + assert: + that: + - network is changed + - network.hcloud_network.name == hcloud_network_name_with_vswitch + - network.hcloud_network.ip_range == "10.0.0.0/16" + - network.hcloud_network.expose_routes_to_vswitch is true + +- name: test create Network with expose_routes_to_vswitch idempotence + hetzner.hcloud.network: + name: "{{hcloud_network_name_with_vswitch}}" + ip_range: "10.0.0.0/16" + expose_routes_to_vswitch: true + register: network +- name: verify test create network idempotency + assert: + that: + - network is not changed + +- name: test update Network label + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + labels: + key: value + register: network +- name: verify test update Network label + assert: + that: + - network is changed + - network.hcloud_network.labels.key == "value" + +- name: test update Network label idempotency + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + labels: + key: value + register: network +- name: verify test update Network label idempotency + assert: + that: + - network is not changed + +- name: test update Network ip range + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/8" + register: network +- name: verify test update Network ip range + assert: + that: + - network is changed + - network.hcloud_network.ip_range == "10.0.0.0/8" + +- name: test update Network ip range idempotency + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/8" + register: network +- name: verify test update Network ip range idempotency + assert: + that: + - network is not changed + +- name: test update Network expose_routes_to_vswitch + hetzner.hcloud.network: + name: "{{hcloud_network_name_with_vswitch}}" + expose_routes_to_vswitch: false + register: network +- name: verify test update Network expose_routes_to_vswitch + assert: + that: + - network is changed + - network.hcloud_network.expose_routes_to_vswitch is false + +- name: test update Network expose_routes_to_vswitch idempotency + hetzner.hcloud.network: + name: "{{hcloud_network_name_with_vswitch}}" + expose_routes_to_vswitch: false + register: network +- name: verify test update Network expose_routes_to_vswitch idempotency + assert: + that: + - network is not changed + +- name: test update Network delete protection + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/8" + delete_protection: true + register: network +- name: verify test update Network delete protection + assert: + that: + - network is changed + - network.hcloud_network.delete_protection is sameas true + +- name: test update Network delete protection idempotency + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/8" + delete_protection: true + register: network +- name: verify test update Network delete protection idempotency + assert: + that: + - network is not changed + - network.hcloud_network.delete_protection is sameas true + +- name: test Network without delete protection set to be idempotent + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/8" + register: network +- name: verify test Network without delete protection set to be idempotent + assert: + that: + - network is not changed + - network.hcloud_network.delete_protection is sameas true + +- name: test delete Network fails if it is protected + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + state: absent + ignore_errors: true + register: result +- name: verify delete Network + assert: + that: + - result is failed + - 'result.msg == "network deletion is protected"' + +- name: test update Network delete protection + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/8" + delete_protection: false + register: network +- name: verify test update Network delete protection + assert: + that: + - network is changed + - network.hcloud_network.delete_protection is sameas false + +- name: test delete Network + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + state: absent + register: result +- name: verify delete Network + assert: + that: + - result is success + +- name: test create Network with delete protection + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + ip_range: "10.0.0.0/8" + delete_protection: true + register: network +- name: verify create Network with delete protection + assert: + that: + - network is changed + - network.hcloud_network.delete_protection is sameas true + +- name: test delete Network fails if it is protected + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + state: absent + ignore_errors: true + register: result +- name: verify delete Network + assert: + that: + - result is failed + - 'result.msg == "network deletion is protected"' + +- name: test update Network delete protection + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + delete_protection: false + register: network +- name: verify test update Network delete protection + assert: + that: + - network is changed + - network.hcloud_network.delete_protection is sameas false + +- name: test delete Network + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + state: absent + register: result +- name: verify delete Network + assert: + that: + - result is success + +- name: test delete Network with expose_routes_to_vswitch + hetzner.hcloud.network: + name: "{{hcloud_network_name_with_vswitch}}" + state: absent + register: result +- name: verify delete Network with expose_routes_to_vswitch + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/aliases new file mode 100644 index 000000000..62828d1e9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/defaults/main/main.yml new file mode 100644 index 000000000..a01c6d60c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_network_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/cleanup.yml new file mode 100644 index 000000000..989d01b80 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_network + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/prepare.yml new file mode 100644 index 000000000..9a6e61862 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/prepare.yml @@ -0,0 +1,23 @@ +--- +- name: Create test_network + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}" + ip_range: 10.0.0.0/16 + labels: + key: value + register: test_network + +- name: Create test_subnetwork + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + type: server + network_zone: eu-central + ip_range: 10.0.1.0/24 + register: test_subnetwork + +- name: Create test_route + hetzner.hcloud.route: + network: "{{ hcloud_network_name }}" + destination: 10.0.3.0/24 + gateway: 10.0.2.1 + register: test_route diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/test.yml new file mode 100644 index 000000000..cf7367d5f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/network_info/tasks/test.yml @@ -0,0 +1,79 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_network_info + hetzner.hcloud.network_info: + register: result +- name: Verify hcloud_network_info + ansible.builtin.assert: + that: + - result.hcloud_network_info | list | count >= 1 + +- name: Gather hcloud_network_info in check mode + hetzner.hcloud.network_info: + check_mode: true + register: result +- name: Verify hcloud_network_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_network_info | list | count >= 1 + +- name: Gather hcloud_network_info with correct id + hetzner.hcloud.network_info: + id: "{{ test_network.hcloud_network.id }}" + register: result +- name: Verify hcloud_network_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_network_info | list | count == 1 + - result.hcloud_network_info[0].subnetworks | list | count >= 1 + - result.hcloud_network_info[0].routes | list | count >= 1 + +- name: Gather hcloud_network_info with wrong id + hetzner.hcloud.network_info: + id: "{{ test_network.hcloud_network.id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_network_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_network_info with correct name + hetzner.hcloud.network_info: + name: "{{ hcloud_network_name }}" + register: result +- name: Verify hcloud_network_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_network_info | list | count == 1 + +- name: Gather hcloud_network_info with wrong name + hetzner.hcloud.network_info: + name: "{{ hcloud_network_name }}-invalid" + register: result +- name: Verify hcloud_network_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_network_info | list | count == 0 + +- name: Gather hcloud_network_info with correct label selector + hetzner.hcloud.network_info: + label_selector: "key=value" + register: result +- name: Verify hcloud_network_info with correct label selector + ansible.builtin.assert: + that: + - > + result.hcloud_network_info + | selectattr('name', 'equalto', hcloud_network_name) + | list | count == 1 + +- name: Gather hcloud_network_info with wrong label selector + hetzner.hcloud.network_info: + label_selector: "key!=value" + register: result +- name: Verify hcloud_network_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_network_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/defaults/main/main.yml new file mode 100644 index 000000000..a4439afbd --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/defaults/main/main.yml @@ -0,0 +1,6 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_placement_group_name: "{{ hcloud_ns }}" +hcloud_server_name: "{{ hcloud_ns }}" +hcloud_ssh_key_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/meta/main.yml new file mode 100644 index 000000000..3a96ecb2d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_ssh_keypair diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/cleanup.yml new file mode 100644 index 000000000..8066c4115 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_ssh_key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/prepare.yml new file mode 100644 index 000000000..4bdb95996 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/prepare.yml @@ -0,0 +1,6 @@ +--- +- name: Create test_ssh_key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + public_key: "{{ test_ssh_keypair.public_key }}" + register: test_ssh_key diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/test.yml new file mode 100644 index 000000000..3694c846f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/placement_group/tasks/test.yml @@ -0,0 +1,169 @@ +# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup placement group to be absent + hetzner.hcloud.placement_group: + name: "{{ hcloud_placement_group_name }}" + state: absent + +- name: setup server to be absent + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: test missing required parameters on create placement group + hetzner.hcloud.placement_group: + register: result + ignore_errors: true +- name: verify fail test missing required parameters on create placement group + assert: + that: + - result is failed + - 'result.msg == "one of the following is required: id, name"' + +- name: test create placement group with check mode + hetzner.hcloud.placement_group: + name: "{{ hcloud_placement_group_name }}" + type: spread + register: result + check_mode: true +- name: test create placement group with check mode + assert: + that: + - result is changed + +- name: test create placement group + hetzner.hcloud.placement_group: + name: "{{ hcloud_placement_group_name }}" + type: spread + labels: + key: value + my-label: label + register: placement_group +- name: verify create placement group + assert: + that: + - placement_group is changed + - placement_group.hcloud_placement_group.name == hcloud_placement_group_name + - placement_group.hcloud_placement_group.type == "spread" + - placement_group.hcloud_placement_group.servers | list | count == 0 + +- name: test create placement group idempotence + hetzner.hcloud.placement_group: + name: "{{ hcloud_placement_group_name }}" + type: spread + labels: + key: value + my-label: label + register: result +- name: verify create placement group idempotence + assert: + that: + - result is not changed + +- name: test create server with placement group + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + placement_group: "{{ hcloud_placement_group_name }}" + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: server +- name: verify create server with placement group + assert: + that: + - server is changed + - server.hcloud_server.placement_group == hcloud_placement_group_name + +- name: test remove server from placement group + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + placement_group: null + state: present + register: result +- name: verify remove server from placement group + assert: + that: + - result is changed + - result.hcloud_server.placement_group == None + +- name: test add server to placement group + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + placement_group: "{{ hcloud_placement_group_name }}" + force: True + state: present + register: result +- name: verify add server to placement group + assert: + that: + - result is changed + - result.hcloud_server.placement_group == hcloud_placement_group_name + - result.hcloud_server.status == "running" + +- name: test add server to placement group idempotence + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + placement_group: "{{ hcloud_placement_group_name }}" + force: True + state: present + register: result +- name: verify add server to placement group idempotence + assert: + that: + - result is not changed + - result.hcloud_server.placement_group == hcloud_placement_group_name + - result.hcloud_server.status == "running" + +- name: test update placement group with check mode + hetzner.hcloud.placement_group: + id: "{{ placement_group.hcloud_placement_group.id }}" + name: "changed-{{ hcloud_placement_group_name }}" + register: result + check_mode: true +- name: verify update placement group with check mode + assert: + that: + - result is changed + +- name: test update placement group + hetzner.hcloud.placement_group: + id: "{{ placement_group.hcloud_placement_group.id }}" + name: "changed-{{ hcloud_placement_group_name }}" + labels: + key: value + register: result +- name: verify update placement group + assert: + that: + - result is changed + - result.hcloud_placement_group.name == "changed-{{ hcloud_placement_group_name }}" + +- name: test update placement group idempotence + hetzner.hcloud.placement_group: + id: "{{ placement_group.hcloud_placement_group.id }}" + name: "changed-{{ hcloud_placement_group_name }}" + labels: + key: value + register: result +- name: verify update placement group idempotence + assert: + that: + - result is not changed + +- name: absent server + hetzner.hcloud.server: + id: "{{ server.hcloud_server.id }}" + state: absent + +- name: absent placement group + hetzner.hcloud.placement_group: + id: "{{ placement_group.hcloud_placement_group.id }}" + state: absent + register: result +- name: verify absent placement group + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/aliases new file mode 100644 index 000000000..a6a90a6bf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group3 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/defaults/main/main.yml new file mode 100644 index 000000000..7944d5b25 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_primary_ip_name: "{{ hcloud_ns }}" +hcloud_server_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/tasks/test.yml new file mode 100644 index 000000000..4c0fd2e5f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip/tasks/test.yml @@ -0,0 +1,248 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup ensure primary ip is absent + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + state: absent + +- name: test create Primary IP with check mode + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: "fsn1-dc14" + register: primaryIP + check_mode: true +- name: verify test create Primary IP with check mode + assert: + that: + - primaryIP is changed + +- name: test create Primary IP + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: "fsn1-dc14" + register: primaryIP +- name: verify test create Primary IP + assert: + that: + - primaryIP is changed + - primaryIP.hcloud_primary_ip.name ==hcloud_primary_ip_name + - primaryIP.hcloud_primary_ip.datacenter == "fsn1-dc14" + +- name: test create Primary IP idempotency + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: "fsn1-dc14" + register: primaryIP +- name: verify test create Primary IP idempotency + assert: + that: + - primaryIP is not changed + +- name: test update Primary IP + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: "fsn1-dc14" + labels: + key: value + register: primaryIP +- name: verify test update Primary IP + assert: + that: + - primaryIP is changed + +- name: test update Primary IP idempotency + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: "fsn1-dc14" + labels: + key: value + register: primaryIP +- name: verify test update Primary IP idempotency + assert: + that: + - primaryIP is not changed + +- name: test update Primary IP with same labels + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: "fsn1-dc14" + labels: + key: value + register: primaryIP +- name: verify test update Primary IP with same labels + assert: + that: + - primaryIP is not changed + +- name: test update Primary IP with other labels + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: "fsn1-dc14" + labels: + key: value + other: label + register: primaryIP +- name: verify test update Primary IP with other labels + assert: + that: + - primaryIP is changed + +- name: test update Primary IP with other labels in different order + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: "fsn1-dc14" + labels: + other: label + key: value + register: primaryIP +- name: verify test update Primary IP with other labels in different order + assert: + that: + - primaryIP is not changed + +- name: test update Primary IP delete protection + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + delete_protection: true + register: primaryIP +- name: verify update Primary IP delete protection + assert: + that: + - primaryIP is changed + - primaryIP.hcloud_primary_ip.delete_protection is sameas true + +- name: test update Primary IP delete protection idempotency + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + delete_protection: true + register: primaryIP +- name: verify update Primary IP delete protection idempotency + assert: + that: + - primaryIP is not changed + - primaryIP.hcloud_primary_ip.delete_protection is sameas true + +- name: test Primary IP without delete protection set to be idempotent + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + register: primaryIP +- name: verify Primary IP without delete protection set to be idempotent + assert: + that: + - primaryIP is not changed + - primaryIP.hcloud_primary_ip.delete_protection is sameas true + +- name: test delete Primary IP fails if it is protected + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + state: "absent" + register: result + ignore_errors: true +- name: verify test delete primary ip + assert: + that: + - result is failed + - 'result.msg == "Primary IP deletion is protected"' + +- name: test update Primary IP delete protection + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + delete_protection: false + register: primaryIP +- name: verify update Primary IP delete protection + assert: + that: + - primaryIP is changed + - primaryIP.hcloud_primary_ip.delete_protection is sameas false + +- name: test delete primary ip + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + state: "absent" + register: result +- name: verify test delete primary ip + assert: + that: + - result is changed + +- name: test create ipv6 primary ip + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv6 + datacenter: "fsn1-dc14" + state: "present" + register: result +- name: verify test create ipv6 primary ip + assert: + that: + - result is changed + +- name: test delete ipv6 primary ip + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + state: "absent" + register: result +- name: verify test delete ipv6 primary ip + assert: + that: + - result is changed + +- name: test create Primary IP with delete protection + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: fsn1-dc14 + delete_protection: true + register: primaryIP +- name: verify create Primary IP with delete protection + assert: + that: + - primaryIP is changed + - primaryIP.hcloud_primary_ip.delete_protection is sameas true + +- name: test delete Primary IP fails if it is protected + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + state: "absent" + register: result + ignore_errors: true +- name: verify test delete primary ip + assert: + that: + - result is failed + - 'result.msg == "Primary IP deletion is protected"' + +- name: test update Primary IP delete protection + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + delete_protection: false + register: primaryIP +- name: verify update Primary IP delete protection + assert: + that: + - primaryIP is changed + - primaryIP.hcloud_primary_ip.delete_protection is sameas false + +- name: test delete primary ip + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + state: "absent" + register: result +- name: verify test delete primary ip + assert: + that: + - result is changed diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/aliases new file mode 100644 index 000000000..a6a90a6bf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group3 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/defaults/main/main.yml new file mode 100644 index 000000000..9527ca2cd --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_primary_ip_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/cleanup.yml new file mode 100644 index 000000000..8fc48059e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_primary_ip + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/prepare.yml new file mode 100644 index 000000000..4c96ee87d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/prepare.yml @@ -0,0 +1,9 @@ +--- +- name: Create test_primary_ip + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + datacenter: fsn1-dc14 + type: ipv4 + labels: + key: value + register: test_primary_ip diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/test.yml new file mode 100644 index 000000000..902e34153 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/primary_ip_info/tasks/test.yml @@ -0,0 +1,77 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_primary_ip_info + hetzner.hcloud.primary_ip_info: + register: result +- name: Verify hcloud_primary_ip_info + ansible.builtin.assert: + that: + - result.hcloud_primary_ip_info | list | count >= 1 + +- name: Gather hcloud_primary_ip_info in check mode + hetzner.hcloud.primary_ip_info: + check_mode: true + register: result +- name: Verify hcloud_primary_ip_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_primary_ip_info | list | count >= 1 + +- name: Gather hcloud_primary_ip_info with correct id + hetzner.hcloud.primary_ip_info: + id: "{{ test_primary_ip.hcloud_primary_ip.id }}" + register: result +- name: Verify hcloud_primary_ip_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_primary_ip_info | list | count == 1 + +- name: Gather hcloud_primary_ip_info with wrong id + hetzner.hcloud.primary_ip_info: + id: "{{ test_primary_ip.hcloud_primary_ip.id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_primary_ip_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_primary_ip_info with correct name + hetzner.hcloud.primary_ip_info: + name: "{{ hcloud_primary_ip_name }}" + register: result +- name: Verify hcloud_primary_ip_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_primary_ip_info | list | count == 1 + +- name: Gather hcloud_primary_ip_info with wrong name + hetzner.hcloud.primary_ip_info: + name: "{{ hcloud_primary_ip_name }}-invalid" + register: result +- name: Verify hcloud_primary_ip_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_primary_ip_info | list | count == 0 + +- name: Gather hcloud_primary_ip_info with correct label selector + hetzner.hcloud.primary_ip_info: + label_selector: "key=value" + register: result +- name: Verify hcloud_primary_ip_info with correct label selector + ansible.builtin.assert: + that: + - > + result.hcloud_primary_ip_info + | selectattr('name', 'equalto', hcloud_primary_ip_name) + | list | count == 1 + +- name: Gather hcloud_primary_ip_info with wrong label selector + hetzner.hcloud.primary_ip_info: + label_selector: "key!=value" + register: result +- name: Verify hcloud_primary_ip_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_primary_ip_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/aliases new file mode 100644 index 000000000..62828d1e9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/defaults/main/main.yml new file mode 100644 index 000000000..6249a9bd6 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/defaults/main/main.yml @@ -0,0 +1,8 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_server_name: "{{ hcloud_ns }}" +hcloud_floating_ip_name: "{{ hcloud_ns }}" +hcloud_primary_ip_name: "{{ hcloud_ns }}" +hcloud_load_balancer_name: "{{ hcloud_ns }}" +hcloud_ssh_key_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/cleanup.yml new file mode 100644 index 000000000..0df360a33 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/cleanup.yml @@ -0,0 +1,20 @@ +--- +- name: Cleanup test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: Cleanup test_primary_ip + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + state: absent + +- name: Cleanup test_floating_ip + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + state: absent + +- name: Cleanup test_load_balancer + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/prepare.yml new file mode 100644 index 000000000..ea36b6dbc --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/prepare.yml @@ -0,0 +1,32 @@ +--- +- name: Create test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: ubuntu-22.04 + state: present + register: test_server + +- name: Create test_primary_ip + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}" + type: ipv4 + datacenter: fsn1-dc14 + state: present + register: test_primary_ip + +- name: Create test_floating_ip + hetzner.hcloud.floating_ip: + name: "{{ hcloud_floating_ip_name }}" + type: ipv4 + home_location: fsn1 + state: present + register: test_floating_ip + +- name: Create test_load_balancer + hetzner.hcloud.load_balancer: + name: "{{ hcloud_load_balancer_name }}" + load_balancer_type: lb11 + network_zone: eu-central + state: present + register: test_load_balancer diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/test.yml new file mode 100644 index 000000000..342716897 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/rdns/tasks/test.yml @@ -0,0 +1,148 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Test missing required parameters + hetzner.hcloud.rdns: + state: present + ignore_errors: true + register: result +- name: Verify missing required parameters + ansible.builtin.assert: + that: + - result is failed + - 'result.msg == "missing required arguments: ip_address"' + +- name: Test create with checkmode + hetzner.hcloud.rdns: + server: "{{ hcloud_server_name }}" + ip_address: "{{ test_server.hcloud_server.ipv6 | ansible.utils.ipaddr('next_usable') }}" + dns_ptr: example.com + state: present + check_mode: true + register: result +- name: Verify create with checkmode + ansible.builtin.assert: + that: + - result is changed + +- name: Test create + hetzner.hcloud.rdns: + server: "{{ hcloud_server_name }}" + ip_address: "{{ test_server.hcloud_server.ipv6 | ansible.utils.ipaddr('next_usable') }}" + dns_ptr: example.com + state: present + register: result +- name: Verify create + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_rdns.server == hcloud_server_name + - result.hcloud_rdns.ip_address == test_server.hcloud_server.ipv6 | ansible.utils.ipaddr('next_usable') + - result.hcloud_rdns.dns_ptr == "example.com" + +- name: Test create idempotency + hetzner.hcloud.rdns: + server: "{{ hcloud_server_name }}" + ip_address: "{{ test_server.hcloud_server.ipv6 | ansible.utils.ipaddr('next_usable') }}" + dns_ptr: example.com + state: present + register: result +- name: Verify create idempotency + ansible.builtin.assert: + that: + - result is not changed + +- name: Test create with not existing server + hetzner.hcloud.rdns: + server: not-existing + ip_address: "127.0.0.1" + dns_ptr: example.com + state: present + ignore_errors: true + register: result +- name: Verify create with not existing server + ansible.builtin.assert: + that: + - result is failed + - 'result.msg == "resource (server) does not exist: not-existing"' + +- name: Test update + hetzner.hcloud.rdns: + server: "{{ hcloud_server_name }}" + ip_address: "{{ test_server.hcloud_server.ipv4_address }}" + dns_ptr: example.com + state: present + register: result +- name: Verify update + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_rdns.ip_address == test_server.hcloud_server.ipv4_address + +- name: Test update reset + hetzner.hcloud.rdns: + server: "{{ hcloud_server_name }}" + ip_address: "{{ test_server.hcloud_server.ipv4_address }}" + state: present + register: result +- name: Verify update reset + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_rdns.dns_ptr != "example.com" + +- name: Test delete + hetzner.hcloud.rdns: + server: "{{ hcloud_server_name }}" + ip_address: "{{ test_server.hcloud_server.ipv6 | ansible.utils.ipaddr('next_usable') }}" + state: absent + register: result +- name: Verify delete + ansible.builtin.assert: + that: + - result is changed + +- name: Test create with primary ip + hetzner.hcloud.rdns: + primary_ip: "{{ hcloud_primary_ip_name }}" + ip_address: "{{ test_primary_ip.hcloud_primary_ip.ip }}" + dns_ptr: example.com + state: present + register: result +- name: Verify create with primary ip + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_rdns.primary_ip == hcloud_primary_ip_name + - result.hcloud_rdns.ip_address == test_primary_ip.hcloud_primary_ip.ip + - result.hcloud_rdns.dns_ptr == "example.com" + +- name: Test create with floating ip + hetzner.hcloud.rdns: + floating_ip: "{{ hcloud_floating_ip_name }}" + ip_address: "{{ test_floating_ip.hcloud_floating_ip.ip }}" + dns_ptr: example.com + state: present + register: result +- name: Verify create with floating ip + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_rdns.floating_ip == hcloud_floating_ip_name + - result.hcloud_rdns.ip_address == test_floating_ip.hcloud_floating_ip.ip + - result.hcloud_rdns.dns_ptr == "example.com" + +- name: Test create with load balancer + hetzner.hcloud.rdns: + load_balancer: "{{ hcloud_load_balancer_name }}" + ip_address: "{{ test_load_balancer.hcloud_load_balancer.ipv4_address }}" + dns_ptr: example.com + state: present + register: result +- name: Verify create with load balancer + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_rdns.load_balancer == hcloud_load_balancer_name + - result.hcloud_rdns.ip_address == test_load_balancer.hcloud_load_balancer.ipv4_address + - result.hcloud_rdns.dns_ptr == "example.com" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/route/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/aliases new file mode 100644 index 000000000..62828d1e9 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group1 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/route/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/route/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/defaults/main/main.yml new file mode 100644 index 000000000..a01c6d60c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_network_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/route/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/route/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/tasks/test.yml new file mode 100644 index 000000000..571f29a86 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/route/tasks/test.yml @@ -0,0 +1,99 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}" + ip_range: "10.0.0.0/8" + state: present + register: network +- name: verify setup + assert: + that: + - network is success + +- name: test missing required parameters on create route + hetzner.hcloud.route: + state: present + register: result + ignore_errors: true +- name: verify fail test missing required parameters on create route + assert: + that: + - result is failed + - 'result.msg == "missing required arguments: destination, gateway, network"' + +- name: test create route with checkmode + hetzner.hcloud.route: + network: "{{ hcloud_network_name }}" + destination: "10.100.1.0/24" + gateway: "10.0.1.1" + state: present + register: result + check_mode: true +- name: verify test create route with checkmode + assert: + that: + - result is changed + +- name: test create route + hetzner.hcloud.route: + network: "{{ hcloud_network_name }}" + destination: "10.100.1.0/24" + gateway: "10.0.1.1" + state: present + register: route +- name: verify create route + assert: + that: + - route is changed + - route.hcloud_route.network == hcloud_network_name + - route.hcloud_route.destination == "10.100.1.0/24" + - route.hcloud_route.gateway == "10.0.1.1" + +- name: test create route idempotency + hetzner.hcloud.route: + network: "{{ hcloud_network_name }}" + destination: "10.100.1.0/24" + gateway: "10.0.1.1" + state: present + register: result +- name: verify create route idempotency + assert: + that: + - result is not changed + +- name: test fail create route with wrong gateway + hetzner.hcloud.route: + network: "{{ hcloud_network_name }}" + destination: "10.100.1.0/24" + gateway: "10.0.1.2" + state: present + register: route + ignore_errors: true +- name: verfiy fail create route with wrong gateway + assert: + that: + - route is failed + +- name: test absent route + hetzner.hcloud.route: + network: "{{ hcloud_network_name }}" + destination: "10.100.1.0/24" + gateway: "10.0.1.1" + state: absent + register: result +- name: verify test absent route + assert: + that: + - result is changed + +- name: cleanup + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + state: absent + register: result +- name: verify cleanup + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/aliases new file mode 100644 index 000000000..a6a90a6bf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group3 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/defaults/main/main.yml new file mode 100644 index 000000000..d9b84598a --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/defaults/main/main.yml @@ -0,0 +1,8 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_ssh_key_name: "{{ hcloud_ns }}" +hcloud_server_name: "{{ hcloud_ns }}" +hcloud_firewall_name: "{{ hcloud_ns }}" +hcloud_primary_ip_name: "{{ hcloud_ns }}" +hcloud_network_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/meta/main.yml new file mode 100644 index 000000000..3a96ecb2d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_ssh_keypair diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/cleanup.yml new file mode 100644 index 000000000..8066c4115 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_ssh_key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/prepare.yml new file mode 100644 index 000000000..4bdb95996 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/prepare.yml @@ -0,0 +1,6 @@ +--- +- name: Create test_ssh_key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + public_key: "{{ test_ssh_keypair.public_key }}" + register: test_ssh_key diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test.yml new file mode 100644 index 000000000..e00bcce50 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test.yml @@ -0,0 +1,8 @@ +# Copyright: (c) 2022, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +#- ansible.builtin.include_tasks: test_validation.yml +- ansible.builtin.include_tasks: test_basic.yml +#- ansible.builtin.include_tasks: test_firewalls.yml +- ansible.builtin.include_tasks: test_primary_ips.yml +- ansible.builtin.include_tasks: test_private_network_only.yml diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_basic.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_basic.yml new file mode 100644 index 000000000..1e94d67af --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_basic.yml @@ -0,0 +1,613 @@ +- name: test create server with check mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: ubuntu-22.04 + state: present + register: result + check_mode: true +- name: test create server server + assert: + that: + - result is changed + +- name: test create server + hetzner.hcloud.server: + name: "{{ hcloud_server_name}}" + server_type: cx11 + image: ubuntu-22.04 + enable_ipv6: False + state: started + register: main_server +- name: verify create server + assert: + that: + - main_server is changed + - main_server.hcloud_server.name == hcloud_server_name + - main_server.hcloud_server.server_type == "cx11" + - main_server.hcloud_server.status == "running" + - main_server.root_password != "" + +- name: test create server idempotence + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: started + register: result +- name: verify create server idempotence + assert: + that: + - result is not changed + +- name: test stop server with check mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: stopped + register: result + check_mode: true +- name: verify stop server with check mode + assert: + that: + - result is changed + - result.hcloud_server.status == "running" + +- name: test stop server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: stopped + register: result +- name: verify stop server + assert: + that: + - result is changed + - result.hcloud_server.status == "off" + +- name: test start server with check mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: started + register: result + check_mode: true +- name: verify start server with check mode + assert: + that: + - result is changed + +- name: test start server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: started + register: result +- name: verify start server + assert: + that: + - result is changed + - result.hcloud_server.status == "running" + +- name: test start server idempotence + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: started + register: result +- name: verify start server idempotence + assert: + that: + - result is not changed + - result.hcloud_server.status == "running" + +- name: test stop server by its id + hetzner.hcloud.server: + id: "{{ main_server.hcloud_server.id }}" + state: stopped + register: result +- name: verify stop server by its id + assert: + that: + - result is changed + - result.hcloud_server.status == "off" + +- name: test resize server running without force + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: "cx21" + state: present + register: result + check_mode: true +- name: verify test resize server running without force + assert: + that: + - result is changed + - result.hcloud_server.server_type == "cx11" + +- name: test resize server with check mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: "cx21" + state: stopped + register: result + check_mode: true +- name: verify resize server with check mode + assert: + that: + - result is changed + +- name: test resize server without disk + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: "cx21" + state: stopped + register: result +- name: verify resize server without disk + assert: + that: + - result is changed + - result.hcloud_server.server_type == "cx21" + +- name: test resize server idempotence + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: "cx21" + state: stopped + register: result +- name: verify resize server idempotence + assert: + that: + - result is not changed + +- name: test resize server to smaller plan + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: "cx11" + state: stopped + register: result +- name: verify resize server to smaller plan + assert: + that: + - result is changed + - result.hcloud_server.server_type == "cx11" + +- name: test resize server with disk + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: "cx21" + upgrade_disk: true + state: stopped + register: result +- name: verify resize server with disk + assert: + that: + - result is changed + - result.hcloud_server.server_type == "cx21" + +- name: test enable backups with check mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + backups: true + state: stopped + register: result + check_mode: true +- name: verify enable backups with check mode + assert: + that: + - result is changed + +- name: test enable backups + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + backups: true + state: stopped + register: result +- name: verify enable backups + assert: + that: + - result is changed + - result.hcloud_server.backup_window != "" + +- name: test enable backups idempotence + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + backups: true + state: stopped + register: result +- name: verify enable backups idempotence + assert: + that: + - result is not changed + - result.hcloud_server.backup_window != "" + +- name: test backups are not accidentally disabled + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + # Make sure that backups are not disabled because a partial server object without "backups" was supplied somewhere + # to update some unrelated properties. + # Regression test for https://github.com/ansible-collections/hetzner.hcloud/pull/196 + # backups: true + state: stopped + register: result +- name: verify backups are not accidentally disabled + assert: + that: + - result is not changed + - result.hcloud_server.backup_window != "" + +- name: test rebuild server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + image: ubuntu-22.04 + state: rebuild + register: result_after_test +- name: verify rebuild server + assert: + that: + - result_after_test is changed + - result.hcloud_server.id == result_after_test.hcloud_server.id + +- name: test rebuild server with check mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + image: ubuntu-22.04 + state: rebuild + register: result_after_test + check_mode: true +- name: verify rebuild server with check mode + assert: + that: + - result_after_test is changed + +- name: test update server protection booth protection arguments are required + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + delete_protection: true + state: present + register: result_after_test + ignore_errors: true +- name: verify update server protection booth protection arguments are required + assert: + that: + - result_after_test is failed + - 'result_after_test.msg == "parameters are required together: delete_protection, rebuild_protection"' + +- name: test update server protection fails if they are not the same + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + delete_protection: true + rebuild_protection: false + state: present + register: result_after_test + ignore_errors: true +- name: verify update server protection fails if they are not the same + assert: + that: + - result_after_test is failed + +- name: test update server protection + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + delete_protection: true + rebuild_protection: true + state: present + register: result_after_test + ignore_errors: true +- name: verify update server protection + assert: + that: + - result_after_test is changed + - result_after_test.hcloud_server.delete_protection is sameas true + - result_after_test.hcloud_server.rebuild_protection is sameas true + +- name: test server without protection set to be idempotent + hetzner.hcloud.server: + name: "{{hcloud_server_name}}" + register: result_after_test +- name: verify test server without protection set to be idempotent + assert: + that: + - result_after_test is not changed + - result_after_test.hcloud_server.delete_protection is sameas true + - result_after_test.hcloud_server.rebuild_protection is sameas true + +- name: test delete server fails if it is protected + hetzner.hcloud.server: + name: "{{hcloud_server_name}}" + state: absent + ignore_errors: true + register: result +- name: verify delete server fails if it is protected + assert: + that: + - result is failed + - 'result.msg == "server deletion is protected"' + +- name: test rebuild server fails if it is protected + hetzner.hcloud.server: + name: "{{hcloud_server_name}}" + image: ubuntu-22.04 + state: rebuild + ignore_errors: true + register: result +- name: verify rebuild server fails if it is protected + assert: + that: + - result is failed + - 'result.msg == "server rebuild is protected"' + +- name: test remove server protection + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + delete_protection: false + rebuild_protection: false + state: present + register: result_after_test + ignore_errors: true +- name: verify remove server protection + assert: + that: + - result_after_test is changed + - result_after_test.hcloud_server.delete_protection is sameas false + - result_after_test.hcloud_server.rebuild_protection is sameas false + +- name: absent server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify absent server + assert: + that: + - result is success + +- name: test create server with ssh key + hetzner.hcloud.server: + name: "{{ hcloud_server_name}}" + server_type: cx11 + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: started + register: main_server +- name: verify create server with ssh key + assert: + that: + - main_server is changed + - main_server.hcloud_server.name == hcloud_server_name + - main_server.hcloud_server.server_type == "cx11" + - main_server.hcloud_server.status == "running" + - main_server.root_password != "" + +- name: test activate rescue mode with check_mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + rescue_mode: "linux64" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: main_server + check_mode: true +- name: verify activate rescue mode + assert: + that: + - main_server is changed + +- name: test activate rescue mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + rescue_mode: "linux64" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: main_server +- name: verify activate rescue mode + assert: + that: + - main_server is changed + - main_server.hcloud_server.rescue_enabled is sameas true + +- name: test disable rescue mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: main_server +- name: verify activate rescue mode + assert: + that: + - main_server is changed + - main_server.hcloud_server.rescue_enabled is sameas false + +- name: test activate rescue mode without ssh keys + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + rescue_mode: "linux64" + state: present + register: main_server +- name: verify activate rescue mode without ssh keys + assert: + that: + - main_server is changed + - main_server.hcloud_server.rescue_enabled is sameas true + +- name: absent server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify absent server + assert: + that: + - result is success + +- name: test create server with rescue_mode + hetzner.hcloud.server: + name: "{{ hcloud_server_name}}" + server_type: cx11 + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + rescue_mode: "linux64" + state: started + register: main_server +- name: verify create server with rescue_mode + assert: + that: + - main_server is changed + - main_server.hcloud_server.name == hcloud_server_name + - main_server.hcloud_server.server_type == "cx11" + - main_server.hcloud_server.status == "running" + - main_server.root_password != "" + - main_server.hcloud_server.rescue_enabled is sameas true + +- name: absent server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify absent server + assert: + that: + - result is success +- name: test create server with labels + hetzner.hcloud.server: + name: "{{ hcloud_server_name}}" + server_type: cx11 + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + labels: + key: value + mylabel: "val123" + state: started + register: main_server +- name: verify create server with labels + assert: + that: + - main_server is changed + - main_server.hcloud_server.labels.key == "value" + - main_server.hcloud_server.labels.mylabel == "val123" + +- name: test update server with labels + hetzner.hcloud.server: + name: "{{ hcloud_server_name}}" + server_type: cx11 + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + labels: + key: other + mylabel: "val123" + state: started + register: main_server +- name: verify update server with labels + assert: + that: + - main_server is changed + - main_server.hcloud_server.labels.key == "other" + - main_server.hcloud_server.labels.mylabel == "val123" + +- name: test update server with labels in other order + hetzner.hcloud.server: + name: "{{ hcloud_server_name}}" + server_type: cx11 + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + labels: + mylabel: "val123" + key: other + state: started + register: main_server +- name: verify update server with labels in other order + assert: + that: + - main_server is not changed + +- name: cleanup with labels + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify cleanup + assert: + that: + - result is success + +- name: test create server with enabled backups + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + backups: true + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: result +- name: verify enable backups + assert: + that: + - result is changed + - result.hcloud_server.backup_window != "" + +- name: cleanup test create server with enabled backups + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify cleanup + assert: + that: + - result is success + +- name: test create server with protection + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + delete_protection: true + rebuild_protection: true + server_type: cpx11 + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: result_after_test + ignore_errors: true +- name: verify create server with protection + assert: + that: + - result_after_test is changed + - result_after_test.hcloud_server.delete_protection is sameas true + - result_after_test.hcloud_server.rebuild_protection is sameas true + +- name: test delete server fails if it is protected + hetzner.hcloud.server: + name: "{{hcloud_server_name}}" + state: absent + ignore_errors: true + register: result +- name: verify delete server fails if it is protected + assert: + that: + - result is failed + - 'result.msg == "server deletion is protected"' + +- name: remove protection from server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + delete_protection: false + rebuild_protection: false + state: present + register: result_after_test + ignore_errors: true +- name: verify update server protection + assert: + that: + - result_after_test is changed + - result_after_test.hcloud_server.delete_protection is sameas false + - result_after_test.hcloud_server.rebuild_protection is sameas false + +- name: cleanup + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify cleanup + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_firewalls.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_firewalls.yml new file mode 100644 index 000000000..896a6c5cf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_firewalls.yml @@ -0,0 +1,105 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: test add not existing firewall should fail + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + firewalls: + - not-existing + state: present + ignore_errors: true + register: result +- name: verify add not existing firewall should fail + assert: + that: + - result is failed + - 'result.msg == "firewall not-existing was not found"' +- name: setup create firewalls + hetzner.hcloud.firewall: + name: "{{ item }}" + rules: + - direction: in + protocol: icmp + source_ips: + - 0.0.0.0/0 + - ::/0 + with_items: + - "{{ hcloud_firewall_name }}" + - "{{ hcloud_firewall_name }}2" + +- name: test create server with firewalls + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + firewalls: + - "{{ hcloud_firewall_name }}" + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: result +- name: verify test create server with firewalls + assert: + that: + - result is changed + +- name: test create server with firewalls idempotence + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + firewalls: + - "{{ hcloud_firewall_name }}" + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: result +- name: verify test create server with firewalls idempotence + assert: + that: + - result is not changed + +- name: test update server with firewalls + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + firewalls: + - "{{ hcloud_firewall_name }}2" + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: result +- name: verify test update server with firewalls + assert: + that: + - result is changed + +- name: test update server with firewalls idempotence + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + firewalls: + - "{{ hcloud_firewall_name }}2" + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: present + register: result +- name: verify test update server with firewalls idempotence + assert: + that: + - result is not changed + +- name: cleanup server with firewalls + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: cleanup test create firewall + hetzner.hcloud.firewall: + name: "{{ item }}" + state: absent + with_items: + - "{{ hcloud_firewall_name }}" + - "{{ hcloud_firewall_name }}2" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_primary_ips.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_primary_ips.yml new file mode 100644 index 000000000..034da9f74 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_primary_ips.yml @@ -0,0 +1,82 @@ +# Copyright: (c) 2022, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup create primary ipv4 + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}v4" + type: ipv4 + datacenter: "fsn1-dc14" + register: primaryIPv4 + +- name: setup create second primary ipv4 + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}v42" + type: ipv4 + datacenter: "fsn1-dc14" + register: secondPrimaryIPv4 + +- name: setup create primary ipv6 + hetzner.hcloud.primary_ip: + name: "{{ hcloud_primary_ip_name }}v6" + type: ipv6 + datacenter: "fsn1-dc14" + register: primaryIPv6 + +- name: test create server with primary ips + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + datacenter: "fsn1-dc14" + image: "ubuntu-22.04" + ipv4: "{{primaryIPv4.hcloud_primary_ip.id}}" + ipv6: "{{primaryIPv6.hcloud_primary_ip.id}}" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: stopped + register: result +- name: verify test create server with primary ips + assert: + that: + - result is changed + +- name: test update server with primary ips + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + datacenter: "fsn1-dc14" + image: "ubuntu-22.04" + ipv4: "{{secondPrimaryIPv4.hcloud_primary_ip.id}}" + ipv6: "" + enable_ipv6: false + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: stopped + register: result +- name: verify test create server with primary ips + assert: + that: + - result is changed + +- name: cleanup server with primary ips + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: cleanup test create primary ips + hetzner.hcloud.primary_ip: + name: "{{ hcloud_server_name }}v4" + state: absent +- name: cleanup test create primary ips + hetzner.hcloud.primary_ip: + name: "{{ hcloud_server_name }}v42" + state: absent + until: result is not failed + retries: 5 + delay: 2 +- name: cleanup test create primary ips + hetzner.hcloud.primary_ip: + name: "{{ hcloud_server_name }}v6" + state: absent + until: result is not failed + retries: 5 + delay: 2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_private_network_only.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_private_network_only.yml new file mode 100644 index 000000000..a4219a00f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_private_network_only.yml @@ -0,0 +1,135 @@ +# Copyright: (c) 2022, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup network 1 to be absent + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}-1" + state: absent + +- name: setup network 2 to be absent + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}-2" + state: absent + +- name: setup server to be absent + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: setup create network + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}-1" + ip_range: 192.168.0.0/23 + register: primaryNetwork + +- name: setup create network subnet 1 + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}-1" + ip_range: 192.168.0.0/24 + network_zone: eu-central + type: cloud + state: present + +- name: setup create network subnet 2 + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}-1" + ip_range: 192.168.1.0/24 + network_zone: eu-central + type: cloud + state: present + +- name: setup create secondary network + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}-2" + ip_range: 192.168.2.0/23 + register: secondaryNetwork + +- name: setup create secondary network subnet 1 + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}-2" + ip_range: 192.168.2.0/24 + network_zone: eu-central + type: cloud + state: present + +- name: setup create secondary network subnet 2 + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}-2" + ip_range: 192.168.3.0/24 + network_zone: eu-central + type: cloud + state: present + +- name: test create server with primary network and no internet + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + datacenter: "fsn1-dc14" + image: "ubuntu-22.04" + enable_ipv4: false + enable_ipv6: false + private_networks: + - "{{ primaryNetwork.hcloud_network.name }}" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: stopped + register: result +- name: verify test create server with primary network + assert: + that: + - result is changed + +- name: test update server by adding secondary network + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + datacenter: "fsn1-dc14" + image: "ubuntu-22.04" + enable_ipv4: false + enable_ipv6: false + private_networks: + - "{{ primaryNetwork.hcloud_network.name }}" + - "{{ secondaryNetwork.hcloud_network.id }}" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: stopped + register: result +- name: verify test update server by adding secondary network + assert: + that: + - result is changed + +- name: test update server idem + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cpx11 + datacenter: "fsn1-dc14" + image: "ubuntu-22.04" + enable_ipv4: false + enable_ipv6: false + private_networks: + - "{{ primaryNetwork.hcloud_network.name }}" + - "{{ secondaryNetwork.hcloud_network.id }}" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: stopped + register: result +- name: verify test update server idem + assert: + that: + - result is not changed + +- name: cleanup server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent +- name: cleanup networks + hetzner.hcloud.network: + name: "{{ item }}" + state: absent + with_items: + - "{{ primaryNetwork.hcloud_network.name }}" + - "{{ secondaryNetwork.hcloud_network.id }}" + until: result is not failed + retries: 5 + delay: 2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_validation.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_validation.yml new file mode 100644 index 000000000..d4e0ef8b4 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server/tasks/test_validation.yml @@ -0,0 +1,51 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify setup + assert: + that: + - result is success + +- name: test missing required parameters on create server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + register: result + ignore_errors: true +- name: verify fail test missing required parameters on create server + assert: + that: + - result is failed + - 'result.msg == "missing required arguments: server_type, image"' + +- name: test create server with not existing server type + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: not-existing-server-type + image: ubuntu-22.04 + state: present + register: result + ignore_errors: true +- name: verify fail test create server with not existing server type + assert: + that: + - result is failed + - 'result.msg == "server_type not-existing-server-type was not found"' + +- name: test create server with not existing image + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: my-not-existing-image-20.04 + state: present + register: result + ignore_errors: true +- name: verify fail test create server with not existing image + assert: + that: + - result is failed + - 'result.msg == "Image my-not-existing-image-20.04 was not found"' diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/defaults/main/main.yml new file mode 100644 index 000000000..a85dafcfc --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_server_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/cleanup.yml new file mode 100644 index 000000000..0c95d2c65 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/cleanup.yml @@ -0,0 +1,10 @@ +--- +- name: Cleanup test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + +- name: Cleanup test_server2 + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}2" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/prepare.yml new file mode 100644 index 000000000..9e8aa2c9f --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/prepare.yml @@ -0,0 +1,22 @@ +--- +- name: Create test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: ubuntu-22.04 + state: started + labels: + key: value + register: test_server + +- name: Create test_server2 (stopped + without ip) + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}2" + server_type: cx11 + image: ubuntu-22.04 + state: stopped + labels: + key: value + enable_ipv4: false + enable_ipv6: false + register: test_server2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/test.yml new file mode 100644 index 000000000..534c50a70 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_info/tasks/test.yml @@ -0,0 +1,89 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_server_info + hetzner.hcloud.server_info: + register: result +- name: Verify hcloud_server_info + ansible.builtin.assert: + that: + - result.hcloud_server_info | list | count >= 2 + +- name: Gather hcloud_server_info in check mode + hetzner.hcloud.server_info: + check_mode: true + register: result +- name: Verify hcloud_server_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_server_info | list | count >= 2 + +- name: Gather hcloud_server_info with correct id + hetzner.hcloud.server_info: + id: "{{ test_server.hcloud_server.id }}" + register: result +- name: Verify hcloud_server_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_server_info | list | count == 1 + - result.hcloud_server_info[0].name == hcloud_server_name + +- name: Gather hcloud_server_info with wrong id + hetzner.hcloud.server_info: + id: "{{ test_server.hcloud_server.id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_server_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_server_info with correct name + hetzner.hcloud.server_info: + name: "{{ hcloud_server_name }}" + register: result +- name: Verify hcloud_server_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_server_info | list | count == 1 + - result.hcloud_server_info[0].name == hcloud_server_name + +- name: Gather hcloud_server_info with wrong name + hetzner.hcloud.server_info: + name: "{{ hcloud_server_name }}-invalid" + register: result +- name: Verify hcloud_server_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_server_info | list | count == 0 + +- name: Gather hcloud_server_info with correct label selector + hetzner.hcloud.server_info: + label_selector: "key=value" + register: result +- name: Verify hcloud_server_info with correct label selector + ansible.builtin.assert: + that: + - > + result.hcloud_server_info + | selectattr('name', 'equalto', hcloud_server_name) + | list | count == 1 + +- name: Gather hcloud_server_info with wrong label selector + hetzner.hcloud.server_info: + label_selector: "key!=value" + register: result +- name: Verify hcloud_server_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_server_info | list | count == 0 + +- name: Gather hcloud_server_info (without ip) + hetzner.hcloud.server_info: + id: "{{ test_server2.hcloud_server.id }}" + register: result +- name: Verify hcloud_server_info (without ip) + ansible.builtin.assert: + that: + - result.hcloud_server_info | list | count == 1 + - result.hcloud_server_info[0].name == hcloud_server_name + '2' diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/aliases new file mode 100644 index 000000000..6e9b68657 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/aliases @@ -0,0 +1,3 @@ +cloud/hcloud +azp/group2 +disabled diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/defaults/main/main.yml new file mode 100644 index 000000000..c9a119410 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_network_name: "{{ hcloud_ns }}" +hcloud_server_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/tasks/test.yml new file mode 100644 index 000000000..a77f2e30a --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_network/tasks/test.yml @@ -0,0 +1,222 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup network + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}" + ip_range: "10.0.0.0/8" + state: present + register: network +- name: verify setup network + assert: + that: + - network is success + +- name: setup subnetwork + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + ip_range: "10.0.0.0/16" + type: "server" + network_zone: "eu-central" + state: present + register: subnetwork +- name: verify subnetwork + assert: + that: + - subnetwork is success + +- name: setup server + hetzner.hcloud.server: + name: "{{hcloud_server_name}}" + server_type: cx11 + image: ubuntu-22.04 + state: started + location: "fsn1" + register: server +- name: verify setup server + assert: + that: + - server is success + +- name: test missing required parameters on create server network + hetzner.hcloud.server_network: + state: present + register: result + ignore_errors: true +- name: verify fail test missing required parameters on create server network + assert: + that: + - result is failed + - 'result.msg == "missing required arguments: network, server"' + +- name: test create server network with checkmode + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + state: present + register: result + check_mode: true +- name: verify test create server network with checkmode + assert: + that: + - result is changed + +- name: test create server network + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + state: present + register: serverNetwork +- name: verify create server network + assert: + that: + - serverNetwork is changed + - serverNetwork.hcloud_server_network.network == hcloud_network_name + - serverNetwork.hcloud_server_network.server == hcloud_server_name + +- name: test create server network idempotency + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + state: present + register: serverNetwork +- name: verify create server network idempotency + assert: + that: + - serverNetwork is not changed + +- name: test absent server network + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + state: absent + register: result +- name: verify test absent server network + assert: + that: + - result is changed + +- name: test create server network with specified ip + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + ip: "10.0.0.2" + state: present + register: serverNetwork +- name: verify create server network with specified ip + assert: + that: + - serverNetwork is changed + - serverNetwork.hcloud_server_network.network == hcloud_network_name + - serverNetwork.hcloud_server_network.server == hcloud_server_name + - serverNetwork.hcloud_server_network.ip == "10.0.0.2" + +- name: cleanup create server network with specified ip + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + state: absent + register: result +- name: verify cleanup create server network with specified ip + assert: + that: + - result is changed + +- name: test create server network with alias ips + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + ip: "10.0.0.2" + alias_ips: + - "10.0.1.2" + - "10.0.2.3" + state: present + register: serverNetwork +- name: verify create server network with alias ips + assert: + that: + - serverNetwork is changed + - serverNetwork.hcloud_server_network.network == hcloud_network_name + - serverNetwork.hcloud_server_network.server == hcloud_server_name + - serverNetwork.hcloud_server_network.ip == "10.0.0.2" + - 'serverNetwork.hcloud_server_network.alias_ips[0] == "10.0.2.3"' + - 'serverNetwork.hcloud_server_network.alias_ips[1] == "10.0.1.2"' + +- name: test update server network with alias ips + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + ip: "10.0.0.2" + alias_ips: + - "10.0.2.3" + - "10.0.3.1" + state: present + register: serverNetwork +- name: verify create server network with alias ips + assert: + that: + - serverNetwork is changed + - serverNetwork.hcloud_server_network.network == hcloud_network_name + - serverNetwork.hcloud_server_network.server == hcloud_server_name + - serverNetwork.hcloud_server_network.ip == "10.0.0.2" + - 'serverNetwork.hcloud_server_network.alias_ips[0] == "10.0.2.3"' + - 'serverNetwork.hcloud_server_network.alias_ips[1] == "10.0.3.1"' + +- name: test update server network with alias ips idempotency + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + ip: "10.0.0.2" + alias_ips: + - "10.0.2.3" + - "10.0.3.1" + state: present + register: serverNetwork +- name: verify create server network with alias ips idempotency + assert: + that: + - serverNetwork is not changed + +- name: cleanup create server network with alias ips + hetzner.hcloud.server_network: + network: "{{ hcloud_network_name }}" + server: "{{hcloud_server_name}}" + state: absent + register: result +- name: verify cleanup create server network with alias ips + assert: + that: + - result is changed + +- name: cleanup server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify cleanup server + assert: + that: + - result is success + +- name: cleanup subnetwork + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + ip_range: "10.0.0.0/16" + type: "server" + network_zone: "eu-central" + state: absent + register: result +- name: verify cleanup subnetwork + assert: + that: + - result is changed + +- name: cleanup + hetzner.hcloud.network: + name: "{{hcloud_network_name}}" + state: absent + register: result +- name: verify cleanup + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/defaults/main/main.yml new file mode 100644 index 000000000..c488c4dfc --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/defaults/main/main.yml @@ -0,0 +1,7 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_server_type_name: cx11 +hcloud_server_type_id: 1 + +hcloud_server_type_id_deprecated: 2 # cx11-ceph diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/tasks/test.yml new file mode 100644 index 000000000..9c51e09c1 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/server_type_info/tasks/test.yml @@ -0,0 +1,69 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_server_type_info + hetzner.hcloud.server_type_info: + register: result +- name: Verify hcloud_server_type_info + ansible.builtin.assert: + that: + - result.hcloud_server_type_info | list | count >= 3 + +- name: Gather hcloud_server_type_info in check mode + hetzner.hcloud.server_type_info: + check_mode: true + register: result +- name: Verify hcloud_server_type_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_server_type_info | list | count >= 3 + +- name: Gather hcloud_server_type_info with correct id + hetzner.hcloud.server_type_info: + id: "{{ hcloud_server_type_id }}" + register: result +- name: Verify hcloud_server_type_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_server_type_info | list | count == 1 + - result.hcloud_server_type_info[0].deprecation is none # fails if cx11 is ever deprecated + +- name: Gather hcloud_server_type_info with wrong id + hetzner.hcloud.server_type_info: + id: "{{ hcloud_server_type_id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_server_type_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_server_type_info with correct name + hetzner.hcloud.server_type_info: + name: "{{ hcloud_server_type_name }}" + register: result +- name: Verify hcloud_server_type_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_server_type_info | list | count == 1 + +- name: Gather hcloud_server_type_info with wrong name + hetzner.hcloud.server_type_info: + name: "{{ hcloud_server_type_name }}-invalid" + register: result +- name: Verify hcloud_server_type_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_server_type_info | list | count == 0 + +- name: Gather hcloud_server_type_info with deprecated field + hetzner.hcloud.server_type_info: + id: "{{ hcloud_server_type_id_deprecated }}" + register: result +- name: Verify hcloud_server_type_info with deprecated field + ansible.builtin.assert: + that: + - result.hcloud_server_type_info | list | count == 1 + - result.hcloud_server_type_info[0].deprecation is not none + - result.hcloud_server_type_info[0].deprecation.announced == '2021-11-09T09:00:00+00:00' + - result.hcloud_server_type_info[0].deprecation.unavailable_after == '2021-12-01T00:00:00+00:00' diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_hcloud_cli/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_hcloud_cli/tasks/main.yml new file mode 100644 index 000000000..7b5356b41 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_hcloud_cli/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: Create temporary file for hcloud_cli_path + ansible.builtin.tempfile: + state: directory + register: _tmp_hcloud_cli + +- name: Download hcloud cli from Github releases + ansible.builtin.unarchive: + src: https://github.com/hetznercloud/cli/releases/download/v1.37.0/hcloud-linux-amd64.tar.gz + dest: "{{ _tmp_hcloud_cli.path }}" + remote_src: true + extra_opts: [hcloud] + +- name: Set hcloud_cli_path + ansible.builtin.set_fact: + hcloud_cli_path: "{{ _tmp_hcloud_cli.path }}/hcloud" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_selfsigned_certificate/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_selfsigned_certificate/tasks/main.yml index 27defe44c..76d6e1791 100644 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_selfsigned_certificate/tasks/main.yml +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_selfsigned_certificate/tasks/main.yml @@ -1,27 +1,35 @@ -# Copyright: (c) 2020, Hetzner Cloud GmbH <info@hetzner-cloud.de> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Create temporary file for test_certificate + ansible.builtin.tempfile: + suffix: "{{ hcloud_certificate_name }}" + register: _tmp_certificate_file -- name: create a cert temp file - tempfile: - state: file - register: certificate_example_com - tags: - - prepare -- name: create a key temp file - tempfile: - state: file - register: certificate_example_com_key - tags: - - prepare - - -- name: generate certificate - shell: openssl req -nodes -new -x509 -keyout {{ certificate_example_com_key.path }} -out {{ certificate_example_com.path }} -subj "/C=DE/ST=Munich/L=Bavaria/O=Dis/CN=www.example.com" - tags: - - prepare +- name: Create certificate privatekey file + community.crypto.openssl_privatekey: + path: "{{ _tmp_certificate_file.path }}.key" + return_content: true + register: _certificate_privatekey_file -- name: set facts for future roles - set_fact: - certificate_example_com: "{{ lookup('file',certificate_example_com.path) }}" - certificate_example_com_key: "{{ lookup('file',certificate_example_com_key.path) }}" - tags: - - prepare +- name: Create certificate signing request file + community.crypto.openssl_csr: + privatekey_path: "{{ _tmp_certificate_file.path }}.key" + path: "{{ _tmp_certificate_file.path }}.csr" + country_name: DE + locality_name: Bavaria + state_or_province_name: Munich + organization_name: Dis + common_name: www.example.com + +- name: Create certificate file + community.crypto.x509_certificate: + privatekey_path: "{{ _tmp_certificate_file.path }}.key" + csr_path: "{{ _tmp_certificate_file.path }}.csr" + path: "{{ _tmp_certificate_file.path }}.crt" + provider: selfsigned + return_content: true + register: _certificate_file + +- name: Save certificate files content + ansible.builtin.set_fact: + test_certificate_privatekey_content: "{{ _certificate_privatekey_file.privatekey }}" + test_certificate_content: "{{ _certificate_file.certificate }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_ssh_keypair/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_ssh_keypair/tasks/main.yml new file mode 100644 index 000000000..c51b42863 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_ssh_keypair/tasks/main.yml @@ -0,0 +1,19 @@ +--- +# https://github.com/ansible-collections/community.crypto/pull/504 +- name: Create temporary directory for test_ssh_keypair + ansible.builtin.file: + state: directory + path: ~/tmp + mode: "0755" + +- name: Create temporary file for test_ssh_keypair + ansible.builtin.tempfile: + path: ~/tmp + suffix: "{{ hcloud_ssh_key_name }}" + register: _tmp_ssh_key_file + +- name: Create test_ssh_keypair + community.crypto.openssh_keypair: + path: "{{ _tmp_ssh_key_file.path }}" + force: true + register: test_ssh_keypair diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_sshkey/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_sshkey/tasks/main.yml deleted file mode 100644 index 18c571b67..000000000 --- a/ansible_collections/hetzner/hcloud/tests/integration/targets/setup_sshkey/tasks/main.yml +++ /dev/null @@ -1,55 +0,0 @@ -# (c) 2014, James Laska <jlaska@ansible.com> - -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. - -- name: create a temp file - tempfile: - state: file - register: sshkey_file - tags: - - prepare - -- name: generate sshkey - shell: echo 'y' | ssh-keygen -P '' -f {{ sshkey_file.path }} - tags: - - prepare - -- name: create another temp file - tempfile: - state: file - register: another_sshkey_file - tags: - - prepare - -- name: generate another_sshkey - shell: echo 'y' | ssh-keygen -P '' -f {{ another_sshkey_file.path }} - tags: - - prepare - -- name: record fingerprint - shell: openssl rsa -in {{ sshkey_file.path }} -pubout -outform DER 2>/dev/null | openssl md5 -c - register: fingerprint - tags: - - prepare - -- name: set facts for future roles - set_fact: - sshkey: '{{ sshkey_file.path }}' - key_material: "{{ lookup('file', sshkey_file.path ~ '.pub') }}" - another_key_material: "{{ lookup('file', another_sshkey_file.path ~ '.pub') }}" - fingerprint: '{{ fingerprint.stdout.split()[1] }}' - tags: - - prepare diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/defaults/main/main.yml new file mode 100644 index 000000000..87c4d8e31 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_server_name: "{{ hcloud_ns }}" +hcloud_ssh_key_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/meta/main.yml new file mode 100644 index 000000000..3a96ecb2d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_ssh_keypair diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/tasks/cleanup.yml new file mode 100644 index 000000000..fea3ff06d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/tasks/cleanup.yml @@ -0,0 +1,10 @@ +--- +- name: Cleanup test_ssh_key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + state: absent + +- name: Cleanup test_server + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/tasks/test.yml new file mode 100644 index 000000000..41b9c351d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key/tasks/test.yml @@ -0,0 +1,146 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: test missing required parameters on create ssh_key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + register: result + ignore_errors: true +- name: verify fail test missing required parameters on create ssh_key + assert: + that: + - result is failed + - 'result.msg == "missing required arguments: public_key"' + +- name: test create ssh key with check mode + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + public_key: "{{ test_ssh_keypair.public_key }}" + register: result + check_mode: true +- name: test create ssh key with check mode + assert: + that: + - result is changed + +- name: test create ssh key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + public_key: "{{ test_ssh_keypair.public_key }}" + labels: + key: value + my-label: label + register: ssh_key +- name: verify create ssh key + assert: + that: + - ssh_key is changed + - ssh_key.hcloud_ssh_key.name == hcloud_ssh_key_name + - ssh_key.hcloud_ssh_key.public_key == test_ssh_keypair.public_key + - ssh_key.hcloud_ssh_key.labels.key == "value" + +- name: test create ssh key idempotence + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + public_key: "{{ test_ssh_keypair.public_key }}" + register: result +- name: verify create ssh key idempotence + assert: + that: + - result is not changed + +- name: test update ssh key with check mode + hetzner.hcloud.ssh_key: + id: "{{ ssh_key.hcloud_ssh_key.id }}" + name: "changed-{{ hcloud_ssh_key_name }}" + register: result + check_mode: true +- name: test create ssh key with check mode + assert: + that: + - result is changed + +- name: test update ssh key + hetzner.hcloud.ssh_key: + id: "{{ ssh_key.hcloud_ssh_key.id }}" + name: "changed-{{ hcloud_ssh_key_name }}" + labels: + key: value + register: result +- name: test update ssh key + assert: + that: + - result is changed + - result.hcloud_ssh_key.name == "changed-{{ hcloud_ssh_key_name }}" + +- name: test update ssh key with same labels + hetzner.hcloud.ssh_key: + id: "{{ ssh_key.hcloud_ssh_key.id }}" + name: "changed-{{ hcloud_ssh_key_name }}" + labels: + key: value + register: result +- name: test update ssh key with same labels + assert: + that: + - result is not changed + +- name: test update ssh key with other labels + hetzner.hcloud.ssh_key: + id: "{{ ssh_key.hcloud_ssh_key.id }}" + name: "changed-{{ hcloud_ssh_key_name }}" + labels: + key: value + test: "val123" + register: result +- name: test update ssh key with other labels + assert: + that: + - result is changed + +- name: test rename ssh key + hetzner.hcloud.ssh_key: + id: "{{ ssh_key.hcloud_ssh_key.id }}" + name: "{{ hcloud_ssh_key_name }}" + register: result +- name: test rename ssh key + assert: + that: + - result is changed + - result.hcloud_ssh_key.name == hcloud_ssh_key_name + +- name: test create server with ssh key + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + server_type: cx11 + image: "ubuntu-22.04" + ssh_keys: + - "{{ hcloud_ssh_key_name }}" + state: started + register: main_server +- name: verify create server with ssh key + assert: + that: + - main_server is changed + +- name: test fail cleanly on double created ssh key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}-other-name" + public_key: "{{ test_ssh_keypair.public_key }}" + register: result + ignore_errors: true +- name: verify failed correctly + assert: + that: + - result is failed + - 'result.msg == "SSH key with the same fingerprint already exists"' + +- name: test delete ssh key + hetzner.hcloud.ssh_key: + id: "{{ ssh_key.hcloud_ssh_key.id }}" + state: absent + register: result +- name: verify absent ssh_key + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/defaults/main/main.yml new file mode 100644 index 000000000..68a7f5a35 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_ssh_key_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/meta/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/meta/main.yml new file mode 100644 index 000000000..3a96ecb2d --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_ssh_keypair diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/cleanup.yml new file mode 100644 index 000000000..8066c4115 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_ssh_key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/prepare.yml new file mode 100644 index 000000000..519bc8526 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/prepare.yml @@ -0,0 +1,8 @@ +--- +- name: Create test_ssh_key + hetzner.hcloud.ssh_key: + name: "{{ hcloud_ssh_key_name }}" + public_key: "{{ test_ssh_keypair.public_key }}" + labels: + key: value + register: test_ssh_key diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/test.yml new file mode 100644 index 000000000..d8ea7cb37 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/ssh_key_info/tasks/test.yml @@ -0,0 +1,77 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_ssh_key_info + hetzner.hcloud.ssh_key_info: + register: result +- name: Verify hcloud_ssh_key_info + ansible.builtin.assert: + that: + - result.hcloud_ssh_key_info | list | count >= 1 + +- name: Gather hcloud_ssh_key_info in check mode + hetzner.hcloud.ssh_key_info: + check_mode: true + register: result +- name: Verify hcloud_ssh_key_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_ssh_key_info | list | count >= 1 + +- name: Gather hcloud_ssh_key_info with correct id + hetzner.hcloud.ssh_key_info: + id: "{{ test_ssh_key.hcloud_ssh_key.id }}" + register: result +- name: Verify hcloud_ssh_key_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_ssh_key_info | list | count == 1 + +- name: Gather hcloud_ssh_key_info with wrong id + hetzner.hcloud.ssh_key_info: + id: "{{ test_ssh_key.hcloud_ssh_key.id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_ssh_key_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_ssh_key_info with correct name + hetzner.hcloud.ssh_key_info: + name: "{{ hcloud_ssh_key_name }}" + register: result +- name: Verify hcloud_ssh_key_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_ssh_key_info | list | count == 1 + +- name: Gather hcloud_ssh_key_info with wrong name + hetzner.hcloud.ssh_key_info: + name: "{{ hcloud_ssh_key_name }}-invalid" + register: result +- name: Verify hcloud_ssh_key_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_ssh_key_info | list | count == 0 + +- name: Gather hcloud_ssh_key_info with correct label selector + hetzner.hcloud.ssh_key_info: + label_selector: "key=value" + register: result +- name: Verify hcloud_ssh_key_info with correct label selector + ansible.builtin.assert: + that: + - > + result.hcloud_ssh_key_info + | selectattr('name', 'equalto', hcloud_ssh_key_name) + | list | count == 1 + +- name: Gather hcloud_ssh_key_info with wrong label selector + hetzner.hcloud.ssh_key_info: + label_selector: "key!=value" + register: result +- name: Verify hcloud_ssh_key_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_ssh_key_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/aliases new file mode 100644 index 000000000..a6a90a6bf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group3 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/defaults/main/main.yml new file mode 100644 index 000000000..afaa77652 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/defaults/main/main.yml @@ -0,0 +1,23 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_network_name: "{{ hcloud_ns }}" +# Pool of external Hetzner vSwitch ID, this prevents using the same vSwitch id twice in +# different jobs. +hetzner_vswitch_ids: + - 43065 + - 44166 + - 44167 + - 44168 + - 44170 + - 44171 + - 44172 + - 44173 + - 44174 + - 44175 + - 44176 + - 44177 + - 44178 + - 44179 + - 44180 + - 44181 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/cleanup.yml new file mode 100644 index 000000000..989d01b80 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_network + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/prepare.yml new file mode 100644 index 000000000..69a709ddf --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/prepare.yml @@ -0,0 +1,11 @@ +--- +- name: Create test_network + hetzner.hcloud.network: + name: "{{ hcloud_network_name }}" + ip_range: "10.0.0.0/16" + state: present + register: test_network + +- name: Select hetzner vswitch id from pool + ansible.builtin.set_fact: + test_vswitch_id: "{{ hetzner_vswitch_ids | random }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/test.yml new file mode 100644 index 000000000..acde98f7b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/subnetwork/tasks/test.yml @@ -0,0 +1,103 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Test missing required parameters + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + state: present + ignore_errors: true + register: result +- name: Verify missing required parameters + ansible.builtin.assert: + that: + - result is failed + - 'result.msg == "missing required arguments: ip_range, network_zone, type"' + +- name: Test create with checkmode + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + network_zone: eu-central + type: cloud + ip_range: 10.0.0.0/24 + state: present + check_mode: true + register: result +- name: Verify create with checkmode + ansible.builtin.assert: + that: + - result is changed + +- name: Test create + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + network_zone: eu-central + type: cloud + ip_range: 10.0.0.0/24 + state: present + register: result +- name: Verify create + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_subnetwork.network == hcloud_network_name + - result.hcloud_subnetwork.network_zone == "eu-central" + - result.hcloud_subnetwork.type == "cloud" + - result.hcloud_subnetwork.ip_range == "10.0.0.0/24" + +- name: Test create idempotency + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + network_zone: eu-central + type: cloud + ip_range: 10.0.0.0/24 + state: present + register: result +- name: Verify create idempotency + ansible.builtin.assert: + that: + - result is not changed + +- name: Test delete + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + network_zone: eu-central + type: cloud + ip_range: 10.0.0.0/24 + state: absent + register: result +- name: Verify delete + ansible.builtin.assert: + that: + - result is changed + +- name: Test create with vswitch + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + network_zone: eu-central + type: vswitch + ip_range: 10.0.1.0/24 + vswitch_id: "{{ test_vswitch_id }}" + state: present + register: result +- name: Verify create with vswitch + ansible.builtin.assert: + that: + - result is changed + - result.hcloud_subnetwork.network == hcloud_network_name + - result.hcloud_subnetwork.network_zone == "eu-central" + - result.hcloud_subnetwork.type == "vswitch" + - result.hcloud_subnetwork.ip_range == "10.0.1.0/24" + - result.hcloud_subnetwork.vswitch_id | string == test_vswitch_id + +- name: Test delete with vswitch + hetzner.hcloud.subnetwork: + network: "{{ hcloud_network_name }}" + network_zone: eu-central + type: vswitch + ip_range: 10.0.1.0/24 + state: absent + register: subnet +- name: Verify delete with vswitch + ansible.builtin.assert: + that: + - result is changed diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/defaults/main/main.yml new file mode 100644 index 000000000..65949c814 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/defaults/main/main.yml @@ -0,0 +1,5 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_volume_name: "{{ hcloud_ns }}" +hcloud_server_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/tasks/test.yml new file mode 100644 index 000000000..fd47d5343 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume/tasks/test.yml @@ -0,0 +1,288 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup server + hetzner.hcloud.server: + name: "{{hcloud_server_name}}" + server_type: cx11 + image: ubuntu-22.04 + state: started + location: "fsn1" + register: vol_server +- name: verify setup server + assert: + that: + - vol_server is changed + +- name: test missing size parameter on create Volume + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + server: "{{hcloud_server_name}}" + register: result + ignore_errors: true +- name: verify fail test missing size parameter on create Volume + assert: + that: + - result is failed + - 'result.msg == "missing required arguments: size"' + +- name: test create Volume with check mode + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + size: 10 + location: "fsn1" + register: result + check_mode: true +- name: verify create Volume with check mode result + assert: + that: + - result is changed + +- name: test create Volume + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + size: 10 + location: "fsn1" + register: volume +- name: verify test create Volume + assert: + that: + - volume is changed + - volume.hcloud_volume.name == hcloud_volume_name + - volume.hcloud_volume.location == "fsn1" + - volume.hcloud_volume.size == 10 + - volume.hcloud_volume.server != hcloud_server_name + - volume.hcloud_volume.linux_device is defined + +- name: test create Volume idempotence + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + size: 10 + location: "fsn1" + register: volume +- name: verify test create Volume + assert: + that: + - volume is not changed + +- name: test attach Volume with checkmode + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + server: "{{hcloud_server_name}}" + check_mode: true + register: volume +- name: verify test attach Volume with checkmode + assert: + that: + - volume is changed + - volume.hcloud_volume.server != hcloud_server_name + +- name: test attach Volume + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + server: "{{hcloud_server_name}}" + register: volume +- name: verify attach volume + assert: + that: + - volume is changed + - volume.hcloud_volume.server == hcloud_server_name + +- name: test attach Volume idempotence + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + server: "{{hcloud_server_name}}" + register: volume +- name: verify attach Volume idempotence + assert: + that: + - volume is not changed + - volume.hcloud_volume.server == hcloud_server_name + +- name: test detach Volume with checkmode + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + check_mode: true + register: volume +- name: verify detach Volume with checkmode + assert: + that: + - volume is changed + - volume.hcloud_volume.server == hcloud_server_name + +- name: test detach Volume + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + register: volume +- name: verify detach volume + assert: + that: + - volume is changed + - volume.hcloud_volume.location == "fsn1" + - volume.hcloud_volume.server != hcloud_server_name + +- name: test update Volume label + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + labels: + key: value + register: volume +- name: verify test update Volume label + assert: + that: + - volume is changed + - volume.hcloud_volume.labels.key == "value" + +- name: test update Volume label with the same label + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + labels: + key: value + register: volume +- name: verify test update Volume lable with the same label + assert: + that: + - volume is not changed + +- name: test increase Volume size + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + size: 11 + register: volume +- name: verify test increase Volume size + assert: + that: + - volume is changed + - volume.hcloud_volume.size == 11 + +- name: test decreace Volume size + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + size: 10 + register: volume +- name: verify test decreace Volume size + assert: + that: + - volume is not changed + - volume.hcloud_volume.size == 11 + +- name: test update Volume delete protection + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + delete_protection: true + register: volume +- name: verify test update Volume delete protection + assert: + that: + - volume is changed + - volume.hcloud_volume.delete_protection is sameas true + +- name: test update Volume delete protection idempotency + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + delete_protection: true + register: volume +- name: verify test update Volume delete protection idempotency + assert: + that: + - volume is not changed + - volume.hcloud_volume.delete_protection is sameas true + +- name: test Volume without delete protection set to be idempotent + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + register: volume +- name: verify test Volume without delete protection set to be idempotent + assert: + that: + - volume is not changed + - volume.hcloud_volume.delete_protection is sameas true + +- name: test delete Volume fails if it is protected + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + state: absent + ignore_errors: true + register: result +- name: verify delete Volume fails if it is protected + assert: + that: + - result is failed + - 'result.msg == "volume deletion is protected"' + +- name: test update Volume delete protection + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + delete_protection: false + register: volume +- name: verify test update Volume delete protection + assert: + that: + - volume is changed + - volume.hcloud_volume.delete_protection is sameas false + +- name: test delete Volume + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + state: absent + register: result +- name: verify delete Volume + assert: + that: + - result is success + +- name: test create Volume with delete protection + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + size: 10 + location: "fsn1" + delete_protection: true + register: volume +- name: verify create Volume with delete protection + assert: + that: + - volume is changed + - volume.hcloud_volume.delete_protection is sameas true + +- name: test delete Volume fails if it is protected + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + state: absent + ignore_errors: true + register: result +- name: verify delete Volume fails if it is protected + assert: + that: + - result is failed + - 'result.msg == "volume deletion is protected"' + +- name: test update Volume delete protection + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + delete_protection: false + register: volume +- name: verify test update Volume delete protection + assert: + that: + - volume is changed + - volume.hcloud_volume.delete_protection is sameas false + +- name: test delete Volume + hetzner.hcloud.volume: + name: "{{hcloud_volume_name}}" + state: absent + register: result +- name: verify delete Volume + assert: + that: + - result is success + +- name: cleanup + hetzner.hcloud.server: + name: "{{ hcloud_server_name }}" + state: absent + register: result +- name: verify cleanup + assert: + that: + - result is success diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/aliases b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/aliases new file mode 100644 index 000000000..0e887600e --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/aliases @@ -0,0 +1,2 @@ +cloud/hcloud +azp/group2 diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/defaults/main/common.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/defaults/main/common.yml new file mode 100644 index 000000000..e316b233c --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/defaults/main/common.yml @@ -0,0 +1,12 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +# Azure Pipelines will configure this value to something similar to +# "azp-84824-1-hetzner-2-13-test-2-13-hcloud-3-9-1-default-i" +hcloud_prefix: "tests" + +# Used to namespace resources created by concurrent test pipelines/targets +hcloud_run_ns: "{{ hcloud_prefix | md5 }}" +hcloud_role_ns: "{{ role_name | split('_') | map('first') | join() }}" +hcloud_ns: "ansible-{{ hcloud_run_ns }}-{{ hcloud_role_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/defaults/main/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/defaults/main/main.yml new file mode 100644 index 000000000..e243ee062 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/defaults/main/main.yml @@ -0,0 +1,4 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +hcloud_volume_name: "{{ hcloud_ns }}" diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/cleanup.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/cleanup.yml new file mode 100644 index 000000000..8fa589a43 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/cleanup.yml @@ -0,0 +1,5 @@ +--- +- name: Cleanup test_volume + hetzner.hcloud.volume: + name: "{{ hcloud_volume_name }}" + state: absent diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/main.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/main.yml new file mode 100644 index 000000000..767fc465b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/main.yml @@ -0,0 +1,31 @@ +# +# DO NOT EDIT THIS FILE! Please edit the files in tests/integration/common instead. +# +--- +- name: Check if cleanup.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/cleanup.yml" + register: cleanup_file + +- name: Check if prepare.yml exists + ansible.builtin.stat: + path: "{{ role_path }}/tasks/prepare.yml" + register: prepare_file + +- name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists + +- name: Include prepare tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/prepare.yml" + when: prepare_file.stat.exists + +- name: Run tests + block: + - name: Include test tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/test.yml" + + always: + - name: Include cleanup tasks + ansible.builtin.include_tasks: "{{ role_path }}/tasks/cleanup.yml" + when: cleanup_file.stat.exists diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/prepare.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/prepare.yml new file mode 100644 index 000000000..de3c70b42 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/prepare.yml @@ -0,0 +1,9 @@ +--- +- name: Create test_volume + hetzner.hcloud.volume: + name: "{{ hcloud_volume_name }}" + size: 10 + location: fsn1 + labels: + key: value + register: test_volume diff --git a/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/test.yml b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/test.yml new file mode 100644 index 000000000..79912029b --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/integration/targets/volume_info/tasks/test.yml @@ -0,0 +1,81 @@ +# Copyright: (c) 2019, Hetzner Cloud GmbH <info@hetzner-cloud.de> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather hcloud_volume_info + hetzner.hcloud.volume_info: + register: result +- name: Verify hcloud_volume_info + ansible.builtin.assert: + that: + - result.hcloud_volume_info | list | count >= 1 + +- name: Gather hcloud_volume_info in check mode + hetzner.hcloud.volume_info: + check_mode: true + register: result +- name: Verify hcloud_volume_info in check mode + ansible.builtin.assert: + that: + - result.hcloud_volume_info | list | count >= 1 + +- name: Gather hcloud_volume_info with correct id + hetzner.hcloud.volume_info: + id: "{{ test_volume.hcloud_volume.id }}" + register: result +- name: Verify hcloud_volume_info with correct id + ansible.builtin.assert: + that: + - result.hcloud_volume_info | list | count == 1 + - result.hcloud_volume_info[0].name == hcloud_volume_name + - result.hcloud_volume_info[0].location == 'fsn1' + - result.hcloud_volume_info[0].size == 10 + - result.hcloud_volume_info[0].linux_device is defined + +- name: Gather hcloud_volume_info with wrong id + hetzner.hcloud.volume_info: + id: "{{ test_volume.hcloud_volume.id }}4321" + ignore_errors: true + register: result +- name: Verify hcloud_volume_info with wrong id + ansible.builtin.assert: + that: + - result is failed + +- name: Gather hcloud_volume_info with correct name + hetzner.hcloud.volume_info: + name: "{{ hcloud_volume_name }}" + register: result +- name: Verify hcloud_volume_info with correct name + ansible.builtin.assert: + that: + - result.hcloud_volume_info | list | count == 1 + +- name: Gather hcloud_volume_info with wrong name + hetzner.hcloud.volume_info: + name: "{{ hcloud_volume_name }}-invalid" + register: result +- name: Verify hcloud_volume_info with wrong name + ansible.builtin.assert: + that: + - result.hcloud_volume_info | list | count == 0 + +- name: Gather hcloud_volume_info with correct label selector + hetzner.hcloud.volume_info: + label_selector: "key=value" + register: result +- name: Verify hcloud_volume_info with correct label selector + ansible.builtin.assert: + that: + - > + result.hcloud_volume_info + | selectattr('name', 'equalto', hcloud_volume_name) + | list | count == 1 + +- name: Gather hcloud_volume_info with wrong label selector + hetzner.hcloud.volume_info: + label_selector: "key!=value" + register: result +- name: Verify hcloud_volume_info with wrong label selector + ansible.builtin.assert: + that: + - result.hcloud_volume_info | list | count == 0 diff --git a/ansible_collections/hetzner/hcloud/tests/requirements.yml b/ansible_collections/hetzner/hcloud/tests/requirements.yml index 6c49d0d9e..3fea3f392 100644 --- a/ansible_collections/hetzner/hcloud/tests/requirements.yml +++ b/ansible_collections/hetzner/hcloud/tests/requirements.yml @@ -1,3 +1,5 @@ -integration_tests_dependencies: -- community.general -- ansible.netcommon +--- +collections: + - ansible.utils + - community.crypto + - community.general diff --git a/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.12.txt b/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.12.txt deleted file mode 100644 index caf221794..000000000 --- a/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.12.txt +++ /dev/null @@ -1,2 +0,0 @@ -tests/utils/shippable/check_matrix.py replace-urlopen -tests/utils/shippable/timing.py shebang diff --git a/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.13.txt b/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.13.txt index caf221794..185a458e3 100644 --- a/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.13.txt +++ b/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.13.txt @@ -1,2 +1,69 @@ -tests/utils/shippable/check_matrix.py replace-urlopen -tests/utils/shippable/timing.py shebang +plugins/inventory/hcloud.py validate-modules:illegal-future-imports +plugins/inventory/hcloud.py validate-modules:import-before-documentation +plugins/inventory/hcloud.py yamllint:unparsable-with-libyaml # bug in ansible-test - https://github.com/ansible/ansible/issues/82353 +plugins/modules/certificate_info.py validate-modules:illegal-future-imports +plugins/modules/certificate_info.py validate-modules:import-before-documentation +plugins/modules/certificate.py validate-modules:illegal-future-imports +plugins/modules/certificate.py validate-modules:import-before-documentation +plugins/modules/datacenter_info.py validate-modules:illegal-future-imports +plugins/modules/datacenter_info.py validate-modules:import-before-documentation +plugins/modules/firewall_info.py validate-modules:illegal-future-imports +plugins/modules/firewall_info.py validate-modules:import-before-documentation +plugins/modules/firewall_resource.py validate-modules:illegal-future-imports +plugins/modules/firewall_resource.py validate-modules:import-before-documentation +plugins/modules/firewall.py validate-modules:illegal-future-imports +plugins/modules/firewall.py validate-modules:import-before-documentation +plugins/modules/floating_ip_info.py validate-modules:illegal-future-imports +plugins/modules/floating_ip_info.py validate-modules:import-before-documentation +plugins/modules/floating_ip.py validate-modules:illegal-future-imports +plugins/modules/floating_ip.py validate-modules:import-before-documentation +plugins/modules/image_info.py validate-modules:illegal-future-imports +plugins/modules/image_info.py validate-modules:import-before-documentation +plugins/modules/iso_info.py validate-modules:illegal-future-imports +plugins/modules/iso_info.py validate-modules:import-before-documentation +plugins/modules/load_balancer_info.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_info.py validate-modules:import-before-documentation +plugins/modules/load_balancer_network.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_network.py validate-modules:import-before-documentation +plugins/modules/load_balancer_service.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_service.py validate-modules:import-before-documentation +plugins/modules/load_balancer_target.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_target.py validate-modules:import-before-documentation +plugins/modules/load_balancer_type_info.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_type_info.py validate-modules:import-before-documentation +plugins/modules/load_balancer.py validate-modules:illegal-future-imports +plugins/modules/load_balancer.py validate-modules:import-before-documentation +plugins/modules/location_info.py validate-modules:illegal-future-imports +plugins/modules/location_info.py validate-modules:import-before-documentation +plugins/modules/network_info.py validate-modules:illegal-future-imports +plugins/modules/network_info.py validate-modules:import-before-documentation +plugins/modules/network.py validate-modules:illegal-future-imports +plugins/modules/network.py validate-modules:import-before-documentation +plugins/modules/placement_group.py validate-modules:illegal-future-imports +plugins/modules/placement_group.py validate-modules:import-before-documentation +plugins/modules/primary_ip_info.py validate-modules:illegal-future-imports +plugins/modules/primary_ip_info.py validate-modules:import-before-documentation +plugins/modules/primary_ip.py validate-modules:illegal-future-imports +plugins/modules/primary_ip.py validate-modules:import-before-documentation +plugins/modules/rdns.py validate-modules:illegal-future-imports +plugins/modules/rdns.py validate-modules:import-before-documentation +plugins/modules/route.py validate-modules:illegal-future-imports +plugins/modules/route.py validate-modules:import-before-documentation +plugins/modules/server_info.py validate-modules:illegal-future-imports +plugins/modules/server_info.py validate-modules:import-before-documentation +plugins/modules/server_network.py validate-modules:illegal-future-imports +plugins/modules/server_network.py validate-modules:import-before-documentation +plugins/modules/server_type_info.py validate-modules:illegal-future-imports +plugins/modules/server_type_info.py validate-modules:import-before-documentation +plugins/modules/server.py validate-modules:illegal-future-imports +plugins/modules/server.py validate-modules:import-before-documentation +plugins/modules/ssh_key_info.py validate-modules:illegal-future-imports +plugins/modules/ssh_key_info.py validate-modules:import-before-documentation +plugins/modules/ssh_key.py validate-modules:illegal-future-imports +plugins/modules/ssh_key.py validate-modules:import-before-documentation +plugins/modules/subnetwork.py validate-modules:illegal-future-imports +plugins/modules/subnetwork.py validate-modules:import-before-documentation +plugins/modules/volume_info.py validate-modules:illegal-future-imports +plugins/modules/volume_info.py validate-modules:import-before-documentation +plugins/modules/volume.py validate-modules:illegal-future-imports +plugins/modules/volume.py validate-modules:import-before-documentation diff --git a/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.14.txt b/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.14.txt index caf221794..e0d8362f4 100644 --- a/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.14.txt +++ b/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.14.txt @@ -1,2 +1,68 @@ -tests/utils/shippable/check_matrix.py replace-urlopen -tests/utils/shippable/timing.py shebang +plugins/inventory/hcloud.py validate-modules:illegal-future-imports +plugins/inventory/hcloud.py yamllint:unparsable-with-libyaml # bug in ansible-test - https://github.com/ansible/ansible/issues/82353 +plugins/modules/certificate_info.py validate-modules:illegal-future-imports +plugins/modules/certificate_info.py validate-modules:import-before-documentation +plugins/modules/certificate.py validate-modules:illegal-future-imports +plugins/modules/certificate.py validate-modules:import-before-documentation +plugins/modules/datacenter_info.py validate-modules:illegal-future-imports +plugins/modules/datacenter_info.py validate-modules:import-before-documentation +plugins/modules/firewall_info.py validate-modules:illegal-future-imports +plugins/modules/firewall_info.py validate-modules:import-before-documentation +plugins/modules/firewall_resource.py validate-modules:illegal-future-imports +plugins/modules/firewall_resource.py validate-modules:import-before-documentation +plugins/modules/firewall.py validate-modules:illegal-future-imports +plugins/modules/firewall.py validate-modules:import-before-documentation +plugins/modules/floating_ip_info.py validate-modules:illegal-future-imports +plugins/modules/floating_ip_info.py validate-modules:import-before-documentation +plugins/modules/floating_ip.py validate-modules:illegal-future-imports +plugins/modules/floating_ip.py validate-modules:import-before-documentation +plugins/modules/image_info.py validate-modules:illegal-future-imports +plugins/modules/image_info.py validate-modules:import-before-documentation +plugins/modules/iso_info.py validate-modules:illegal-future-imports +plugins/modules/iso_info.py validate-modules:import-before-documentation +plugins/modules/load_balancer_info.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_info.py validate-modules:import-before-documentation +plugins/modules/load_balancer_network.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_network.py validate-modules:import-before-documentation +plugins/modules/load_balancer_service.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_service.py validate-modules:import-before-documentation +plugins/modules/load_balancer_target.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_target.py validate-modules:import-before-documentation +plugins/modules/load_balancer_type_info.py validate-modules:illegal-future-imports +plugins/modules/load_balancer_type_info.py validate-modules:import-before-documentation +plugins/modules/load_balancer.py validate-modules:illegal-future-imports +plugins/modules/load_balancer.py validate-modules:import-before-documentation +plugins/modules/location_info.py validate-modules:illegal-future-imports +plugins/modules/location_info.py validate-modules:import-before-documentation +plugins/modules/network_info.py validate-modules:illegal-future-imports +plugins/modules/network_info.py validate-modules:import-before-documentation +plugins/modules/network.py validate-modules:illegal-future-imports +plugins/modules/network.py validate-modules:import-before-documentation +plugins/modules/placement_group.py validate-modules:illegal-future-imports +plugins/modules/placement_group.py validate-modules:import-before-documentation +plugins/modules/primary_ip_info.py validate-modules:illegal-future-imports +plugins/modules/primary_ip_info.py validate-modules:import-before-documentation +plugins/modules/primary_ip.py validate-modules:illegal-future-imports +plugins/modules/primary_ip.py validate-modules:import-before-documentation +plugins/modules/rdns.py validate-modules:illegal-future-imports +plugins/modules/rdns.py validate-modules:import-before-documentation +plugins/modules/route.py validate-modules:illegal-future-imports +plugins/modules/route.py validate-modules:import-before-documentation +plugins/modules/server_info.py validate-modules:illegal-future-imports +plugins/modules/server_info.py validate-modules:import-before-documentation +plugins/modules/server_network.py validate-modules:illegal-future-imports +plugins/modules/server_network.py validate-modules:import-before-documentation +plugins/modules/server_type_info.py validate-modules:illegal-future-imports +plugins/modules/server_type_info.py validate-modules:import-before-documentation +plugins/modules/server.py validate-modules:illegal-future-imports +plugins/modules/server.py validate-modules:import-before-documentation +plugins/modules/ssh_key_info.py validate-modules:illegal-future-imports +plugins/modules/ssh_key_info.py validate-modules:import-before-documentation +plugins/modules/ssh_key.py validate-modules:illegal-future-imports +plugins/modules/ssh_key.py validate-modules:import-before-documentation +plugins/modules/subnetwork.py validate-modules:illegal-future-imports +plugins/modules/subnetwork.py validate-modules:import-before-documentation +plugins/modules/volume_info.py validate-modules:illegal-future-imports +plugins/modules/volume_info.py validate-modules:import-before-documentation +plugins/modules/volume.py validate-modules:illegal-future-imports +plugins/modules/volume.py validate-modules:import-before-documentation diff --git a/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.15.txt b/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.15.txt index caf221794..d6cabf4c0 100644 --- a/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.15.txt +++ b/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.15.txt @@ -1,2 +1 @@ -tests/utils/shippable/check_matrix.py replace-urlopen -tests/utils/shippable/timing.py shebang +plugins/inventory/hcloud.py yamllint:unparsable-with-libyaml # bug in ansible-test - https://github.com/ansible/ansible/issues/82353 diff --git a/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.16.txt b/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.16.txt new file mode 100644 index 000000000..d6cabf4c0 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/sanity/ignore-2.16.txt @@ -0,0 +1 @@ +plugins/inventory/hcloud.py yamllint:unparsable-with-libyaml # bug in ansible-test - https://github.com/ansible/ansible/issues/82353 diff --git a/ansible_collections/hetzner/hcloud/tests/unit/module_utils/test_hcloud.py b/ansible_collections/hetzner/hcloud/tests/unit/module_utils/test_hcloud.py new file mode 100644 index 000000000..c1a9ffb77 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/unit/module_utils/test_hcloud.py @@ -0,0 +1,125 @@ +from __future__ import annotations + +import traceback +from datetime import datetime, timezone +from unittest.mock import MagicMock + +from ansible_collections.hetzner.hcloud.plugins.module_utils.hcloud import AnsibleHCloud +from ansible_collections.hetzner.hcloud.plugins.module_utils.vendor.hcloud import ( + APIException, +) +from ansible_collections.hetzner.hcloud.plugins.module_utils.vendor.hcloud.actions import ( + Action, + ActionException, + ActionFailedException, + ActionTimeoutException, +) + + +def test_hcloud_fail_json_hcloud(): + module = MagicMock() + module.params = { + "api_token": "fake_token", + "api_endpoint": "https://api.hetzner.cloud/v1", + } + AnsibleHCloud.represent = "hcloud_test" + hcloud = AnsibleHCloud(module) + + try: + raise APIException( + code="invalid_input", + message="invalid input in fields 'server', 'home_location'", + details={ + "fields": [ + {"messages": ["either server or home_location must be provided"], "name": "server"}, + {"messages": ["either server or home_location must be provided"], "name": "home_location"}, + ] + }, + ) + except APIException as exception: + hcloud.fail_json_hcloud(exception) + + module.fail_json.assert_called_with( + msg="invalid input in fields 'server', 'home_location'", + exception=traceback.format_exc(), + failure={ + "message": "invalid input in fields 'server', 'home_location'", + "code": "invalid_input", + "details": { + "fields": [ + {"messages": ["either server or home_location must be provided"], "name": "server"}, + {"messages": ["either server or home_location must be provided"], "name": "home_location"}, + ] + }, + }, + ) + + try: + raise ActionFailedException( + action=Action( + **{ + "id": 1084730887, + "command": "change_server_type", + "status": "error", + "progress": 100, + "resources": [{"id": 34574042, "type": "server"}], + "error": {"code": "server_does_not_exist_anymore", "message": "Server does not exist anymore"}, + "started": "2023-07-06T14:52:42+00:00", + "finished": "2023-07-06T14:53:08+00:00", + } + ) + ) + except ActionException as exception: + hcloud.fail_json_hcloud(exception) + + module.fail_json.assert_called_with( + msg="The pending action failed: Server does not exist anymore", + exception=traceback.format_exc(), + failure={ + "action": { + "id": 1084730887, + "command": "change_server_type", + "status": "error", + "progress": 100, + "resources": [{"id": 34574042, "type": "server"}], + "error": {"code": "server_does_not_exist_anymore", "message": "Server does not exist anymore"}, + "started": datetime(2023, 7, 6, 14, 52, 42, tzinfo=timezone.utc), + "finished": datetime(2023, 7, 6, 14, 53, 8, tzinfo=timezone.utc), + } + }, + ) + + try: + raise ActionTimeoutException( + action=Action( + **{ + "id": 1084659545, + "command": "create_server", + "status": "running", + "progress": 50, + "started": "2023-07-06T13:58:38+00:00", + "finished": None, + "resources": [{"id": 34572291, "type": "server"}], + "error": None, + } + ) + ) + except ActionException as exception: + hcloud.fail_json_hcloud(exception) + + module.fail_json.assert_called_with( + msg="The pending action timed out", + exception=traceback.format_exc(), + failure={ + "action": { + "id": 1084659545, + "command": "create_server", + "status": "running", + "progress": 50, + "resources": [{"id": 34572291, "type": "server"}], + "error": None, + "started": datetime(2023, 7, 6, 13, 58, 38, tzinfo=timezone.utc), + "finished": None, + } + }, + ) diff --git a/ansible_collections/hetzner/hcloud/tests/unit/requirements.txt b/ansible_collections/hetzner/hcloud/tests/unit/requirements.txt new file mode 100644 index 000000000..bc314c3c2 --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/unit/requirements.txt @@ -0,0 +1,2 @@ +python-dateutil +requests diff --git a/ansible_collections/hetzner/hcloud/tests/utils/ci.sh b/ansible_collections/hetzner/hcloud/tests/utils/ci.sh new file mode 100755 index 000000000..e5d73912a --- /dev/null +++ b/ansible_collections/hetzner/hcloud/tests/utils/ci.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash + +set -o pipefail -eux + +error() { + echo >&2 "error: $*" + exit 1 +} + +retry() { + local exit_code=1 + + for _ in 1 2 3; do + set +e + "$@" + exit_code=$? + set -e + if [ $exit_code == 0 ]; then + return $exit_code + fi + done + + echo "Command '$*' failed 3 times!" + exit $exit_code +} + +declare -a entry_point_args +IFS='/:' read -ra entry_point_args <<< "$1" + +# Explode entry point args, for example '2.16/integration/3.10/2' or '2.16/sanity' +ansible_version="${entry_point_args[0]}" +test_name="${entry_point_args[1]}" +python_version="${entry_point_args[2]:-}" +test_group="${entry_point_args[3]:-}" + +export PYTHONIOENCODING="utf-8" +export PIP_DISABLE_PIP_VERSION_CHECK=true +export PIP_NO_WARN_SCRIPT_LOCATION=false # Negative options are a bit weird: https://pip.pypa.io/en/stable/topics/configuration/#boolean-options +export ANSIBLE_COLLECTIONS_PATH="$PWD/../.." + +command -v python +python -V + +command -v pip +pip --version +pip list + +if [ "$ansible_version" == "devel" ]; then + pip install "https://github.com/ansible/ansible/archive/devel.tar.gz" +else + pip install "https://github.com/ansible/ansible/archive/stable-$ansible_version.tar.gz" +fi +command -v ansible +ansible --version + +# Prepare coverage args +if $COVERAGE; then + coverage_args="--coverage" +elif [[ "$COMMIT_MESSAGE" =~ ci_coverage ]]; then + coverage_args="--coverage" +else + coverage_args="--coverage-check" +fi + +# Prepare changed args +if $COMPLETE; then + changed_args="" +elif [[ "$COMMIT_MESSAGE" =~ ci_complete ]]; then + changed_args="" +else + changed_args="--changed" +fi + +# Prepare unstable args +if $IS_PULL_REQUEST; then + unstable_args="--allow-unstable-changed" +else + unstable_args="" +fi + +# Install dependencies +pip install rstcheck + +# Ensure we can write other collections to this dir +sudo chown "$(whoami)" "$ANSIBLE_COLLECTIONS_PATH" + +pip install -r tests/integration/requirements.txt -c tests/constraints.txt +ansible-galaxy -vvv collection install -r tests/requirements.yml + +# Dump env and set timeout +timeout=45 +if $COVERAGE; then + timeout=60 +fi + +ansible-test env --color -v --dump --show --timeout "$timeout" + +# Run tests +case "$test_name" in + sanity) + # shellcheck disable=SC2086 + ansible-test sanity --color -v \ + --exclude plugins/module_utils/vendor/ \ + --exclude scripts/ \ + --exclude tests/utils/ \ + --docker default \ + --junit \ + $coverage_args \ + $changed_args \ + --allow-disabled + ;; + + units) + # shellcheck disable=SC2086 + ansible-test units --color -v \ + --docker default \ + --python "$python_version" \ + $coverage_args \ + $changed_args + ;; + + integration) + # shellcheck disable=SC2086 + ansible-test integration --color -v \ + --remote-terminate always \ + --remote-stage prod \ + --docker default \ + --python "$python_version" \ + --retry-on-error \ + $coverage_args \ + $changed_args \ + --changed-all-target none \ + --changed-all-mode include \ + $unstable_args \ + "azp/group$test_group/" + ;; + + *) + error "found invalid test_name: $test_name" + ;; +esac diff --git a/ansible_collections/hetzner/hcloud/tests/utils/gitlab/gitlab.sh b/ansible_collections/hetzner/hcloud/tests/utils/gitlab/gitlab.sh index b09bd2f3a..a96ad2f4c 100755 --- a/ansible_collections/hetzner/hcloud/tests/utils/gitlab/gitlab.sh +++ b/ansible_collections/hetzner/hcloud/tests/utils/gitlab/gitlab.sh @@ -11,77 +11,73 @@ ansible_version="${args[0]}" script="${args[1]}" function join { - local IFS="$1"; - shift; - echo "$*"; + local IFS="$1" + shift + echo "$*" } test="$(join / "${args[@]:1}")" command -v python python -V -function retry -{ - # shellcheck disable=SC2034 - for repetition in 1 2 3; do - set +e - "$@" - result=$? - set -e - if [ ${result} == 0 ]; then - return ${result} - fi - echo "@* -> ${result}" - done - echo "Command '@*' failed 3 times!" - exit 1 +function retry { + # shellcheck disable=SC2034 + for repetition in 1 2 3; do + set +e + "$@" + result=$? + set -e + if [ ${result} == 0 ]; then + return ${result} + fi + echo "@* -> ${result}" + done + echo "Command '@*' failed 3 times!" + exit 1 } command -v pip pip --version pip list --disable-pip-version-check if [ "${ansible_version}" == "devel" ]; then - retry pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check + retry pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check else - retry pip install "https://github.com/ansible/ansible/archive/stable-${ansible_version}.tar.gz" --disable-pip-version-check + retry pip install "https://github.com/ansible/ansible/archive/stable-${ansible_version}.tar.gz" --disable-pip-version-check fi -export ANSIBLE_COLLECTIONS_PATHS="${HOME}/.ansible" +export ANSIBLE_COLLECTIONS_PATH="${HOME}/.ansible" # shellcheck disable=SC2034 SHIPPABLE_RESULT_DIR="$(pwd)/shippable" -TEST_DIR="${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/hetzner/hcloud" +TEST_DIR="${ANSIBLE_COLLECTIONS_PATH}/ansible_collections/hetzner/hcloud" rm -rf "${TEST_DIR}" mkdir -p "${TEST_DIR}" cp -r "." "${TEST_DIR}" cd "${TEST_DIR}" # STAR: HACK install dependencies -retry ansible-galaxy -vvv collection install community.general -retry ansible-galaxy -vvv collection install ansible.netcommon -retry ansible-galaxy -vvv collection install community.internal_test_tools -retry pip install netaddr --disable-pip-version-check -retry pip install hcloud +retry pip install -r tests/integration/requirements.txt -c tests/integration/constraints.txt +retry ansible-galaxy -vvv collection install -r tests/requirements.yml + retry pip install rstcheck +retry ansible-galaxy -vvv collection install community.internal_test_tools # END: HACK export PYTHONIOENCODING='utf-8' if [ "${JOB_TRIGGERED_BY_NAME:-}" == "nightly-trigger" ]; then - COMPLETE=yes + COMPLETE=yes fi - if [ -n "${COMPLETE:-}" ]; then - # disable change detection triggered by setting the COMPLETE environment variable to a non-empty value - export CHANGED="" + # disable change detection triggered by setting the COMPLETE environment variable to a non-empty value + export CHANGED="" elif [[ "${CI_COMMIT_MESSAGE}" =~ ci_complete ]]; then - # disable change detection triggered by having 'ci_complete' in the latest commit message - export CHANGED="" + # disable change detection triggered by having 'ci_complete' in the latest commit message + export CHANGED="" else - # enable change detection (default behavior) - export CHANGED="" + # enable change detection (default behavior) + export CHANGED="" fi - export UNSTABLE="--allow-unstable-changed" # remove empty core/extras module directories from PRs created prior to the repo-merge diff --git a/ansible_collections/hetzner/hcloud/tests/utils/gitlab/sanity.sh b/ansible_collections/hetzner/hcloud/tests/utils/gitlab/sanity.sh index 4ee96aefe..9f6711b05 100755 --- a/ansible_collections/hetzner/hcloud/tests/utils/gitlab/sanity.sh +++ b/ansible_collections/hetzner/hcloud/tests/utils/gitlab/sanity.sh @@ -8,32 +8,32 @@ IFS='/:' read -ra args <<< "$1" group="${args[1]}" if [ "${BASE_BRANCH:-}" ]; then - base_branch="origin/${BASE_BRANCH}" + base_branch="origin/${BASE_BRANCH}" else - base_branch="" + base_branch="" fi if [ "${group}" == "extra" ]; then - ../internal_test_tools/tools/run.py --color - exit + ../internal_test_tools/tools/run.py --color + exit fi case "${group}" in - 1) options=(--skip-test pylint --skip-test ansible-doc --skip-test validate-modules) ;; - 2) options=( --test ansible-doc --test validate-modules) ;; - 3) options=(--test pylint plugins/modules/) ;; - 4) options=(--test pylint --exclude plugins/modules/) ;; + 1) options=(--skip-test pylint --skip-test ansible-doc --skip-test validate-modules) ;; + 2) options=(--test ansible-doc --test validate-modules) ;; + 3) options=(--test pylint plugins/modules/) ;; + 4) options=(--test pylint --exclude plugins/modules/) ;; esac # allow collection migration sanity tests for groups 3 and 4 to pass without updating this script during migration network_path="lib/ansible/modules/network/" if [ -d "${network_path}" ]; then - if [ "${group}" -eq 3 ]; then - options+=(--exclude "${network_path}") - elif [ "${group}" -eq 4 ]; then - options+=("${network_path}") - fi + if [ "${group}" -eq 3 ]; then + options+=(--exclude "${network_path}") + elif [ "${group}" -eq 4 ]; then + options+=("${network_path}") + fi fi pip install pycodestyle @@ -42,6 +42,8 @@ pip install voluptuous pip install pylint==2.5.3 # shellcheck disable=SC2086 ansible-test sanity --color -v --junit ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \ - --base-branch "${base_branch}" \ - --exclude tests/utils/ \ - "${options[@]}" --allow-disabled + --base-branch "${base_branch}" \ + --exclude plugins/module_utils/vendor/ \ + --exclude scripts/ \ + --exclude tests/utils/ \ + "${options[@]}" --allow-disabled diff --git a/ansible_collections/hetzner/hcloud/tests/utils/shippable/check_matrix.py b/ansible_collections/hetzner/hcloud/tests/utils/shippable/check_matrix.py deleted file mode 100755 index dfcca3e6d..000000000 --- a/ansible_collections/hetzner/hcloud/tests/utils/shippable/check_matrix.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python -"""Verify the currently executing Shippable test matrix matches the one defined in the "shippable.yml" file.""" -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import datetime -import json -import os -import re -import sys -import time - -try: - from typing import NoReturn -except ImportError: - NoReturn = None - -try: - # noinspection PyCompatibility - from urllib2 import urlopen # pylint: disable=ansible-bad-import-from -except ImportError: - # noinspection PyCompatibility - from urllib.request import urlopen - - -def main(): # type: () -> None - """Main entry point.""" - repo_full_name = os.environ['REPO_FULL_NAME'] - required_repo_full_name = 'ansible-collections/hetzner.hcloud' - - if repo_full_name != required_repo_full_name: - sys.stderr.write('Skipping matrix check on repo "%s" which is not "%s".\n' % (repo_full_name, required_repo_full_name)) - return - - with open('shippable.yml', 'rb') as yaml_file: - yaml = yaml_file.read().decode('utf-8').splitlines() - - defined_matrix = [match.group(1) for match in [re.search(r'^ *- env: T=(.*)$', line) for line in yaml] if match and match.group(1) != 'none'] - - if not defined_matrix: - fail('No matrix entries found in the "shippable.yml" file.', - 'Did you modify the "shippable.yml" file?') - - run_id = os.environ['SHIPPABLE_BUILD_ID'] - sleep = 1 - jobs = [] - - for attempts_remaining in range(4, -1, -1): - try: - jobs = json.loads(urlopen('https://api.shippable.com/jobs?runIds=%s' % run_id).read()) - - if not isinstance(jobs, list): - raise Exception('Shippable run %s data is not a list.' % run_id) - - break - except Exception as ex: - if not attempts_remaining: - fail('Unable to retrieve Shippable run %s matrix.' % run_id, - str(ex)) - - sys.stderr.write('Unable to retrieve Shippable run %s matrix: %s\n' % (run_id, ex)) - sys.stderr.write('Trying again in %d seconds...\n' % sleep) - time.sleep(sleep) - sleep *= 2 - - if len(jobs) != len(defined_matrix): - if len(jobs) == 1: - hint = '\n\nMake sure you do not use the "Rebuild with SSH" option.' - else: - hint = '' - - fail('Shippable run %s has %d jobs instead of the expected %d jobs.' % (run_id, len(jobs), len(defined_matrix)), - 'Try re-running the entire matrix.%s' % hint) - - actual_matrix = dict((job.get('jobNumber'), dict(tuple(line.split('=', 1)) for line in job.get('env', [])).get('T', '')) for job in jobs) - errors = [(job_number, test, actual_matrix.get(job_number)) for job_number, test in enumerate(defined_matrix, 1) if actual_matrix.get(job_number) != test] - - if len(errors): - error_summary = '\n'.join('Job %s expected "%s" but found "%s" instead.' % (job_number, expected, actual) for job_number, expected, actual in errors) - - fail('Shippable run %s has a job matrix mismatch.' % run_id, - 'Try re-running the entire matrix.\n\n%s' % error_summary) - - -def fail(message, output): # type: (str, str) -> NoReturn - # Include a leading newline to improve readability on Shippable "Tests" tab. - # Without this, the first line becomes indented. - output = '\n' + output.strip() - - timestamp = datetime.datetime.utcnow().replace(microsecond=0).isoformat() - - # hack to avoid requiring junit-xml, which isn't pre-installed on Shippable outside our test containers - xml = ''' -<?xml version="1.0" encoding="utf-8"?> -<testsuites disabled="0" errors="1" failures="0" tests="1" time="0.0"> -\t<testsuite disabled="0" errors="1" failures="0" file="None" log="None" name="ansible-test" skipped="0" tests="1" time="0" timestamp="%s" url="None"> -\t\t<testcase classname="timeout" name="timeout"> -\t\t\t<error message="%s" type="error">%s</error> -\t\t</testcase> -\t</testsuite> -</testsuites> -''' % (timestamp, message, output) - - path = 'shippable/testresults/check-matrix.xml' - dir_path = os.path.dirname(path) - - if not os.path.exists(dir_path): - os.makedirs(dir_path) - - with open(path, 'w') as junit_fd: - junit_fd.write(xml.lstrip()) - - sys.stderr.write(message + '\n') - sys.stderr.write(output + '\n') - - sys.exit(1) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/hetzner/hcloud/tests/utils/shippable/hcloud.sh b/ansible_collections/hetzner/hcloud/tests/utils/shippable/hcloud.sh deleted file mode 100755 index da037e09e..000000000 --- a/ansible_collections/hetzner/hcloud/tests/utils/shippable/hcloud.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -set -o pipefail -eux - -declare -a args -IFS='/:' read -ra args <<< "$1" - -cloud="${args[0]}" -python="${args[1]}" -group="${args[2]}" - -target="shippable/${cloud}/group${group}/" - -stage="${S:-prod}" - -changed_all_target="shippable/${cloud}/smoketest/" - -if ! ansible-test integration "${changed_all_target}" --list-targets > /dev/null 2>&1; then - # no smoketest tests are available for this cloud - changed_all_target="none" -fi - -if [ "${group}" == "1" ]; then - # only run smoketest tests for group1 - changed_all_mode="include" -else - # smoketest tests already covered by group1 - changed_all_mode="exclude" -fi - -# shellcheck disable=SC2086 -ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ - --remote-terminate always --remote-stage "${stage}" \ - --docker --python "${python}" --changed-all-target "${changed_all_target}" --changed-all-mode "${changed_all_mode}" diff --git a/ansible_collections/hetzner/hcloud/tests/utils/shippable/sanity.sh b/ansible_collections/hetzner/hcloud/tests/utils/shippable/sanity.sh deleted file mode 100755 index 9339aeda4..000000000 --- a/ansible_collections/hetzner/hcloud/tests/utils/shippable/sanity.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -set -o pipefail -eux - -declare -a args -IFS='/:' read -ra args <<< "$1" - -group="${args[1]}" - -if [ "${BASE_BRANCH:-}" ]; then - base_branch="origin/${BASE_BRANCH}" -else - base_branch="" -fi - -if [ "${group}" == "extra" ]; then - # ansible-galaxy -vvv collection install community.internal_test_tools - git clone --single-branch --depth 1 https://github.com/ansible-collections/community.internal_test_tools.git ../../community/internal_test_tools - - ../internal_test_tools/tools/run.py --color - exit -fi - -# shellcheck disable=SC2086 -ansible-test sanity --color -v --junit ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \ - --docker --base-branch "${base_branch}" \ - --allow-disabled diff --git a/ansible_collections/hetzner/hcloud/tests/utils/shippable/shippable.sh b/ansible_collections/hetzner/hcloud/tests/utils/shippable/shippable.sh deleted file mode 100755 index 8c0bd6deb..000000000 --- a/ansible_collections/hetzner/hcloud/tests/utils/shippable/shippable.sh +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/env bash - -set -o pipefail -eux - -declare -a args -IFS='/:' read -ra args <<< "$1" - -ansible_version="${args[0]}" -script="${args[1]}" - -function join { - local IFS="$1"; - shift; - echo "$*"; -} - -# Ensure we can write other collections to this dir -sudo chown "$(whoami)" "${PWD}/../../" - -test="$(join / "${args[@]:1}")" - -docker images ansible/ansible -docker images quay.io/ansible/* -docker ps - -for container in $(docker ps --format '{{.Image}} {{.ID}}' | grep -v -e '^drydock/' -e '^quay.io/ansible/azure-pipelines-test-container:' | sed 's/^.* //'); do - docker rm -f "${container}" || true # ignore errors -done - -docker ps - -if [ -d /home/shippable/cache/ ]; then - ls -la /home/shippable/cache/ -fi - -command -v python -python -V - -function retry -{ - # shellcheck disable=SC2034 - for repetition in 1 2 3; do - set +e - "$@" - result=$? - set -e - if [ ${result} == 0 ]; then - return ${result} - fi - echo "@* -> ${result}" - done - echo "Command '@*' failed 3 times!" - exit 1 -} - -command -v pip -pip --version -pip list --disable-pip-version-check -if [ "${ansible_version}" == "devel" ]; then - retry pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check -else - retry pip install "https://github.com/ansible/ansible/archive/stable-${ansible_version}.tar.gz" --disable-pip-version-check -fi - -if [ "${SHIPPABLE_BUILD_ID:-}" ]; then - export ANSIBLE_COLLECTIONS_PATHS="${HOME}/.ansible" - SHIPPABLE_RESULT_DIR="$(pwd)/shippable" - TEST_DIR="${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/hetzner/hcloud" - mkdir -p "${TEST_DIR}" - cp -aT "${SHIPPABLE_BUILD_DIR}" "${TEST_DIR}" - cd "${TEST_DIR}" -else - export ANSIBLE_COLLECTIONS_PATHS="${PWD}/../../../" -fi - -# STAR: HACK install dependencies -retry ansible-galaxy -vvv collection install community.general -retry ansible-galaxy -vvv collection install ansible.netcommon - -retry pip install hcloud -retry pip install netaddr --disable-pip-version-check -retry ansible-galaxy -vvv collection install community.internal_test_tools -# END: HACK - -export PYTHONIOENCODING='utf-8' - -if [ "${JOB_TRIGGERED_BY_NAME:-}" == "nightly-trigger" ]; then - COVERAGE=yes - COMPLETE=yes -fi - -if [ -n "${COVERAGE:-}" ]; then - # on-demand coverage reporting triggered by setting the COVERAGE environment variable to a non-empty value - export COVERAGE="--coverage" -elif [[ "${COMMIT_MESSAGE}" =~ ci_coverage ]]; then - # on-demand coverage reporting triggered by having 'ci_coverage' in the latest commit message - export COVERAGE="--coverage" -else - # on-demand coverage reporting disabled (default behavior, always-on coverage reporting remains enabled) - export COVERAGE="--coverage-check" -fi - -if [ -n "${COMPLETE:-}" ]; then - # disable change detection triggered by setting the COMPLETE environment variable to a non-empty value - export CHANGED="" -elif [[ "${COMMIT_MESSAGE}" =~ ci_complete ]]; then - # disable change detection triggered by having 'ci_complete' in the latest commit message - export CHANGED="" -else - # enable change detection (default behavior) - export CHANGED="--changed" -fi - -if [ "${IS_PULL_REQUEST:-}" == "true" ]; then - # run unstable tests which are targeted by focused changes on PRs - export UNSTABLE="--allow-unstable-changed" -else - # do not run unstable tests outside PRs - export UNSTABLE="" -fi - -# remove empty core/extras module directories from PRs created prior to the repo-merge -find plugins -type d -empty -print -delete - -function cleanup -{ - # for complete on-demand coverage generate a report for all files with no coverage on the "sanity/5" job so we only have one copy - if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ] && [ "${test}" == "sanity/5" ]; then - stub="--stub" - # trigger coverage reporting for stubs even if no other coverage data exists - mkdir -p tests/output/coverage/ - else - stub="" - fi - - if [ -d tests/output/coverage/ ]; then - if find tests/output/coverage/ -mindepth 1 -name '.*' -prune -o -print -quit | grep -q .; then - process_coverage='yes' # process existing coverage files - elif [ "${stub}" ]; then - process_coverage='yes' # process coverage when stubs are enabled - else - process_coverage='' - fi - - if [ "${process_coverage}" ]; then - # use python 3.7 for coverage to avoid running out of memory during coverage xml processing - # only use it for coverage to avoid the additional overhead of setting up a virtual environment for a potential no-op job - virtualenv --python /usr/bin/python3.7 ~/ansible-venv - set +ux - . ~/ansible-venv/bin/activate - set -ux - - # shellcheck disable=SC2086 - ansible-test coverage xml --color -v --requirements --group-by command --group-by version ${stub:+"$stub"} - cp -a tests/output/reports/coverage=*.xml "$SHIPPABLE_RESULT_DIR/codecoverage/" - - if [ "${ansible_version}" != "2.9" ]; then - # analyze and capture code coverage aggregated by integration test target - ansible-test coverage analyze targets generate -v "$SHIPPABLE_RESULT_DIR/testresults/coverage-analyze-targets.json" - fi - - # upload coverage report to codecov.io only when using complete on-demand coverage - if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ]; then - for file in tests/output/reports/coverage=*.xml; do - flags="${file##*/coverage=}" - flags="${flags%-powershell.xml}" - flags="${flags%.xml}" - # remove numbered component from stub files when converting to tags - flags="${flags//stub-[0-9]*/stub}" - flags="${flags//=/,}" - flags="${flags//[^a-zA-Z0-9_,]/_}" - - bash <(curl -s https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh) \ - -f "${file}" \ - -F "${flags}" \ - -n "${test}" \ - -t 8a86e979-f37b-4d5d-95a4-960c280d5eaa \ - -X coveragepy \ - -X gcov \ - -X fix \ - -X search \ - -X xcode \ - || echo "Failed to upload code coverage report to codecov.io: ${file}" - done - fi - fi - fi - - if [ -d tests/output/junit/ ]; then - cp -aT tests/output/junit/ "$SHIPPABLE_RESULT_DIR/testresults/" - fi - - if [ -d tests/output/data/ ]; then - cp -a tests/output/data/ "$SHIPPABLE_RESULT_DIR/testresults/" - fi - - if [ -d tests/output/bot/ ]; then - cp -aT tests/output/bot/ "$SHIPPABLE_RESULT_DIR/testresults/" - fi -} - -if [ "${SHIPPABLE_BUILD_ID:-}" ]; then trap cleanup EXIT; fi - -if [[ "${COVERAGE:-}" == "--coverage" ]]; then - timeout=60 -else - timeout=45 -fi - -ansible-test env --dump --show --timeout "${timeout}" --color -v - -if [ "${SHIPPABLE_BUILD_ID:-}" ]; then "tests/utils/shippable/check_matrix.py"; fi -"tests/utils/shippable/${script}.sh" "${test}" diff --git a/ansible_collections/hetzner/hcloud/tests/utils/shippable/timing.py b/ansible_collections/hetzner/hcloud/tests/utils/shippable/timing.py deleted file mode 100755 index fb538271b..000000000 --- a/ansible_collections/hetzner/hcloud/tests/utils/shippable/timing.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python3.7 -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -import time - -start = time.time() - -sys.stdin.reconfigure(errors='surrogateescape') -sys.stdout.reconfigure(errors='surrogateescape') - -for line in sys.stdin: - seconds = time.time() - start - sys.stdout.write('%02d:%02d %s' % (seconds // 60, seconds % 60, line)) - sys.stdout.flush() diff --git a/ansible_collections/hetzner/hcloud/tests/utils/shippable/timing.sh b/ansible_collections/hetzner/hcloud/tests/utils/shippable/timing.sh deleted file mode 100755 index 77e257830..000000000 --- a/ansible_collections/hetzner/hcloud/tests/utils/shippable/timing.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -o pipefail -eu - -"$@" 2>&1 | "$(dirname "$0")/timing.py" |