refactor: rework ci and testing #3
@ -5,9 +5,30 @@ local PythonVersion(pyversion='3.8') = {
|
||||
PY_COLORS: 1,
|
||||
},
|
||||
commands: [
|
||||
'pip install -r dev-requirements.txt -qq',
|
||||
'pip install -r test/unit/requirements.txt -qq',
|
||||
'python -m pytest --cov --cov-append --no-cov-on-fail',
|
||||
'pip install poetry -qq',
|
||||
'poetry config experimental.new-installer false',
|
||||
'poetry install --all-extras',
|
||||
'poetry run pytest',
|
||||
],
|
||||
depends_on: [
|
||||
'clone',
|
||||
],
|
||||
};
|
||||
|
||||
local AnsibleVersion(version='devel') = {
|
||||
local gitversion = if version == 'devel' then 'devel' else 'stable-' + version,
|
||||
name: 'ansible-' + std.strReplace(version, '.', ''),
|
||||
image: 'python:3.9',
|
||||
environment: {
|
||||
PY_COLORS: 1,
|
||||
},
|
||||
commands: [
|
||||
'pip install poetry -qq',
|
||||
'poetry config experimental.new-installer false',
|
||||
'poetry install',
|
||||
'poetry run pip install https://github.com/ansible/ansible/archive/' + gitversion + '.tar.gz --disable-pip-version-check',
|
||||
'poetry run ansible --version',
|
||||
'poetry run ansible-test sanity --exclude .chglog/ --exclude .drone.yml --python 3.9',
|
||||
],
|
||||
depends_on: [
|
||||
'clone',
|
||||
@ -23,14 +44,31 @@ local PipelineLint = {
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: 'flake8',
|
||||
image: 'python:3.10',
|
||||
name: 'check-format',
|
||||
image: 'python:3.11',
|
||||
environment: {
|
||||
PY_COLORS: 1,
|
||||
},
|
||||
commands: [
|
||||
'pip install -r dev-requirements.txt -qq',
|
||||
'flake8',
|
||||
'git fetch -tq',
|
||||
'pip install poetry -qq',
|
||||
'poetry config experimental.new-installer false',
|
||||
'poetry install --all-extras',
|
||||
'poetry run yapf -dr ./plugins',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'check-coding',
|
||||
image: 'python:3.11',
|
||||
environment: {
|
||||
PY_COLORS: 1,
|
||||
},
|
||||
commands: [
|
||||
'git fetch -tq',
|
||||
'pip install poetry -qq',
|
||||
'poetry config experimental.new-installer false',
|
||||
'poetry install --all-extras',
|
||||
'poetry run ruff ./plugins',
|
||||
],
|
||||
},
|
||||
],
|
||||
@ -39,9 +77,9 @@ local PipelineLint = {
|
||||
},
|
||||
};
|
||||
|
||||
local PipelineTest = {
|
||||
local PipelineUnitTest = {
|
||||
kind: 'pipeline',
|
||||
name: 'test',
|
||||
name: 'unit-test',
|
||||
platform: {
|
||||
os: 'linux',
|
||||
arch: 'amd64',
|
||||
@ -50,6 +88,7 @@ local PipelineTest = {
|
||||
PythonVersion(pyversion='3.8'),
|
||||
PythonVersion(pyversion='3.9'),
|
||||
PythonVersion(pyversion='3.10'),
|
||||
PythonVersion(pyversion='3.11'),
|
||||
],
|
||||
depends_on: [
|
||||
'lint',
|
||||
@ -59,6 +98,29 @@ local PipelineTest = {
|
||||
},
|
||||
};
|
||||
|
||||
local PipelineSanityTest = {
|
||||
kind: 'pipeline',
|
||||
name: 'sanity-test',
|
||||
platform: {
|
||||
os: 'linux',
|
||||
arch: 'amd64',
|
||||
},
|
||||
workspace: {
|
||||
path: '/drone/src/ansible_collections/${DRONE_REPO_NAME/./\\/}',
|
||||
},
|
||||
steps: [
|
||||
AnsibleVersion(version='devel'),
|
||||
AnsibleVersion(version='2.14'),
|
||||
AnsibleVersion(version='2.13'),
|
||||
],
|
||||
depends_on: [
|
||||
'unit-test',
|
||||
],
|
||||
trigger: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
|
||||
},
|
||||
};
|
||||
|
||||
local PipelineBuild = {
|
||||
kind: 'pipeline',
|
||||
name: 'build',
|
||||
@ -69,12 +131,14 @@ local PipelineBuild = {
|
||||
steps: [
|
||||
{
|
||||
name: 'build',
|
||||
image: 'python:3.9',
|
||||
image: 'python:3.11',
|
||||
commands: [
|
||||
'GALAXY_VERSION=${DRONE_TAG##v}',
|
||||
"sed -i 's/version: 0.0.0/version: '\"$${GALAXY_VERSION:-0.0.0}\"'/g' galaxy.yml",
|
||||
'pip install ansible -qq',
|
||||
'ansible-galaxy collection build --output-path dist/',
|
||||
'pip install poetry -qq',
|
||||
'poetry config experimental.new-installer false',
|
||||
'poetry install --all-extras',
|
||||
'poetry run ansible-galaxy collection build --output-path dist/',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -117,7 +181,7 @@ local PipelineBuild = {
|
||||
},
|
||||
],
|
||||
depends_on: [
|
||||
'test',
|
||||
'sanity-test',
|
||||
],
|
||||
trigger: {
|
||||
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
|
||||
@ -188,7 +252,8 @@ local PipelineNotifications = {
|
||||
|
||||
[
|
||||
PipelineLint,
|
||||
PipelineTest,
|
||||
PipelineUnitTest,
|
||||
PipelineSanityTest,
|
||||
PipelineBuild,
|
||||
PipelineDocumentation,
|
||||
PipelineNotifications,
|
||||
|
132
.drone.yml
132
.drone.yml
@ -7,11 +7,25 @@ platform:
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: flake8
|
||||
image: python:3.10
|
||||
- name: check-format
|
||||
image: python:3.11
|
||||
commands:
|
||||
- pip install -r dev-requirements.txt -qq
|
||||
- flake8
|
||||
- git fetch -tq
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install --all-extras
|
||||
- poetry run yapf -dr ./plugins
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
|
||||
- name: check-coding
|
||||
image: python:3.11
|
||||
commands:
|
||||
- git fetch -tq
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install --all-extras
|
||||
- poetry run ruff ./plugins
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
|
||||
@ -23,7 +37,7 @@ trigger:
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: test
|
||||
name: unit-test
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
@ -33,9 +47,10 @@ steps:
|
||||
- name: python38-pytest
|
||||
image: python:3.8
|
||||
commands:
|
||||
- pip install -r dev-requirements.txt -qq
|
||||
- pip install -r test/unit/requirements.txt -qq
|
||||
- python -m pytest --cov --cov-append --no-cov-on-fail
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install --all-extras
|
||||
- poetry run pytest
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
@ -44,9 +59,10 @@ steps:
|
||||
- name: python39-pytest
|
||||
image: python:3.9
|
||||
commands:
|
||||
- pip install -r dev-requirements.txt -qq
|
||||
- pip install -r test/unit/requirements.txt -qq
|
||||
- python -m pytest --cov --cov-append --no-cov-on-fail
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install --all-extras
|
||||
- poetry run pytest
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
@ -55,9 +71,22 @@ steps:
|
||||
- name: python310-pytest
|
||||
image: python:3.10
|
||||
commands:
|
||||
- pip install -r dev-requirements.txt -qq
|
||||
- pip install -r test/unit/requirements.txt -qq
|
||||
- python -m pytest --cov --cov-append --no-cov-on-fail
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install --all-extras
|
||||
- poetry run pytest
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
- clone
|
||||
|
||||
- name: python311-pytest
|
||||
image: python:3.11
|
||||
commands:
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install --all-extras
|
||||
- poetry run pytest
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
@ -72,6 +101,69 @@ trigger:
|
||||
depends_on:
|
||||
- lint
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: sanity-test
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
workspace:
|
||||
path: /drone/src/ansible_collections/${DRONE_REPO_NAME/./\/}
|
||||
|
||||
steps:
|
||||
- name: ansible-devel
|
||||
image: python:3.9
|
||||
commands:
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry run pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
|
||||
- poetry run ansible --version
|
||||
- poetry run ansible-test sanity --exclude .chglog/ --exclude .drone.yml --python 3.9
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
- clone
|
||||
|
||||
- name: ansible-214
|
||||
image: python:3.9
|
||||
commands:
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry run pip install https://github.com/ansible/ansible/archive/stable-2.14.tar.gz --disable-pip-version-check
|
||||
- poetry run ansible --version
|
||||
- poetry run ansible-test sanity --exclude .chglog/ --exclude .drone.yml --python 3.9
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
- clone
|
||||
|
||||
- name: ansible-213
|
||||
image: python:3.9
|
||||
commands:
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install
|
||||
- poetry run pip install https://github.com/ansible/ansible/archive/stable-2.13.tar.gz --disable-pip-version-check
|
||||
- poetry run ansible --version
|
||||
- poetry run ansible-test sanity --exclude .chglog/ --exclude .drone.yml --python 3.9
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
- clone
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- refs/tags/**
|
||||
- refs/pull/**
|
||||
|
||||
depends_on:
|
||||
- unit-test
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: build
|
||||
@ -82,12 +174,14 @@ platform:
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: python:3.9
|
||||
image: python:3.11
|
||||
commands:
|
||||
- GALAXY_VERSION=${DRONE_TAG##v}
|
||||
- "sed -i 's/version: 0.0.0/version: '\"$${GALAXY_VERSION:-0.0.0}\"'/g' galaxy.yml"
|
||||
- pip install ansible -qq
|
||||
- ansible-galaxy collection build --output-path dist/
|
||||
- pip install poetry -qq
|
||||
- poetry config experimental.new-installer false
|
||||
- poetry install --all-extras
|
||||
- poetry run ansible-galaxy collection build --output-path dist/
|
||||
|
||||
- name: checksum
|
||||
image: alpine
|
||||
@ -129,7 +223,7 @@ trigger:
|
||||
- refs/pull/**
|
||||
|
||||
depends_on:
|
||||
- test
|
||||
- sanity-test
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
@ -195,6 +289,6 @@ depends_on:
|
||||
|
||||
---
|
||||
kind: signature
|
||||
hmac: 93f735c3d2fbaf499fd96b79301f6de2455349051dc320c511e6e62c8ba04a4d
|
||||
hmac: 407d145ab4483651c579eea4e1e9375536caee1aa0579abcdf57b5653e595cb5
|
||||
|
||||
...
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -108,3 +108,4 @@ docs/public/
|
||||
resources/_gen/
|
||||
|
||||
CHANGELOG.md
|
||||
tests/output
|
||||
|
@ -1,18 +0,0 @@
|
||||
pydocstyle
|
||||
flake8
|
||||
flake8-blind-except
|
||||
flake8-builtins
|
||||
flake8-docstrings
|
||||
flake8-isort
|
||||
flake8-logging-format
|
||||
flake8-polyfill
|
||||
flake8-quotes
|
||||
flake8-pep3101
|
||||
flake8-eradicate
|
||||
pep8-naming
|
||||
wheel
|
||||
pytest
|
||||
pytest-mock
|
||||
pytest-cov
|
||||
bandit
|
||||
yapf
|
@ -1,11 +1,15 @@
|
||||
"""Filter to prefix all itams from a list."""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
def prefix(value, prefix="--"):
|
||||
return [prefix + x for x in value]
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
class FilterModule(object): # noqa
|
||||
|
||||
def filters(self):
|
||||
return {"prefix": prefix}
|
||||
|
@ -1,11 +1,15 @@
|
||||
"""Filter to wrap all items from a list."""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
def wrap(value, wrapper="'"):
|
||||
return [wrapper + x + wrapper for x in value]
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
class FilterModule(object): # noqa
|
||||
|
||||
def filters(self):
|
||||
return {"wrap": wrap}
|
||||
|
@ -1,52 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2014, Mathieu GAUTHIER-LAFAYE <gauthierl@lapth.cnrs.fr>
|
||||
# Copyright (c) 2016, Matt Harris <matthaeus.harris@gmail.com>
|
||||
# Copyright (c) 2020, Robert Kaussow <mail@thegeeklab.de>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""Dynamic inventory plugin for Proxmox VE."""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
name: proxmox
|
||||
plugin_type: inventory
|
||||
short_description: Proxmox VE inventory source
|
||||
version_added: 1.0.0
|
||||
description:
|
||||
---
|
||||
name: proxmox
|
||||
short_description: Proxmox VE inventory source
|
||||
version_added: 1.1.0
|
||||
description:
|
||||
- Get inventory hosts from the proxmox service.
|
||||
- "Uses a configuration file as an inventory source, it must end in C(.proxmox.yml) or C(.proxmox.yaml) and has a C(plugin: xoxys.general.proxmox) entry."
|
||||
extends_documentation_fragment:
|
||||
extends_documentation_fragment:
|
||||
- inventory_cache
|
||||
options:
|
||||
options:
|
||||
plugin:
|
||||
description: The name of this plugin, it should always be set to C(xoxys.general.proxmox) for this plugin to recognize it as it's own.
|
||||
required: yes
|
||||
choices: ["xoxys.general.proxmox"]
|
||||
server:
|
||||
description: Proxmox VE server url.
|
||||
default: "pve.example.com"
|
||||
type: string
|
||||
required: yes
|
||||
env:
|
||||
- name: PROXMOX_SERVER
|
||||
user:
|
||||
description: Proxmox VE authentication user.
|
||||
type: string
|
||||
required: yes
|
||||
env:
|
||||
- name: PROXMOX_USER
|
||||
password:
|
||||
description: Proxmox VE authentication password.
|
||||
type: string
|
||||
required: yes
|
||||
env:
|
||||
- name: PROXMOX_PASSWORD
|
||||
api_host:
|
||||
description:
|
||||
- Specify the target host of the Proxmox VE cluster.
|
||||
type: str
|
||||
required: true
|
||||
api_user:
|
||||
description:
|
||||
- Specify the user to authenticate with.
|
||||
type: str
|
||||
required: true
|
||||
api_password:
|
||||
description:
|
||||
- Specify the password to authenticate with.
|
||||
- You can use C(PROXMOX_PASSWORD) environment variable.
|
||||
type: str
|
||||
api_token_id:
|
||||
description:
|
||||
- Specify the token ID.
|
||||
type: str
|
||||
api_token_secret:
|
||||
description:
|
||||
- Specify the token secret.
|
||||
type: str
|
||||
verify_ssl:
|
||||
description: Skip SSL certificate verification.
|
||||
type: boolean
|
||||
default: yes
|
||||
description:
|
||||
- If C(false), SSL certificates will not be validated.
|
||||
- This should only be used on personally controlled sites using self-signed certificates.
|
||||
type: bool
|
||||
default: True
|
||||
auth_timeout:
|
||||
description: Proxmox VE authentication timeout.
|
||||
type: int
|
||||
@ -68,28 +73,30 @@ DOCUMENTATION = """
|
||||
want_facts:
|
||||
description: Toggle, if C(true) the plugin will retrieve host facts from the server
|
||||
type: boolean
|
||||
default: yes
|
||||
default: True
|
||||
requirements:
|
||||
- "proxmoxer"
|
||||
""" # noqa
|
||||
|
||||
EXAMPLES = """
|
||||
# proxmox.yml
|
||||
plugin: xoxys.general.proxmox
|
||||
server: pve.example.com
|
||||
user: admin@pve
|
||||
password: secure
|
||||
api_user: root@pam
|
||||
api_password: secret
|
||||
api_host: helldorado
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import socket
|
||||
from collections import defaultdict
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin
|
||||
from collections import defaultdict
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
try:
|
||||
from proxmoxer import ProxmoxAPI
|
||||
@ -97,17 +104,39 @@ try:
|
||||
except ImportError:
|
||||
HAS_PROXMOXER = False
|
||||
|
||||
try:
|
||||
from requests.packages import urllib3
|
||||
HAS_URLLIB3 = True
|
||||
except ImportError:
|
||||
try:
|
||||
import urllib3
|
||||
HAS_URLLIB3 = True
|
||||
except ImportError:
|
||||
HAS_URLLIB3 = False
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin):
|
||||
"""Provide Proxmox VE inventory."""
|
||||
|
||||
NAME = "xoxys.general.proxmox"
|
||||
|
||||
def _auth(self):
|
||||
auth_args = {"user": self.get_option("api_user")}
|
||||
if not (self.get_option("api_token_id") and self.get_option("api_token_secret")):
|
||||
auth_args["password"] = self.get_option("api_password")
|
||||
else:
|
||||
auth_args["token_name"] = self.get_option("api_token_id")
|
||||
auth_args["token_value"] = self.get_option("api_token_secret")
|
||||
|
||||
verify_ssl = boolean(self.get_option("verify_ssl"), strict=False)
|
||||
if not verify_ssl and HAS_URLLIB3:
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
return ProxmoxAPI(
|
||||
self.get_option("server"),
|
||||
user=self.get_option("user"),
|
||||
password=self.get_option("password"),
|
||||
verify_ssl=boolean(self.get_option("password"), strict=False),
|
||||
timeout=self.get_option("auth_timeout")
|
||||
self.get_option("api_host"),
|
||||
verify_ssl=verify_ssl,
|
||||
timeout=self.get_option("auth_timeout"),
|
||||
**auth_args
|
||||
)
|
||||
|
||||
def _get_version(self):
|
||||
@ -117,14 +146,12 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
return LooseVersion(self.client.version.get()["release"])
|
||||
|
||||
def _get_names(self, pve_list, pve_type):
|
||||
names = []
|
||||
|
||||
if pve_type == "node":
|
||||
names = [node["node"] for node in pve_list]
|
||||
elif pve_type == "pool":
|
||||
names = [pool["poolid"] for pool in pve_list]
|
||||
return [node["node"] for node in pve_list]
|
||||
if pve_type == "pool":
|
||||
return [pool["poolid"] for pool in pve_list]
|
||||
|
||||
return names
|
||||
return []
|
||||
|
||||
def _get_variables(self, pve_list, pve_type):
|
||||
variables = {}
|
||||
@ -143,11 +170,9 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
def validate(address):
|
||||
try:
|
||||
# IP address validation
|
||||
if socket.inet_aton(address):
|
||||
# Ignore localhost
|
||||
if address != "127.0.0.1":
|
||||
if socket.inet_aton(address) and address != "127.0.0.1":
|
||||
return address
|
||||
except socket.error:
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
address = False
|
||||
@ -159,11 +184,10 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
networks = self.client.nodes(pve_node).get(
|
||||
"qemu", vmid, "agent", "network-get-interfaces"
|
||||
)["result"]
|
||||
except Exception:
|
||||
except Exception: # noqa
|
||||
pass
|
||||
|
||||
if networks:
|
||||
if type(networks) is list:
|
||||
if networks and isinstance(networks, list):
|
||||
for network in networks:
|
||||
for ip_address in network["ip-addresses"]:
|
||||
address = validate(ip_address["ip-address"])
|
||||
@ -171,7 +195,7 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
try:
|
||||
config = self.client.nodes(pve_node).get(pve_type, vmid, "config")
|
||||
address = re.search(r"ip=(\d*\.\d*\.\d*\.\d*)", config["net0"]).group(1)
|
||||
except Exception:
|
||||
except Exception: # noqa
|
||||
pass
|
||||
|
||||
return address
|
||||
@ -197,8 +221,8 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
try:
|
||||
qemu_list = self._exclude(self.client.nodes(node).qemu.get())
|
||||
container_list = self._exclude(self.client.nodes(node).lxc.get())
|
||||
except Exception as e:
|
||||
raise AnsibleError("Proxmoxer API error: {0}".format(to_native(e)))
|
||||
except Exception as e: # noqa
|
||||
raise AnsibleError(f"Proxmoxer API error: {to_native(e)}") from e
|
||||
|
||||
# Merge QEMU and Containers lists from this node
|
||||
instances = self._get_variables(qemu_list, "qemu").copy()
|
||||
@ -217,8 +241,8 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
"config")["description"]
|
||||
except KeyError:
|
||||
description = None
|
||||
except Exception as e:
|
||||
raise AnsibleError("Proxmoxer API error: {0}".format(to_native(e)))
|
||||
except Exception as e: # noqa
|
||||
raise AnsibleError(f"Proxmoxer API error: {to_native(e)}") from e
|
||||
|
||||
try:
|
||||
metadata = json.loads(description)
|
||||
@ -252,8 +276,8 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
for pool in self._get_names(self.client.pools.get(), "pool"):
|
||||
try:
|
||||
pool_list = self._exclude(self.client.pool(pool).get()["members"])
|
||||
except Exception as e:
|
||||
raise AnsibleError("Proxmoxer API error: {0}".format(to_native(e)))
|
||||
except Exception as e: # noqa
|
||||
raise AnsibleError(f"Proxmoxer API error: {to_native(e)}") from e
|
||||
|
||||
members = [
|
||||
member["name"]
|
||||
@ -266,13 +290,13 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
|
||||
def verify_file(self, path):
|
||||
"""Verify the Proxmox VE configuration file."""
|
||||
if super(InventoryModule, self).verify_file(path):
|
||||
if super().verify_file(path):
|
||||
endings = ("proxmox.yaml", "proxmox.yml")
|
||||
if any((path.endswith(ending) for ending in endings)):
|
||||
if any(path.endswith(ending) for ending in endings):
|
||||
return True
|
||||
return False
|
||||
|
||||
def parse(self, inventory, loader, path, cache=True):
|
||||
def parse(self, inventory, loader, path, cache=True): # noqa
|
||||
"""Dynamically parse the Proxmox VE cloud inventory."""
|
||||
if not HAS_PROXMOXER:
|
||||
raise AnsibleError(
|
||||
@ -280,7 +304,7 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
"https://pypi.org/project/proxmoxer/"
|
||||
)
|
||||
|
||||
super(InventoryModule, self).parse(inventory, loader, path)
|
||||
super().parse(inventory, loader, path)
|
||||
|
||||
self._read_config_data(path)
|
||||
self.client = self._auth()
|
||||
|
@ -1,3 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
IPtables raw module.
|
||||
@ -21,13 +22,17 @@ You should have received a copy of the GNU General Public License
|
||||
along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'status': ['preview'], 'supported_by': 'community', 'metadata_version': '1.0'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: iptables_raw
|
||||
short_description: Manage iptables rules
|
||||
version_added: "2.4"
|
||||
version_added: 1.1.0
|
||||
description:
|
||||
- Add/remove iptables rules while keeping state.
|
||||
options:
|
||||
@ -35,13 +40,14 @@ options:
|
||||
description:
|
||||
- Create a backup of the iptables state file before overwriting it.
|
||||
required: false
|
||||
choices: ["yes", "no"]
|
||||
default: "no"
|
||||
type: bool
|
||||
default: False
|
||||
ipversion:
|
||||
description:
|
||||
- Target the IP version this rule is for.
|
||||
required: false
|
||||
default: "4"
|
||||
type: str
|
||||
choices: ["4", "6"]
|
||||
keep_unmanaged:
|
||||
description:
|
||||
@ -54,8 +60,8 @@ options:
|
||||
first time, since if you don't specify correct rules, you can block
|
||||
yourself out of the managed host."
|
||||
required: false
|
||||
choices: ["yes", "no"]
|
||||
default: "yes"
|
||||
type: bool
|
||||
default: True
|
||||
name:
|
||||
description:
|
||||
- Name that will be used as an identifier for these rules. It can contain
|
||||
@ -64,17 +70,21 @@ options:
|
||||
C(state=absent) to flush all rules in the selected table, or even all
|
||||
tables with C(table=*).
|
||||
required: true
|
||||
type: str
|
||||
rules:
|
||||
description:
|
||||
- The rules that we want to add. Accepts multiline values.
|
||||
- "Note: You can only use C(-A)/C(--append), C(-N)/C(--new-chain), and
|
||||
C(-P)/C(--policy) to specify rules."
|
||||
required: false
|
||||
type: str
|
||||
default: ""
|
||||
state:
|
||||
description:
|
||||
- The state this rules fragment should be in.
|
||||
choices: ["present", "absent"]
|
||||
required: false
|
||||
type: str
|
||||
default: present
|
||||
table:
|
||||
description:
|
||||
@ -82,12 +92,13 @@ options:
|
||||
with C(name=*) and C(state=absent) to flush all rules in all tables.
|
||||
choices: ["filter", "nat", "mangle", "raw", "security", "*"]
|
||||
required: false
|
||||
type: str
|
||||
default: filter
|
||||
weight:
|
||||
description:
|
||||
- Determines the order of the rules. Lower C(weight) means higher
|
||||
priority. Supported range is C(0 - 99)
|
||||
choices: ["0 - 99"]
|
||||
type: int
|
||||
required: false
|
||||
default: 40
|
||||
notes:
|
||||
@ -116,7 +127,7 @@ EXAMPLES = '''
|
||||
- iptables_raw:
|
||||
name: default_rules
|
||||
weight: 10
|
||||
keep_unmanaged: no
|
||||
keep_unmanaged: false
|
||||
rules: |
|
||||
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
-A INPUT -i lo -j ACCEPT
|
||||
@ -156,12 +167,12 @@ RETURN = '''
|
||||
state:
|
||||
description: state of the rules
|
||||
returned: success
|
||||
type: string
|
||||
type: str
|
||||
sample: present
|
||||
name:
|
||||
description: name of the rules
|
||||
returned: success
|
||||
type: string
|
||||
type: str
|
||||
sample: open_tcp_80
|
||||
weight:
|
||||
description: weight of the rules
|
||||
@ -176,22 +187,22 @@ ipversion:
|
||||
rules:
|
||||
description: passed rules
|
||||
returned: success
|
||||
type: string
|
||||
type: str
|
||||
sample: "-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT"
|
||||
table:
|
||||
description: iptables table used
|
||||
returned: success
|
||||
type: string
|
||||
type: str
|
||||
sample: filter
|
||||
backup:
|
||||
description: if the iptables file should backed up
|
||||
returned: success
|
||||
type: boolean
|
||||
type: bool
|
||||
sample: False
|
||||
keep_unmanaged:
|
||||
description: if it should keep unmanaged rules
|
||||
returned: success
|
||||
type: boolean
|
||||
type: bool
|
||||
sample: True
|
||||
'''
|
||||
|
||||
|
@ -1,25 +1,36 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""OpenSSL PKCS12 module."""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {"metadata_version": "1.0", "status": ["preview"], "supported_by": "community"}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: openssl_pkcs12
|
||||
author: "Guillaume Delpierre (@gdelpierre)"
|
||||
version_added: "2.4"
|
||||
version_added: 1.1.0
|
||||
short_description: Generate OpenSSL pkcs12 archive.
|
||||
description:
|
||||
- "This module allows one to (re-)generate PKCS#12."
|
||||
requirements:
|
||||
- "python-pyOpenSSL"
|
||||
extends_documentation_fragment: files
|
||||
options:
|
||||
ca_certificates:
|
||||
required: False
|
||||
type: list
|
||||
elements: str
|
||||
description:
|
||||
- List of CA certificate to include.
|
||||
cert_path:
|
||||
required: False
|
||||
type: path
|
||||
description:
|
||||
- The path to read certificates and private keys from.
|
||||
Must be in PEM format.
|
||||
@ -27,61 +38,70 @@ options:
|
||||
required: False
|
||||
default: "export"
|
||||
choices: ["parse", "export"]
|
||||
type: str
|
||||
description:
|
||||
- Create (export) or parse a PKCS#12.
|
||||
src:
|
||||
required: False
|
||||
type: path
|
||||
description:
|
||||
- PKCS#12 file path to parse.
|
||||
path:
|
||||
required: True
|
||||
default: null
|
||||
type: path
|
||||
description:
|
||||
- Filename to write the PKCS#12 file to.
|
||||
force:
|
||||
required: False
|
||||
default: False
|
||||
type: bool
|
||||
description:
|
||||
- Should the file be regenerated even it it already exists.
|
||||
friendly_name:
|
||||
required: False
|
||||
default: null
|
||||
aliases: "name"
|
||||
type: str
|
||||
aliases:
|
||||
- "name"
|
||||
description:
|
||||
- Specifies the friendly name for the certificate and private key.
|
||||
iter_size:
|
||||
required: False
|
||||
default: 2048
|
||||
type: int
|
||||
description:
|
||||
- Number of times to repeat the encryption step.
|
||||
maciter_size:
|
||||
required: False
|
||||
default: 1
|
||||
type: int
|
||||
description:
|
||||
- Number of times to repeat the MAC step.
|
||||
mode:
|
||||
required: False
|
||||
default: 0400
|
||||
default: "0400"
|
||||
type: str
|
||||
description:
|
||||
- Default mode for the generated PKCS#12 file.
|
||||
passphrase:
|
||||
required: False
|
||||
default: null
|
||||
type: str
|
||||
description:
|
||||
- The PKCS#12 password.
|
||||
privatekey_path:
|
||||
required: False
|
||||
type: path
|
||||
description:
|
||||
- File to read private key from.
|
||||
privatekey_passphrase:
|
||||
required: False
|
||||
default: null
|
||||
type: str
|
||||
description:
|
||||
- Passphrase source to decrypt any input private keys with.
|
||||
state:
|
||||
required: False
|
||||
default: "present"
|
||||
choices: ["present", "absent"]
|
||||
type: str
|
||||
description:
|
||||
- Whether the file should exist or not.
|
||||
"""
|
||||
@ -133,7 +153,7 @@ RETURN = """
|
||||
filename:
|
||||
description: Path to the generate PKCS#12 file.
|
||||
returned: changed or success
|
||||
type: string
|
||||
type: str
|
||||
sample: /opt/certs/ansible.p12
|
||||
"""
|
||||
|
||||
@ -151,11 +171,11 @@ else:
|
||||
pyopenssl_found = True
|
||||
|
||||
|
||||
class PkcsError(Exception):
|
||||
class PkcsError(Exception): # noqa
|
||||
pass
|
||||
|
||||
|
||||
class Pkcs(object):
|
||||
class Pkcs(object): # noqa
|
||||
|
||||
def __init__(self, module):
|
||||
self.path = module.params["path"]
|
||||
@ -181,36 +201,37 @@ class Pkcs(object):
|
||||
def load_privatekey(self, path, passphrase=None):
|
||||
"""Load the specified OpenSSL private key."""
|
||||
try:
|
||||
if passphrase:
|
||||
privatekey = crypto.load_privatekey(
|
||||
crypto.FILETYPE_PEM,
|
||||
open(path, "rb").read(), passphrase
|
||||
open(path, "rb").read(), # noqa
|
||||
passphrase
|
||||
) if passphrase else crypto.load_privatekey(
|
||||
crypto.FILETYPE_PEM,
|
||||
open(path, "rb").read() # noqa
|
||||
)
|
||||
else:
|
||||
privatekey = crypto.load_privatekey(crypto.FILETYPE_PEM, open(path, "rb").read())
|
||||
|
||||
return privatekey
|
||||
except (IOError, OSError) as exc:
|
||||
raise PkcsError(exc)
|
||||
except OSError as exc:
|
||||
raise PkcsError(exc) from exc
|
||||
|
||||
def load_certificate(self, path):
|
||||
"""Load the specified certificate."""
|
||||
try:
|
||||
cert_content = open(path, "rb").read()
|
||||
cert_content = open(path, "rb").read() # noqa
|
||||
cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert_content)
|
||||
return cert
|
||||
except (IOError, OSError) as exc:
|
||||
raise PkcsError(exc)
|
||||
except OSError as exc:
|
||||
raise PkcsError(exc) from exc
|
||||
|
||||
def load_pkcs12(self, path, passphrase=None):
|
||||
"""Load pkcs12 file."""
|
||||
try:
|
||||
if passphrase:
|
||||
return crypto.load_pkcs12(open(path, "rb").read(), passphrase)
|
||||
else:
|
||||
return crypto.load_pkcs12(open(path, "rb").read())
|
||||
except (IOError, OSError) as exc:
|
||||
raise PkcsError(exc)
|
||||
return crypto.load_pkcs12(open(path, "rb").read(), passphrase) # noqa
|
||||
|
||||
return crypto.load_pkcs12(open(path, "rb").read()) # noqa
|
||||
except OSError as exc:
|
||||
raise PkcsError(exc) from exc
|
||||
|
||||
def dump_privatekey(self, path):
|
||||
"""Dump the specified OpenSSL private key."""
|
||||
@ -219,8 +240,8 @@ class Pkcs(object):
|
||||
crypto.FILETYPE_PEM,
|
||||
self.load_pkcs12(path).get_privatekey()
|
||||
)
|
||||
except (IOError, OSError) as exc:
|
||||
raise PkcsError(exc)
|
||||
except OSError as exc:
|
||||
raise PkcsError(exc) from exc
|
||||
|
||||
def dump_certificate(self, path):
|
||||
"""Dump the specified certificate."""
|
||||
@ -229,8 +250,8 @@ class Pkcs(object):
|
||||
crypto.FILETYPE_PEM,
|
||||
self.load_pkcs12(path).get_certificate()
|
||||
)
|
||||
except (IOError, OSError) as exc:
|
||||
raise PkcsError(exc)
|
||||
except OSError as exc:
|
||||
raise PkcsError(exc) from exc
|
||||
|
||||
def generate(self, module):
|
||||
"""Generate PKCS#12 file archive."""
|
||||
@ -264,9 +285,9 @@ class Pkcs(object):
|
||||
)
|
||||
module.set_mode_if_different(self.path, self.mode, False)
|
||||
self.changed = True
|
||||
except (IOError, OSError) as exc:
|
||||
except OSError as exc:
|
||||
self.remove()
|
||||
raise PkcsError(exc)
|
||||
raise PkcsError(exc) from exc
|
||||
|
||||
file_args = module.load_file_common_arguments(module.params)
|
||||
if module.set_fs_attributes_if_different(file_args, False):
|
||||
@ -281,14 +302,12 @@ class Pkcs(object):
|
||||
|
||||
with open(self.path, "wb") as content:
|
||||
content.write(
|
||||
"{0}{1}".format(
|
||||
self.dump_privatekey(self.src), self.dump_certificate(self.src)
|
||||
)
|
||||
f"{self.dump_privatekey(self.src)}{self.dump_certificate(self.src)}"
|
||||
)
|
||||
module.set_mode_if_different(self.path, self.mode, False)
|
||||
self.changed = True
|
||||
except IOError as exc:
|
||||
raise PkcsError(exc)
|
||||
except OSError as exc:
|
||||
raise PkcsError(exc) from exc
|
||||
|
||||
file_args = module.load_file_common_arguments(module.params)
|
||||
if module.set_fs_attributes_if_different(file_args, False):
|
||||
@ -302,11 +321,11 @@ class Pkcs(object):
|
||||
self.changed = True
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.ENOENT:
|
||||
raise PkcsError(exc)
|
||||
else:
|
||||
raise PkcsError(exc) from exc
|
||||
|
||||
pass
|
||||
|
||||
def check(self, module, perms_required=True):
|
||||
def check(self, module, perms_required=True): # noqa
|
||||
|
||||
def _check_pkey_passphrase():
|
||||
if self.privatekey_passphrase:
|
||||
@ -337,19 +356,20 @@ class Pkcs(object):
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
action=dict(default="export", choices=["parse", "export"], type="str"),
|
||||
ca_certificates=dict(type="list"),
|
||||
action=dict(default="export", choices=["parse", "export"], type="str", required=False),
|
||||
ca_certificates=dict(type="list", elements="str", required=False),
|
||||
cert_path=dict(type="path"),
|
||||
force=dict(default=False, type="bool"),
|
||||
friendly_name=dict(type="str", aliases=["name"]),
|
||||
iter_size=dict(default=2048, type="int"),
|
||||
maciter_size=dict(default=1, type="int"),
|
||||
passphrase=dict(type="str", no_log=True),
|
||||
path=dict(required=True, type="path"),
|
||||
path=dict(type="path", required=True),
|
||||
privatekey_path=dict(type="path"),
|
||||
privatekey_passphrase=dict(type="str", no_log=True),
|
||||
state=dict(default="present", choices=["present", "absent"], type="str"),
|
||||
src=dict(type="path"),
|
||||
mode=dict(default="0400", type="str", required=False)
|
||||
)
|
||||
|
||||
required_if = [
|
||||
@ -376,8 +396,7 @@ def main():
|
||||
if not os.path.isdir(base_dir):
|
||||
module.fail_json(
|
||||
name=base_dir,
|
||||
msg="The directory {0} does not exist or "
|
||||
"the file is not a directory".format(base_dir)
|
||||
msg=f"The directory {base_dir} does not exist or the file is not a directory"
|
||||
)
|
||||
|
||||
pkcs12 = Pkcs(module)
|
||||
|
@ -1,20 +1,69 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2016, Abdoul Bah (@helldorado) <bahabdoul at gmail.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""Module to control Proxmox KVM machines."""
|
||||
"""Control Proxmox KVM machines."""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
DOCUMENTATION = r"""
|
||||
---
|
||||
module: proxmox_kvm
|
||||
short_description: Management of Qemu(KVM) Virtual Machines in Proxmox VE cluster.
|
||||
description:
|
||||
- Allows you to create/delete/stop Qemu(KVM) Virtual Machines in Proxmox VE cluster.
|
||||
author: "Abdoul Bah (@helldorado) <bahabdoul at gmail.com>"
|
||||
version_added: 1.1.0
|
||||
options:
|
||||
vmid:
|
||||
description:
|
||||
- Specifies the instance ID.
|
||||
- If not set the next available ID will be fetched from ProxmoxAPI.
|
||||
type: int
|
||||
node:
|
||||
description:
|
||||
- Proxmox VE node on which to operate.
|
||||
- Only required for I(state=present).
|
||||
- For every other states it will be autodiscovered.
|
||||
type: str
|
||||
pool:
|
||||
description:
|
||||
- Add the new VM to the specified pool.
|
||||
type: str
|
||||
api_host:
|
||||
description:
|
||||
- Specify the target host of the Proxmox VE cluster.
|
||||
type: str
|
||||
required: true
|
||||
api_user:
|
||||
description:
|
||||
- Specify the user to authenticate with.
|
||||
type: str
|
||||
required: true
|
||||
api_password:
|
||||
description:
|
||||
- Specify the password to authenticate with.
|
||||
- You can use C(PROXMOX_PASSWORD) environment variable.
|
||||
type: str
|
||||
api_token_id:
|
||||
description:
|
||||
- Specify the token ID.
|
||||
type: str
|
||||
version_added: 1.3.0
|
||||
api_token_secret:
|
||||
description:
|
||||
- Specify the token secret.
|
||||
type: str
|
||||
version_added: 1.3.0
|
||||
verify_ssl:
|
||||
description:
|
||||
- If C(false), SSL certificates will not be validated.
|
||||
- This should only be used on personally controlled sites using self-signed certificates.
|
||||
type: bool
|
||||
default: True
|
||||
acpi:
|
||||
description:
|
||||
- Specify if ACPI should be enabled/disabled.
|
||||
@ -49,7 +98,7 @@ options:
|
||||
description:
|
||||
- Specify the BIOS implementation.
|
||||
type: str
|
||||
choices: ['seabios', 'ovmf']
|
||||
choices: ["seabios", "ovmf"]
|
||||
boot:
|
||||
description:
|
||||
- Specify the boot order -> boot on floppy C(a), hard disk C(c), CD-ROM C(d), or network C(n).
|
||||
@ -63,27 +112,23 @@ options:
|
||||
type: str
|
||||
cicustom:
|
||||
description:
|
||||
- 'cloud-init: Specify custom files to replace the automatically generated ones at start.'
|
||||
- "cloud-init: Specify custom files to replace the automatically generated ones at start."
|
||||
type: str
|
||||
version_added: 1.3.0
|
||||
cipassword:
|
||||
description:
|
||||
- 'cloud-init: password of default user to create.'
|
||||
- "cloud-init: password of default user to create."
|
||||
type: str
|
||||
version_added: 1.3.0
|
||||
citype:
|
||||
description:
|
||||
- 'cloud-init: Specifies the cloud-init configuration format.'
|
||||
- "cloud-init: Specifies the cloud-init configuration format."
|
||||
- The default depends on the configured operating system type (C(ostype)).
|
||||
- We use the C(nocloud) format for Linux, and C(configdrive2) for Windows.
|
||||
type: str
|
||||
choices: ['nocloud', 'configdrive2']
|
||||
version_added: 1.3.0
|
||||
choices: ["nocloud", "configdrive2"]
|
||||
ciuser:
|
||||
description:
|
||||
- 'cloud-init: username of default user to create.'
|
||||
- "cloud-init: username of default user to create."
|
||||
type: str
|
||||
version_added: 1.3.0
|
||||
clone:
|
||||
description:
|
||||
- Name of VM to be cloned. If C(vmid) is setted, C(clone) can take arbitrary value but required for initiating the clone.
|
||||
@ -142,7 +187,8 @@ options:
|
||||
option has a default of C(qcow2). If I(proxmox_default_behavior) is set to C(no_defaults),
|
||||
not specifying this option is equivalent to setting it to C(unspecified).
|
||||
type: str
|
||||
choices: [ "cloop", "cow", "qcow", "qcow2", "qed", "raw", "vmdk", "unspecified" ]
|
||||
choices:
|
||||
["cloop", "cow", "qcow", "qcow2", "qed", "raw", "vmdk", "unspecified"]
|
||||
freeze:
|
||||
description:
|
||||
- Specify if PVE should freeze CPU at startup (use 'c' monitor command to start execution).
|
||||
@ -153,7 +199,7 @@ options:
|
||||
- For VM templates, we try to create a linked clone by default.
|
||||
- Used only with clone
|
||||
type: bool
|
||||
default: 'yes'
|
||||
default: "yes"
|
||||
hostpci:
|
||||
description:
|
||||
- Specify a hash/dictionary of map host pci devices into guest. C(hostpci='{"key":"value", "key":"value"}').
|
||||
@ -175,7 +221,7 @@ options:
|
||||
description:
|
||||
- Enable/disable hugepages memory.
|
||||
type: str
|
||||
choices: ['any', '2', '1024']
|
||||
choices: ["any", "2", "1024"]
|
||||
ide:
|
||||
description:
|
||||
- A hash/dictionary of volume used as IDE hard disk or CD-ROM. C(ide='{"key":"value", "key":"value"}').
|
||||
@ -187,17 +233,16 @@ options:
|
||||
type: dict
|
||||
ipconfig:
|
||||
description:
|
||||
- 'cloud-init: Set the IP configuration.'
|
||||
- "cloud-init: Set the IP configuration."
|
||||
- A hash/dictionary of network ip configurations. C(ipconfig='{"key":"value", "key":"value"}').
|
||||
- Keys allowed are - C(ipconfig[n]) where 0 ≤ n ≤ network interfaces.
|
||||
- Values allowed are - C("[gw=<GatewayIPv4>] [,gw6=<GatewayIPv6>] [,ip=<IPv4Format/CIDR>] [,ip6=<IPv6Format/CIDR>]").
|
||||
- 'cloud-init: Specify IP addresses and gateways for the corresponding interface.'
|
||||
- "cloud-init: Specify IP addresses and gateways for the corresponding interface."
|
||||
- IP addresses use CIDR notation, gateways are optional but they should be in the same subnet of specified IP address.
|
||||
- The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit gateway should be provided.
|
||||
- For IPv6 the special string 'auto' can be used to use stateless autoconfiguration.
|
||||
- If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using dhcp on IPv4.
|
||||
type: dict
|
||||
version_added: 1.3.0
|
||||
keyboard:
|
||||
description:
|
||||
- Sets the keyboard layout for VNC server.
|
||||
@ -217,7 +262,7 @@ options:
|
||||
description:
|
||||
- Lock/unlock the VM.
|
||||
type: str
|
||||
choices: ['migrate', 'backup', 'snapshot', 'rollback']
|
||||
choices: ["migrate", "backup", "snapshot", "rollback"]
|
||||
machine:
|
||||
description:
|
||||
- Specifies the Qemu machine type.
|
||||
@ -245,11 +290,10 @@ options:
|
||||
type: str
|
||||
nameservers:
|
||||
description:
|
||||
- 'cloud-init: DNS server IP address(es).'
|
||||
- "cloud-init: DNS server IP address(es)."
|
||||
- If unset, PVE host settings are used.
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 1.3.0
|
||||
net:
|
||||
description:
|
||||
- A hash/dictionary of network interfaces for the VM. C(net='{"key":"value", "key":"value"}').
|
||||
@ -293,7 +337,21 @@ options:
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(l26).
|
||||
type: str
|
||||
choices: ['other', 'wxp', 'w2k', 'w2k3', 'w2k8', 'wvista', 'win7', 'win8', 'win10', 'l24', 'l26', 'solaris']
|
||||
choices:
|
||||
[
|
||||
"other",
|
||||
"wxp",
|
||||
"w2k",
|
||||
"w2k3",
|
||||
"w2k8",
|
||||
"wvista",
|
||||
"win7",
|
||||
"win8",
|
||||
"win10",
|
||||
"l24",
|
||||
"l26",
|
||||
"solaris",
|
||||
]
|
||||
parallel:
|
||||
description:
|
||||
- A hash/dictionary of map host parallel devices. C(parallel='{"key":"value", "key":"value"}').
|
||||
@ -334,14 +392,21 @@ options:
|
||||
description:
|
||||
- Specifies the SCSI controller model.
|
||||
type: str
|
||||
choices: ['lsi', 'lsi53c810', 'virtio-scsi-pci', 'virtio-scsi-single', 'megasas', 'pvscsi']
|
||||
choices:
|
||||
[
|
||||
"lsi",
|
||||
"lsi53c810",
|
||||
"virtio-scsi-pci",
|
||||
"virtio-scsi-single",
|
||||
"megasas",
|
||||
"pvscsi",
|
||||
]
|
||||
searchdomains:
|
||||
description:
|
||||
- 'cloud-init: Sets DNS search domain(s).'
|
||||
- "cloud-init: Sets DNS search domain(s)."
|
||||
- If unset, PVE host settings are used.
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 1.3.0
|
||||
serial:
|
||||
description:
|
||||
- A hash/dictionary of serial device to create inside the VM. C('{"key":"value", "key":"value"}').
|
||||
@ -377,9 +442,8 @@ options:
|
||||
type: int
|
||||
sshkeys:
|
||||
description:
|
||||
- 'cloud-init: SSH key to assign to the default user. NOT TESTED with multiple keys but a multi-line value should work.'
|
||||
- "cloud-init: SSH key to assign to the default user. NOT TESTED with multiple keys but a multi-line value should work."
|
||||
type: str
|
||||
version_added: 1.3.0
|
||||
startdate:
|
||||
description:
|
||||
- Sets the initial date of the real time clock.
|
||||
@ -396,7 +460,7 @@ options:
|
||||
- Indicates desired state of the instance.
|
||||
- If C(current), the current state of the VM will be fetched. You can access it with C(results.status)
|
||||
type: str
|
||||
choices: ['present', 'started', 'absent', 'stopped', 'restarted','current']
|
||||
choices: ["present", "started", "absent", "stopped", "restarted", "current"]
|
||||
default: present
|
||||
storage:
|
||||
description:
|
||||
@ -415,7 +479,6 @@ options:
|
||||
- Tags are only available in Proxmox 6+.
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 2.3.0
|
||||
target:
|
||||
description:
|
||||
- Target node. Only allowed if the original VM is on shared storage.
|
||||
@ -441,7 +504,7 @@ options:
|
||||
- If C(yes), the VM will be updated with new value.
|
||||
- Update of C(pool) is disabled. It needs an additional API endpoint not covered by this module.
|
||||
type: bool
|
||||
default: 'no'
|
||||
default: "no"
|
||||
vcpus:
|
||||
description:
|
||||
- Sets number of hotplugged vcpus.
|
||||
@ -452,7 +515,20 @@ options:
|
||||
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
|
||||
option has a default of C(std).
|
||||
type: str
|
||||
choices: ['std', 'cirrus', 'vmware', 'qxl', 'serial0', 'serial1', 'serial2', 'serial3', 'qxl2', 'qxl3', 'qxl4']
|
||||
choices:
|
||||
[
|
||||
"std",
|
||||
"cirrus",
|
||||
"vmware",
|
||||
"qxl",
|
||||
"serial0",
|
||||
"serial1",
|
||||
"serial2",
|
||||
"serial3",
|
||||
"qxl2",
|
||||
"qxl3",
|
||||
"qxl4",
|
||||
]
|
||||
virtio:
|
||||
description:
|
||||
- A hash/dictionary of volume used as VIRTIO hard disk. C(virtio='{"key":"value", "key":"value"}').
|
||||
@ -477,10 +553,6 @@ options:
|
||||
choices:
|
||||
- compatibility
|
||||
- no_defaults
|
||||
version_added: "1.3.0"
|
||||
extends_documentation_fragment:
|
||||
- xoxys.general.proxmox.documentation
|
||||
- xoxys.general.proxmox.selection
|
||||
""" # noqa
|
||||
|
||||
EXAMPLES = """
|
||||
@ -509,8 +581,8 @@ EXAMPLES = """
|
||||
name: spynal
|
||||
node: sabrewulf
|
||||
net:
|
||||
net0: 'virtio,bridge=vmbr1,rate=200'
|
||||
net1: 'e1000,bridge=vmbr2'
|
||||
net0: "virtio,bridge=vmbr1,rate=200"
|
||||
net1: "e1000,bridge=vmbr2"
|
||||
|
||||
- name: Create new VM with one network interface, three virto hard disk, 4 cores, and 2 vcpus
|
||||
xoxys.general.proxmox_kvm:
|
||||
@ -520,11 +592,11 @@ EXAMPLES = """
|
||||
name: spynal
|
||||
node: sabrewulf
|
||||
net:
|
||||
net0: 'virtio,bridge=vmbr1,rate=200'
|
||||
net0: "virtio,bridge=vmbr1,rate=200"
|
||||
virtio:
|
||||
virtio0: 'VMs_LVM:10'
|
||||
virtio1: 'VMs:2,format=qcow2'
|
||||
virtio2: 'VMs:5,format=raw'
|
||||
virtio0: "VMs_LVM:10"
|
||||
virtio1: "VMs:2,format=qcow2"
|
||||
virtio2: "VMs:5,format=raw"
|
||||
cores: 4
|
||||
vcpus: 2
|
||||
|
||||
@ -599,15 +671,15 @@ EXAMPLES = """
|
||||
api_host: helldorado
|
||||
name: spynal
|
||||
ide:
|
||||
ide2: 'local:cloudinit,format=qcow2'
|
||||
ide2: "local:cloudinit,format=qcow2"
|
||||
ciuser: mylinuxuser
|
||||
cipassword: supersecret
|
||||
searchdomains: 'mydomain.internal'
|
||||
searchdomains: "mydomain.internal"
|
||||
nameservers: 1.1.1.1
|
||||
net:
|
||||
net0: 'virtio,bridge=vmbr1,tag=77'
|
||||
net0: "virtio,bridge=vmbr1,tag=77"
|
||||
ipconfig:
|
||||
ipconfig0: 'ip=192.168.1.1/24,gw=192.168.1.1'
|
||||
ipconfig0: "ip=192.168.1.1/24,gw=192.168.1.1"
|
||||
|
||||
- name: Create new VM using Cloud-Init with an ssh key
|
||||
xoxys.general.proxmox_kvm:
|
||||
@ -617,16 +689,16 @@ EXAMPLES = """
|
||||
api_host: helldorado
|
||||
name: spynal
|
||||
ide:
|
||||
ide2: 'local:cloudinit,format=qcow2'
|
||||
sshkeys: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILJkVm98B71lD5XHfihwcYHE9TVpsJmK1vR1JcaU82L+'
|
||||
searchdomains: 'mydomain.internal'
|
||||
ide2: "local:cloudinit,format=qcow2"
|
||||
sshkeys: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILJkVm98B71lD5XHfihwcYHE9TVpsJmK1vR1JcaU82L+"
|
||||
searchdomains: "mydomain.internal"
|
||||
nameservers:
|
||||
- '1.1.1.1'
|
||||
- '8.8.8.8'
|
||||
- "1.1.1.1"
|
||||
- "8.8.8.8"
|
||||
net:
|
||||
net0: 'virtio,bridge=vmbr1,tag=77'
|
||||
net0: "virtio,bridge=vmbr1,tag=77"
|
||||
ipconfig:
|
||||
ipconfig0: 'ip=192.168.1.1/24'
|
||||
ipconfig0: "ip=192.168.1.1/24"
|
||||
|
||||
- name: Start VM
|
||||
xoxys.general.proxmox_kvm:
|
||||
@ -701,7 +773,7 @@ EXAMPLES = """
|
||||
api_host: helldorado
|
||||
name: spynal
|
||||
node: sabrewulf
|
||||
delete: 'args,template,cpulimit'
|
||||
delete: "args,template,cpulimit"
|
||||
|
||||
- name: Revert a pending change
|
||||
xoxys.general.proxmox_kvm:
|
||||
@ -710,8 +782,9 @@ EXAMPLES = """
|
||||
api_host: helldorado
|
||||
name: spynal
|
||||
node: sabrewulf
|
||||
revert: 'template,cpulimit'
|
||||
""" # noqa
|
||||
revert: "template,cpulimit"
|
||||
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
vmid:
|
||||
@ -735,9 +808,10 @@ import re
|
||||
import string
|
||||
import time
|
||||
import traceback
|
||||
from distutils.version import LooseVersion
|
||||
from ansible.module_utils.six.moves.urllib.parse import quote
|
||||
from collections import defaultdict
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
from ansible.module_utils.six.moves.urllib.parse import quote
|
||||
|
||||
try:
|
||||
from proxmoxer import ProxmoxAPI
|
||||
@ -745,17 +819,27 @@ try:
|
||||
except ImportError:
|
||||
HAS_PROXMOXER = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
try:
|
||||
from requests.packages import urllib3
|
||||
HAS_URLLIB3 = True
|
||||
except ImportError:
|
||||
try:
|
||||
import urllib3
|
||||
HAS_URLLIB3 = True
|
||||
except ImportError:
|
||||
HAS_URLLIB3 = False
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
|
||||
|
||||
def get_nextvmid(module, proxmox):
|
||||
try:
|
||||
vmid = proxmox.cluster.nextid.get()
|
||||
return vmid
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa
|
||||
module.fail_json(
|
||||
msg="Unable to get next vmid. Failed with exception: {}".format(to_native(e)),
|
||||
msg=f"Unable to get next vmid. Failed with exception: {to_native(e)}",
|
||||
exception=traceback.format_exc()
|
||||
)
|
||||
|
||||
@ -775,14 +859,13 @@ def node_check(proxmox, node):
|
||||
|
||||
|
||||
def get_vminfo(module, proxmox, node, vmid, **kwargs):
|
||||
global results # noqa
|
||||
global results
|
||||
results = {}
|
||||
try:
|
||||
vm = proxmox.nodes(node).qemu(vmid).config.get()
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa
|
||||
module.fail_json(
|
||||
msg="Getting information for VM with vmid = {} failed with exception: {}".
|
||||
format(vmid, e)
|
||||
msg=f"Getting information for VM with vmid = {vmid} failed with exception: {e}"
|
||||
)
|
||||
|
||||
# Sanitize kwargs. Remove not defined args and ensure True and False converted to int.
|
||||
@ -800,16 +883,13 @@ def get_vminfo(module, proxmox, node, vmid, **kwargs):
|
||||
results["_raw"] = vm
|
||||
|
||||
|
||||
def settings(module, proxmox, vmid, node, name, **kwargs):
|
||||
def settings(module, proxmox, vmid, node, name, **kwargs): # noqa
|
||||
proxmox_node = proxmox.nodes(node)
|
||||
|
||||
# Sanitize kwargs. Remove not defined args and ensure True and False converted to int.
|
||||
kwargs = dict((k, v) for k, v in kwargs.items() if v is not None)
|
||||
|
||||
if proxmox_node.qemu(vmid).config.set(**kwargs) is None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return (proxmox_node.qemu(vmid).config.set(**kwargs) is None)
|
||||
|
||||
|
||||
def wait_for_task(module, proxmox, node, taskid):
|
||||
@ -840,7 +920,7 @@ def create_vm(
|
||||
clone_params = {}
|
||||
# Default args for vm. Note: -args option is for experts only. It allows you
|
||||
# to pass arbitrary arguments to kvm.
|
||||
vm_args = "-serial unix:/var/run/qemu-server/{0}.serial,server,nowait".format(vmid)
|
||||
vm_args = f"-serial unix:/var/run/qemu-server/{vmid}.serial,server,nowait"
|
||||
|
||||
proxmox_node = proxmox.nodes(node)
|
||||
|
||||
@ -849,13 +929,13 @@ def create_vm(
|
||||
kwargs.update(dict([k, int(v)] for k, v in kwargs.items() if isinstance(v, bool)))
|
||||
|
||||
# The features work only on PVE 4+
|
||||
if PVE_MAJOR_VERSION < 4: # noqa
|
||||
if PVE_MAJOR_VERSION < 4:
|
||||
for p in only_v4:
|
||||
if p in kwargs:
|
||||
del kwargs[p]
|
||||
|
||||
# The features work only on PVE 6
|
||||
if PVE_MAJOR_VERSION < 6: # noqa
|
||||
if PVE_MAJOR_VERSION < 6:
|
||||
for p in only_v6:
|
||||
if p in kwargs:
|
||||
del kwargs[p]
|
||||
@ -877,17 +957,17 @@ def create_vm(
|
||||
if update:
|
||||
for k, v in disks.items():
|
||||
if results["disks"].get(k):
|
||||
kwargs[k.rstrip(string.digits)][k] = "{0}:{1},{2}".format(
|
||||
results["disks"][k]["storage_id"], results["disks"][k]["storage_opts"],
|
||||
",".join(disks[k]["opts"])
|
||||
)
|
||||
storage_id = results["disks"][k]["storage_id"]
|
||||
storage_opts = results["disks"][k]["storage_opts"]
|
||||
opts = ",".join(v["opts"])
|
||||
kwargs[k.rstrip(string.digits)][k] = f"{storage_id}:{storage_opts},{opts}"
|
||||
|
||||
for k, v in nets.items():
|
||||
if results["nets"].get(k):
|
||||
kwargs[k.rstrip(string.digits)][k] = "{0}={1},{2}".format(
|
||||
results["nets"][k]["net_id"], results["nets"][k]["net_opts"],
|
||||
",".join(nets[k]["opts"])
|
||||
)
|
||||
net_id = results["nets"][k]["net_id"]
|
||||
net_opts = results["nets"][k]["net_opts"]
|
||||
opts = ",".join(v["opts"])
|
||||
kwargs[k.rstrip(string.digits)][k] = f"{net_id}={net_opts},{opts}"
|
||||
|
||||
# Convert all dict in kwargs to elements.
|
||||
for k in list(kwargs.keys()):
|
||||
@ -914,12 +994,14 @@ def create_vm(
|
||||
if "tags" in kwargs:
|
||||
for tag in kwargs["tags"]:
|
||||
if not re.match(r"^[a-z0-9_][a-z0-9_\-\+\.]*$", tag):
|
||||
module.fail_json(msg="{} is not a valid tag".format(tag))
|
||||
module.fail_json(msg=f"{tag} is not a valid tag")
|
||||
kwargs["tags"] = ",".join(kwargs["tags"])
|
||||
|
||||
# -args and skiplock require root@pam user - but can not use api tokens
|
||||
if module.params["api_user"] == "root@pam" and module.params["args"] is None:
|
||||
if not update and module.params["proxmox_default_behavior"] == "compatibility":
|
||||
if (
|
||||
module.params["api_user"] == "root@pam" and module.params["args"] is None and not update
|
||||
and module.params["proxmox_default_behavior"] == "compatibility"
|
||||
):
|
||||
kwargs["args"] = vm_args
|
||||
elif module.params["api_user"] == "root@pam" and module.params["args"] is not None:
|
||||
kwargs["args"] = module.params["args"]
|
||||
@ -930,13 +1012,12 @@ def create_vm(
|
||||
module.fail_json(msg="skiplock parameter require root@pam user. ")
|
||||
|
||||
if update:
|
||||
if proxmox_node.qemu(vmid).config.set(
|
||||
name=name, memory=memory, cpu=cpu, cores=cores, sockets=sockets, **kwargs
|
||||
) is None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
elif module.params["clone"] is not None:
|
||||
return (
|
||||
proxmox_node.qemu(vmid).config.
|
||||
set(name=name, memory=memory, cpu=cpu, cores=cores, sockets=sockets, **kwargs) is None
|
||||
)
|
||||
|
||||
if module.params["clone"] is not None:
|
||||
for param in valid_clone_params:
|
||||
if module.params[param] is not None:
|
||||
clone_params[param] = module.params[param]
|
||||
@ -952,8 +1033,7 @@ def create_vm(
|
||||
if not wait_for_task(module, proxmox, node, taskid):
|
||||
module.fail_json(
|
||||
msg="Reached timeout while waiting for creating VM."
|
||||
"Last line in task before timeout: {}".
|
||||
format(proxmox_node.tasks(taskid).log.get()[:1])
|
||||
f"Last line in task before timeout: {proxmox_node.tasks(taskid).log.get()[:1]}"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
@ -966,8 +1046,7 @@ def start_vm(module, proxmox, vm):
|
||||
if not wait_for_task(module, proxmox, vm[0]["node"], taskid):
|
||||
module.fail_json(
|
||||
msg="Reached timeout while waiting for starting VM."
|
||||
"Last line in task before timeout: {}".
|
||||
format(proxmox_node.tasks(taskid).log.get()[:1])
|
||||
f"Last line in task before timeout: {proxmox_node.tasks(taskid).log.get()[:1]}"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
@ -980,8 +1059,7 @@ def stop_vm(module, proxmox, vm, force):
|
||||
if not wait_for_task(module, proxmox, vm[0]["node"], taskid):
|
||||
module.fail_json(
|
||||
msg="Reached timeout while waiting for stopping VM."
|
||||
"Last line in task before timeout: {}".
|
||||
format(proxmox_node.tasks(taskid).log.get()[:1])
|
||||
f"Last line in task before timeout: {proxmox_node.tasks(taskid).log.get()[:1]}"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
@ -998,7 +1076,7 @@ def main():
|
||||
acpi=dict(type="bool"),
|
||||
agent=dict(type="bool"),
|
||||
args=dict(type="str"),
|
||||
api_host=dict(required=True),
|
||||
api_host=dict(required=True, type="str"),
|
||||
api_password=dict(no_log=True, fallback=(env_fallback, ["PROXMOX_PASSWORD"])),
|
||||
api_token_id=dict(no_log=True),
|
||||
api_token_secret=dict(no_log=True),
|
||||
@ -1044,7 +1122,7 @@ def main():
|
||||
nameservers=dict(type="list", elements="str"),
|
||||
net=dict(type="dict"),
|
||||
newid=dict(type="int"),
|
||||
node=dict(),
|
||||
node=dict(type="str"),
|
||||
numa=dict(type="dict"),
|
||||
numa_enabled=dict(type="bool"),
|
||||
onboot=dict(type="bool"),
|
||||
@ -1076,7 +1154,7 @@ def main():
|
||||
sockets=dict(type="int"),
|
||||
sshkeys=dict(type="str", no_log=False),
|
||||
startdate=dict(type="str"),
|
||||
startup=dict(),
|
||||
startup=dict(type="str"),
|
||||
state=dict(
|
||||
default="present",
|
||||
choices=["present", "absent", "stopped", "started", "restarted", "current"]
|
||||
@ -1089,7 +1167,7 @@ def main():
|
||||
template=dict(type="bool"),
|
||||
timeout=dict(type="int", default=30),
|
||||
update=dict(type="bool", default=False),
|
||||
validate_certs=dict(type="bool", default=False),
|
||||
verify_ssl=dict(type="bool", default=True),
|
||||
vcpus=dict(type="int"),
|
||||
vga=dict(
|
||||
choices=[
|
||||
@ -1099,7 +1177,7 @@ def main():
|
||||
),
|
||||
virtio=dict(type="dict"),
|
||||
vmid=dict(type="int"),
|
||||
watchdog=dict(),
|
||||
watchdog=dict(type="str"),
|
||||
proxmox_default_behavior=dict(type="str", choices=["compatibility", "no_defaults"]),
|
||||
),
|
||||
mutually_exclusive=[("delete", "revert"), ("delete", "update"), ("revert", "update"),
|
||||
@ -1130,7 +1208,7 @@ def main():
|
||||
state = module.params["state"]
|
||||
update = bool(module.params["update"])
|
||||
vmid = module.params["vmid"]
|
||||
validate_certs = module.params["validate_certs"]
|
||||
verify_ssl = module.params["verify_ssl"]
|
||||
|
||||
if module.params["proxmox_default_behavior"] is None:
|
||||
module.params["proxmox_default_behavior"] = "compatibility"
|
||||
@ -1166,15 +1244,16 @@ def main():
|
||||
auth_args["token_name"] = api_token_id
|
||||
auth_args["token_value"] = api_token_secret
|
||||
|
||||
if not verify_ssl and HAS_URLLIB3:
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
try:
|
||||
proxmox = ProxmoxAPI(api_host, verify_ssl=validate_certs, **auth_args)
|
||||
global PVE_MAJOR_VERSION # noqa
|
||||
proxmox = ProxmoxAPI(api_host, verify_ssl=verify_ssl, **auth_args)
|
||||
global PVE_MAJOR_VERSION
|
||||
version = proxmox_version(proxmox)
|
||||
PVE_MAJOR_VERSION = 3 if version < LooseVersion("4.0") else version.version[0]
|
||||
except Exception as e:
|
||||
module.fail_json(
|
||||
msg="authorization on proxmox cluster failed with exception: {}".format(e)
|
||||
)
|
||||
except Exception as e: # noqa
|
||||
module.fail_json(msg=f"authorization on proxmox cluster failed with exception: {e}")
|
||||
|
||||
# If vmid is not defined then retrieve its value from the vm name,
|
||||
# the cloned vm name or retrieve the next free VM id from ProxmoxAPI.
|
||||
@ -1182,16 +1261,16 @@ def main():
|
||||
if state == "present" and not update and not clone and not delete and not revert:
|
||||
try:
|
||||
vmid = get_nextvmid(module, proxmox)
|
||||
except Exception:
|
||||
except Exception: # noqa
|
||||
module.fail_json(
|
||||
msg="Can't get the next vmid for VM {0} automatically."
|
||||
"Ensure your cluster state is good".format(name)
|
||||
msg=f"Can't get the next vmid for VM {name} automatically."
|
||||
"Ensure your cluster state is good"
|
||||
)
|
||||
else:
|
||||
clone_target = clone or name
|
||||
try:
|
||||
vmid = get_vmid(proxmox, clone_target)[0]
|
||||
except Exception:
|
||||
except Exception: # noqa
|
||||
vmid = -1
|
||||
|
||||
if clone is not None:
|
||||
@ -1199,50 +1278,40 @@ def main():
|
||||
if not newid:
|
||||
try:
|
||||
newid = get_nextvmid(module, proxmox)
|
||||
except Exception:
|
||||
except Exception: # noqa
|
||||
module.fail_json(
|
||||
msg="Can't get the next vmid for VM {0} automatically."
|
||||
"Ensure your cluster state is good".format(name)
|
||||
msg=f"Can't get the next vmid for VM {name} automatically."
|
||||
"Ensure your cluster state is good"
|
||||
)
|
||||
|
||||
# Ensure source VM name exists when cloning
|
||||
if -1 == vmid:
|
||||
module.fail_json(msg="VM with name = {} does not exist in cluster".format(clone))
|
||||
module.fail_json(msg=f"VM with name = {clone} does not exist in cluster")
|
||||
|
||||
# Ensure source VM id exists when cloning
|
||||
if not get_vm(proxmox, vmid):
|
||||
module.fail_json(
|
||||
vmid=vmid, msg="VM with vmid = {} does not exist in cluster".format(vmid)
|
||||
)
|
||||
module.fail_json(vmid=vmid, msg=f"VM with vmid = {vmid} does not exist in cluster")
|
||||
|
||||
# Ensure the choosen VM name doesn't already exist when cloning
|
||||
if get_vmid(proxmox, name):
|
||||
module.exit_json(
|
||||
changed=False, vmid=vmid, msg="VM with name <{}> already exists".format(name)
|
||||
)
|
||||
module.exit_json(changed=False, vmid=vmid, msg=f"VM with name <{name}> already exists")
|
||||
|
||||
# Ensure the choosen VM id doesn't already exist when cloning
|
||||
if get_vm(proxmox, newid):
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
vmid=vmid,
|
||||
msg="vmid {} with VM name {} already exists".format(newid, name)
|
||||
changed=False, vmid=vmid, msg=f"vmid {newid} with VM name {name} already exists"
|
||||
)
|
||||
|
||||
if delete is not None:
|
||||
try:
|
||||
settings(module, proxmox, vmid, node, name, delete=delete)
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
vmid=vmid,
|
||||
msg="Settings has deleted on VM {0} with vmid {1}".format(name, vmid)
|
||||
changed=True, vmid=vmid, msg=f"Settings has deleted on VM {name} with vmid {vmid}"
|
||||
)
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa
|
||||
module.fail_json(
|
||||
vmid=vmid,
|
||||
msg="Unable to delete settings on VM {0} with vmid {1}: {2}".format(
|
||||
name, vmid, str(e)
|
||||
)
|
||||
msg=f"Unable to delete settings on VM {name} with vmid {vmid}: {str(e)}"
|
||||
)
|
||||
|
||||
if revert is not None:
|
||||
@ -1251,29 +1320,29 @@ def main():
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
vmid=vmid,
|
||||
msg="Settings has reverted on VM {0} with vmid {1}".format(name, vmid)
|
||||
msg=f"Settings has reverted on VM {name} with vmid {vmid}"
|
||||
)
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa
|
||||
module.fail_json(
|
||||
vmid=vmid,
|
||||
msg="Unable to revert settings on VM {0} with vmid {1}:"
|
||||
"Maybe is not a pending task...".format(name, vmid) + str(e)
|
||||
msg=f"Unable to revert settings on VM {name} with vmid {vmid}:"
|
||||
f"Maybe is not a pending task...{str(e)}"
|
||||
)
|
||||
|
||||
if state == "present":
|
||||
try:
|
||||
if get_vm(proxmox, vmid) and not (update or clone):
|
||||
module.exit_json(
|
||||
changed=False, vmid=vmid, msg="VM with vmid <{}> already exists".format(vmid)
|
||||
changed=False, vmid=vmid, msg=f"VM with vmid <{vmid}> already exists"
|
||||
)
|
||||
elif get_vmid(proxmox, name) and not (update or clone):
|
||||
module.exit_json(
|
||||
changed=False, vmid=vmid, msg="VM with name <{}> already exists".format(name)
|
||||
changed=False, vmid=vmid, msg=f"VM with name <{name}> already exists"
|
||||
)
|
||||
elif not (node, name):
|
||||
module.fail_json(msg="node, name is mandatory for creating/updating vm")
|
||||
elif not node_check(proxmox, node):
|
||||
module.fail_json(msg="node '{}' does not exist in cluster".format(node))
|
||||
module.fail_json(msg=f"node '{node}' does not exist in cluster")
|
||||
|
||||
if not clone:
|
||||
get_vminfo(
|
||||
@ -1362,130 +1431,100 @@ def main():
|
||||
|
||||
if update:
|
||||
module.exit_json(
|
||||
changed=True, vmid=vmid, msg="VM {} with vmid {} updated".format(name, vmid)
|
||||
changed=True, vmid=vmid, msg=f"VM {name} with vmid {vmid} updated"
|
||||
)
|
||||
elif clone is not None:
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
vmid=vmid,
|
||||
msg="VM {} with newid {} cloned from vm with vmid {}".format(
|
||||
name, newid, vmid
|
||||
)
|
||||
msg=f"VM {name} with newid {newid} cloned from vm with vmid {vmid}"
|
||||
)
|
||||
else:
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
msg="VM {} with vmid {} deployed".format(name, vmid),
|
||||
**results # noqa
|
||||
changed=True, msg=f"VM {name} with vmid {vmid} deployed", **results
|
||||
)
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa
|
||||
if update:
|
||||
module.fail_json(
|
||||
vmid=vmid,
|
||||
msg="Unable to update vm {0} with vmid {1}=".format(name, vmid) + str(e)
|
||||
vmid=vmid, msg=f"Unable to update vm {name} with vmid {vmid}=" + str(e)
|
||||
)
|
||||
elif clone is not None:
|
||||
module.fail_json(
|
||||
vmid=vmid,
|
||||
msg="Unable to clone vm {0} from vmid {1}=".format(name, vmid) + str(e)
|
||||
vmid=vmid, msg=f"Unable to clone vm {name} from vmid {vmid}=" + str(e)
|
||||
)
|
||||
else:
|
||||
module.fail_json(
|
||||
vmid=vmid,
|
||||
msg="creation of qemu VM {} with vmid {} failed with exception={}".format(
|
||||
name, vmid, e
|
||||
)
|
||||
msg=f"creation of qemu VM {name} with vmid {vmid} failed with exception={e}"
|
||||
)
|
||||
|
||||
elif state == "started":
|
||||
status = {}
|
||||
try:
|
||||
if -1 == vmid:
|
||||
module.fail_json(msg="VM with name = {} does not exist in cluster".format(name))
|
||||
module.fail_json(msg=f"VM with name = {name} does not exist in cluster")
|
||||
vm = get_vm(proxmox, vmid)
|
||||
if not vm:
|
||||
module.fail_json(
|
||||
vmid=vmid, msg="VM with vmid <{}> does not exist in cluster".format(vmid)
|
||||
)
|
||||
module.fail_json(vmid=vmid, msg=f"VM with vmid <{vmid}> does not exist in cluster")
|
||||
status["status"] = vm[0]["status"]
|
||||
if vm[0]["status"] == "running":
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
vmid=vmid,
|
||||
msg="VM {} is already running".format(vmid),
|
||||
**status
|
||||
changed=False, vmid=vmid, msg=f"VM {vmid} is already running", **status
|
||||
)
|
||||
|
||||
if start_vm(module, proxmox, vm):
|
||||
module.exit_json(
|
||||
changed=True, vmid=vmid, msg="VM {} started".format(vmid), **status
|
||||
)
|
||||
except Exception as e:
|
||||
module.exit_json(changed=True, vmid=vmid, msg=f"VM {vmid} started", **status)
|
||||
except Exception as e: # noqa
|
||||
module.fail_json(
|
||||
vmid=vmid,
|
||||
msg="starting of VM {} failed with exception: {}".format(vmid, e),
|
||||
**status
|
||||
vmid=vmid, msg=f"starting of VM {vmid} failed with exception: {e}", **status
|
||||
)
|
||||
|
||||
elif state == "stopped":
|
||||
status = {}
|
||||
try:
|
||||
if -1 == vmid:
|
||||
module.fail_json(msg="VM with name = {} does not exist in cluster".format(name))
|
||||
module.fail_json(msg=f"VM with name = {name} does not exist in cluster")
|
||||
|
||||
vm = get_vm(proxmox, vmid)
|
||||
if not vm:
|
||||
module.fail_json(
|
||||
vmid=vmid, msg="VM with vmid = {} does not exist in cluster".format(vmid)
|
||||
)
|
||||
module.fail_json(vmid=vmid, msg=f"VM with vmid = {vmid} does not exist in cluster")
|
||||
|
||||
status["status"] = vm[0]["status"]
|
||||
if vm[0]["status"] == "stopped":
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
vmid=vmid,
|
||||
msg="VM {} is already stopped".format(vmid),
|
||||
**status
|
||||
changed=False, vmid=vmid, msg=f"VM {vmid} is already stopped", **status
|
||||
)
|
||||
|
||||
if stop_vm(module, proxmox, vm, force=module.params["force"]):
|
||||
module.exit_json(
|
||||
changed=True, vmid=vmid, msg="VM {} is shutting down".format(vmid), **status
|
||||
changed=True, vmid=vmid, msg=f"VM {vmid} is shutting down", **status
|
||||
)
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa
|
||||
module.fail_json(
|
||||
vmid=vmid,
|
||||
msg="stopping of VM {} failed with exception: {}".format(vmid, e),
|
||||
**status
|
||||
vmid=vmid, msg=f"stopping of VM {vmid} failed with exception: {e}", **status
|
||||
)
|
||||
|
||||
elif state == "restarted":
|
||||
status = {}
|
||||
try:
|
||||
if -1 == vmid:
|
||||
module.fail_json(msg="VM with name = {} does not exist in cluster".format(name))
|
||||
module.fail_json(msg=f"VM with name = {name} does not exist in cluster")
|
||||
|
||||
vm = get_vm(proxmox, vmid)
|
||||
if not vm:
|
||||
module.fail_json(
|
||||
vmid=vmid, msg="VM with vmid = {} does not exist in cluster".format(vmid)
|
||||
)
|
||||
module.fail_json(vmid=vmid, msg=f"VM with vmid = {vmid} does not exist in cluster")
|
||||
status["status"] = vm[0]["status"]
|
||||
if vm[0]["status"] == "stopped":
|
||||
module.exit_json(
|
||||
changed=False, vmid=vmid, msg="VM {} is not running".format(vmid), **status
|
||||
changed=False, vmid=vmid, msg=f"VM {vmid} is not running", **status
|
||||
)
|
||||
|
||||
if stop_vm(module, proxmox, vm,
|
||||
force=module.params["force"]) and start_vm(module, proxmox, vm):
|
||||
module.exit_json(
|
||||
changed=True, vmid=vmid, msg="VM {} is restarted".format(vmid), **status
|
||||
)
|
||||
except Exception as e:
|
||||
module.exit_json(changed=True, vmid=vmid, msg=f"VM {vmid} is restarted", **status)
|
||||
except Exception as e: # noqa
|
||||
module.fail_json(
|
||||
vmid=vmid,
|
||||
msg="restarting of VM {} failed with exception: {}".format(vmid, e),
|
||||
**status
|
||||
vmid=vmid, msg=f"restarting of VM {vmid} failed with exception: {e}", **status
|
||||
)
|
||||
|
||||
elif state == "absent":
|
||||
@ -1504,28 +1543,26 @@ def main():
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
vmid=vmid,
|
||||
msg="VM {} is running. Stop it before deletion or use force=yes.".
|
||||
format(vmid)
|
||||
msg=f"VM {vmid} is running. Stop it before deletion or use force=yes."
|
||||
)
|
||||
taskid = proxmox_node.qemu.delete(vmid)
|
||||
if not wait_for_task(module, proxmox, vm[0]["node"], taskid):
|
||||
module.fail_json(
|
||||
msg="Reached timeout while waiting for removing VM."
|
||||
"Last line in task before timeout: {}".
|
||||
format(proxmox_node.tasks(taskid).log.get()[:1])
|
||||
f"Last line in task before timeout: {proxmox_node.tasks(taskid).log.get()[:1]}"
|
||||
)
|
||||
else:
|
||||
module.exit_json(changed=True, vmid=vmid, msg="VM {} removed".format(vmid))
|
||||
except Exception as e:
|
||||
module.fail_json(msg="deletion of VM {} failed with exception: {}".format(vmid, e))
|
||||
module.exit_json(changed=True, vmid=vmid, msg=f"VM {vmid} removed")
|
||||
except Exception as e: # noqa
|
||||
module.fail_json(msg=f"deletion of VM {vmid} failed with exception: {e}")
|
||||
|
||||
elif state == "current":
|
||||
status = {}
|
||||
if -1 == vmid:
|
||||
module.fail_json(msg="VM with name = {} does not exist in cluster".format(name))
|
||||
module.fail_json(msg=f"VM with name = {name} does not exist in cluster")
|
||||
vm = get_vm(proxmox, vmid)
|
||||
if not vm:
|
||||
module.fail_json(msg="VM with vmid = {} does not exist in cluster".format(vmid))
|
||||
module.fail_json(msg=f"VM with vmid = {vmid} does not exist in cluster")
|
||||
if not name:
|
||||
name = vm[0]["name"]
|
||||
current = proxmox.nodes(vm[0]["node"]).qemu(vmid).status.current.get()["status"]
|
||||
@ -1534,7 +1571,7 @@ def main():
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
vmid=vmid,
|
||||
msg="VM {} with vmid = {} is {}".format(name, vmid, current),
|
||||
msg=f"VM {name} with vmid = {vmid} is {current}",
|
||||
**status
|
||||
)
|
||||
|
||||
@ -1561,8 +1598,10 @@ def _extract_nets(item):
|
||||
if re.match(r"net[0-9]", k):
|
||||
nets[k]["opts"] = []
|
||||
for val in v.split(","):
|
||||
if any(val.startswith(s) for s in ["e1000", "rtl8139", "virtio", "vmxnet3"]):
|
||||
if len(val.split("=")) == 2:
|
||||
if (
|
||||
any(val.startswith(s) for s in ["e1000", "rtl8139", "virtio", "vmxnet3"])
|
||||
and len(val.split("=")) == 2
|
||||
):
|
||||
net = val.split("=")
|
||||
nets[k]["net_id"] = net[0]
|
||||
nets[k]["net_opts"] = net[1]
|
||||
|
@ -1,5 +1,12 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Module to control Univention Corporate Registry."""
|
||||
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""Control Univention Corporate Registry."""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"}
|
||||
|
||||
@ -7,7 +14,7 @@ DOCUMENTATION = """
|
||||
---
|
||||
module: ucr
|
||||
short_description: Manage variables in univention configuration registry.
|
||||
version_added: "2.6"
|
||||
version_added: 1.1.0
|
||||
description:
|
||||
- "This module allows to manage variables inside the univention configuration registry
|
||||
on a univention corporate server (UCS)."
|
||||
@ -15,16 +22,21 @@ options:
|
||||
path:
|
||||
description:
|
||||
- Path for the variable
|
||||
aliases:
|
||||
- name
|
||||
required: True
|
||||
default: null
|
||||
type: str
|
||||
value:
|
||||
description:
|
||||
- New value of the variable
|
||||
required: False
|
||||
type: str
|
||||
default: ""
|
||||
state:
|
||||
required: False
|
||||
default: "present"
|
||||
choices: ["present", "absent"]
|
||||
type: str
|
||||
description:
|
||||
- Whether the variable should be exist or not.
|
||||
author:
|
||||
@ -49,39 +61,41 @@ RETURN = """
|
||||
original_message:
|
||||
description: The original name param that was passed in
|
||||
type: str
|
||||
returned: success
|
||||
message:
|
||||
description: The output message that the sample module generates
|
||||
type: str
|
||||
returned: success
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from univention.config_registry import ConfigRegistry # noqa
|
||||
from univention.config_registry.frontend import ucr_update # noqa
|
||||
|
||||
try:
|
||||
from univention.config_registry import ConfigRegistry
|
||||
from univention.config_registry.frontend import ucr_update
|
||||
HAS_UNIVENTION = True
|
||||
except ImportError:
|
||||
HAS_UNIVENTION = False
|
||||
|
||||
|
||||
def get_variable(ucr, path):
|
||||
ucr.load()
|
||||
if path in ucr:
|
||||
value = ucr.get(path)
|
||||
else:
|
||||
value = None
|
||||
return value
|
||||
return ucr.get(path) if path in ucr else None
|
||||
|
||||
|
||||
def set_variable(ucr, path, value, result):
|
||||
def set_variable(ucr, path, value, result): # noqa
|
||||
org_value = get_variable(ucr, path)
|
||||
ucr_update(ucr, {path: value})
|
||||
new_value = get_variable(ucr, path)
|
||||
return not org_value == new_value
|
||||
return org_value != new_value
|
||||
|
||||
|
||||
def dry_variable(ucr, path, value, result):
|
||||
def dry_variable(ucr, path, value, result): # noqa
|
||||
org_value = get_variable(ucr, path)
|
||||
return not org_value == value
|
||||
return org_value != value
|
||||
|
||||
|
||||
def main():
|
||||
ucr = ConfigRegistry()
|
||||
|
||||
module_args = dict(
|
||||
path=dict(type="str", required=True, aliases=["name"]),
|
||||
value=dict(type="str", required=False, default=""),
|
||||
@ -94,12 +108,16 @@ def main():
|
||||
argument_spec=module_args, supports_check_mode=True, required_if=required_if
|
||||
)
|
||||
|
||||
if not HAS_UNIVENTION:
|
||||
module.fail_json(msg="univention required for this module")
|
||||
|
||||
ucr = ConfigRegistry()
|
||||
|
||||
result = dict(changed=False, original_message="", message="")
|
||||
|
||||
path = module.params["path"]
|
||||
value = module.params["value"]
|
||||
if module.params["state"] == "present":
|
||||
if value is None or value == "None":
|
||||
if module.params["state"] == "present" and (value is None or value == "None"):
|
||||
value = ""
|
||||
elif module.params["state"] == "absent":
|
||||
value = None
|
||||
|
1143
poetry.lock
generated
Normal file
1143
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
149
pyproject.toml
Normal file
149
pyproject.toml
Normal file
@ -0,0 +1,149 @@
|
||||
[tool.poetry]
|
||||
authors = ["Robert Kaussow <mail@thegeeklab.de>"]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Console",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Information Technology",
|
||||
"Intended Audience :: System Administrators",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: POSIX",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Topic :: Utilities",
|
||||
"Topic :: Software Development",
|
||||
"Topic :: Software Development :: Documentation",
|
||||
]
|
||||
description = "Build environment for Ansible Collection."
|
||||
license = "MIT"
|
||||
name = "xoxys.general"
|
||||
readme = "README.md"
|
||||
repository = "https://gitea.rknet.org/ansible/xoxys.general"
|
||||
version = "0.0.0"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8.0"
|
||||
ansible-core = { version = "<=2.14.0", optional = true }
|
||||
pyopenssl = "23.0.0"
|
||||
proxmoxer = "2.0.1"
|
||||
hcloud = "1.18.2"
|
||||
|
||||
[tool.poetry.extras]
|
||||
ansible = ["ansible-core"]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "0.0.230"
|
||||
pytest = "7.2.1"
|
||||
pytest-mock = "3.10.0"
|
||||
pytest-cov = "4.0.0"
|
||||
toml = "0.10.2"
|
||||
yapf = "0.32.0"
|
||||
pycodestyle = "2.10.0"
|
||||
yamllint = "1.29.0"
|
||||
pylint = "2.15.0"
|
||||
voluptuous = "0.13.1"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "--cov --cov-report=xml:coverage.xml --cov-report=term --cov-append --no-cov-on-fail"
|
||||
pythonpath = [
|
||||
"."
|
||||
]
|
||||
testpaths = [
|
||||
"tests",
|
||||
]
|
||||
filterwarnings = [
|
||||
"ignore::FutureWarning",
|
||||
"ignore::DeprecationWarning",
|
||||
"ignore:.*pep8.*:FutureWarning",
|
||||
]
|
||||
|
||||
[tool.coverage.run]
|
||||
omit = ["**/tests/*"]
|
||||
|
||||
[build-system]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
|
||||
|
||||
[tool.ruff]
|
||||
exclude = [
|
||||
".git",
|
||||
"__pycache__",
|
||||
"build",
|
||||
"dist",
|
||||
"tests",
|
||||
"*.pyc",
|
||||
"*.egg-info",
|
||||
".cache",
|
||||
".eggs",
|
||||
"env*",
|
||||
"iptables_raw.py",
|
||||
]
|
||||
# Explanation of errors
|
||||
#
|
||||
# D102: Missing docstring in public method
|
||||
# D103: Missing docstring in public function
|
||||
# D105: Missing docstring in magic method
|
||||
# D107: Missing docstring in __init__
|
||||
# D202: No blank lines allowed after function docstring
|
||||
# D203: One blank line required before class docstring
|
||||
# E402: Module level import not at top of file
|
||||
# SIM105: Use `contextlib.suppress(Exception)` instead of try-except-pass
|
||||
# C402: Unnecessary generator (rewrite as a `dict` comprehension)
|
||||
# C408: Unnecessary `dict` call (rewrite as a literal)
|
||||
# I001: Import block is un-sorted or un-formatted
|
||||
# UP001: `__metaclass__ = type` is implied
|
||||
# UP009: UTF-8 encoding declaration is unnecessary
|
||||
# UP010: Unnecessary `__future__` imports `absolute_import`, `division`, `print_function` for target Python version
|
||||
ignore = [
|
||||
"D102",
|
||||
"D103",
|
||||
"D105",
|
||||
"D107",
|
||||
"D202",
|
||||
"D203",
|
||||
"D212",
|
||||
"E402",
|
||||
"SIM105",
|
||||
"C402",
|
||||
"C408",
|
||||
"I001",
|
||||
"UP001",
|
||||
"UP009",
|
||||
"UP010",
|
||||
]
|
||||
line-length = 99
|
||||
select = [
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"Q",
|
||||
"W",
|
||||
"I",
|
||||
"S",
|
||||
"BLE",
|
||||
"N",
|
||||
"UP",
|
||||
"B",
|
||||
"A",
|
||||
"C4",
|
||||
"T20",
|
||||
"SIM",
|
||||
"RET",
|
||||
"ARG",
|
||||
"ERA",
|
||||
"RUF",
|
||||
]
|
||||
|
||||
[tool.ruff.flake8-quotes]
|
||||
inline-quotes = "double"
|
||||
|
||||
[tool.yapf]
|
||||
based_on_style = "google"
|
||||
column_limit = 99
|
||||
dedent_closing_brackets = true
|
||||
coalesce_brackets = true
|
||||
split_before_logical_operator = true
|
@ -1,4 +0,0 @@
|
||||
ansible-core
|
||||
pyopenssl
|
||||
proxmoxer
|
||||
hcloud
|
42
setup.cfg
42
setup.cfg
@ -1,42 +0,0 @@
|
||||
[isort]
|
||||
default_section = THIRDPARTY
|
||||
sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
|
||||
force_single_line = true
|
||||
line_length = 99
|
||||
skip_glob = **/.venv*,**/venv/*,**/docs/*,**/inventory/*,**/modules/*
|
||||
|
||||
[yapf]
|
||||
based_on_style = google
|
||||
column_limit = 99
|
||||
dedent_closing_brackets = true
|
||||
coalesce_brackets = true
|
||||
split_before_logical_operator = true
|
||||
|
||||
[tool:pytest]
|
||||
filterwarnings =
|
||||
ignore::FutureWarning
|
||||
ignore:.*collections.*:DeprecationWarning
|
||||
ignore:.*pep8.*:FutureWarning
|
||||
|
||||
[coverage:run]
|
||||
omit =
|
||||
**/test/*
|
||||
**/.venv/*
|
||||
|
||||
[flake8]
|
||||
ignore = D101, D102, D103, D105, D107, D202, E402, W503, B902
|
||||
max-line-length = 99
|
||||
inline-quotes = double
|
||||
exclude =
|
||||
.git
|
||||
.tox
|
||||
__pycache__
|
||||
build
|
||||
dist
|
||||
test
|
||||
*.pyc
|
||||
*.egg-info
|
||||
.cache
|
||||
.eggs
|
||||
env*
|
||||
iptables_raw.py
|
@ -1 +0,0 @@
|
||||
# noqa
|
@ -1,11 +0,0 @@
|
||||
ansible
|
||||
|
||||
# requirement for the proxmox modules
|
||||
proxmoxer
|
||||
requests
|
||||
|
||||
# requirement for the corenetworks modules
|
||||
corenetworks
|
||||
|
||||
# requirement for the openssl_pkcs12 module
|
||||
pyOpenSSL
|
2
tests/config.yml
Normal file
2
tests/config.yml
Normal file
@ -0,0 +1,2 @@
|
||||
modules:
|
||||
python_requires: ">=3.8"
|
0
tests/unit/plugins/inventory/__init__.py
Normal file
0
tests/unit/plugins/inventory/__init__.py
Normal file
@ -1,10 +1,11 @@
|
||||
"""Test inventory plugin proxmox."""
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Robert Kaussow <mail@thegeeklab.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
|
||||
|
||||
import pytest
|
||||
|
||||
proxmox = pytest.importorskip("proxmoxer")
|
||||
@ -58,7 +59,7 @@ def test_get_ip_address(inventory, mocker):
|
||||
inventory.client = mocker.MagicMock()
|
||||
inventory.client.nodes.return_value.get.return_value = networks
|
||||
|
||||
assert "10.0.0.1" == inventory._get_ip_address("qemu", None, None)
|
||||
assert inventory._get_ip_address("qemu", None, None) == "10.0.0.1"
|
||||
|
||||
|
||||
def test_exclude(inventory, mocker):
|
Loading…
Reference in New Issue
Block a user