diff --git a/.vscode/settings.json b/.vscode/settings.json index c3a915e8..a13692ca 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,6 +19,5 @@ "./f/ansible-requirements.json": ["requirements.yml"], "./f/zuul.json": ["zuul.d/**/*.yml", "zuul.d/**/*.yaml"], "./f/ansible-playbook.json": ["playbooks/*.yml", "playbooks/*.yaml"] - }, - "python.formatting.provider": "black" + } } diff --git a/examples/ansible-navigator.yml b/examples/ansible-navigator.yml new file mode 100644 index 00000000..584947a9 --- /dev/null +++ b/examples/ansible-navigator.yml @@ -0,0 +1,80 @@ +--- +ansible-navigator: + ansible: + config: /tmp/ansible.cfg + cmdline: "--forks 15" + inventories: + - /tmp/test_inventory.yml + playbook: /tmp/test_playbook.yml + + ansible-runner: + artifact-dir: /tmp/test1 + rotate-artifacts-count: 10 + timeout: 300 + + app: run + + collection-doc-cache-path: /tmp/cache.db + + color: + enable: False + osc4: False + + documentation: + plugin: + name: shell + type: become + + editor: + command: vim_from_setting + console: False + + exec: + shell: False + command: /bin/foo + + execution-environment: + container-engine: podman + enabled: False + environment-variables: + pass: + - ONE + - TWO + - THREE + set: + KEY1: VALUE1 + KEY2: VALUE2 + KEY3: VALUE3 + image: test_image:latest + pull-policy: never + volume-mounts: + - src: "/test1" + dest: "/test1" + label: "Z" + container-options: + - "--net=host" + + help-config: True + + help-doc: True + + help-inventory: True + + help-playbook: False + + inventory-columns: + - ansible_network_os + - ansible_network_cli_ssh_type + - ansible_connection + + logging: + level: critical + append: False + file: /tmp/log.txt + + mode: stdout + + playbook-artifact: + enable: True + replay: /tmp/test_artifact.json + save-as: /tmp/test_artifact.json diff --git a/f/ansible-navigator.json b/f/ansible-navigator.json new file mode 100644 index 00000000..628c86b5 --- /dev/null +++ b/f/ansible-navigator.json @@ -0,0 +1,433 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "additionalProperties": false, + "definitions": { + "AnsibleModel": { + "additionalProperties": false, + "properties": { + "cmdline": { + "description": "Extra parameters passed to the corresponding command", + "title": "Cmdline", + "type": "string" + }, + "config": { + "description": "Specify the path to the ansible configuration file", + "title": "Config", + "type": "string" + }, + "inventories": { + "description": "Specify an inventory file path or host list", + "items": { + "type": "string" + }, + "title": "Inventories", + "type": "array" + }, + "playbook": { + "description": "Specify the playbook name", + "title": "Playbook", + "type": "string" + } + }, + "title": "AnsibleModel", + "type": "object" + }, + "AnsibleNavigatorModel": { + "additionalProperties": false, + "properties": { + "ansible": { + "$ref": "#/definitions/AnsibleModel" + }, + "ansible-runner": { + "$ref": "#/definitions/AnsibleRunnerModel" + }, + "app": { + "default": "welcome", + "description": "Subcommands", + "enum": [ + "collections", + "config", + "doc", + "exec", + "images", + "inventory", + "replay", + "run", + "welcome" + ], + "title": "App", + "type": "string" + }, + "collection-doc-cache-path": { + "default": "$HOME/.cache/ansible-navigator/collection_doc_cache.db", + "description": "The path to collection doc cache", + "title": "Collection-Doc-Cache-Path", + "type": "string" + }, + "color": { + "$ref": "#/definitions/ColorModel" + }, + "documentation": { + "$ref": "#/definitions/DocumentationModel" + }, + "editor": { + "$ref": "#/definitions/EditorModel" + }, + "exec": { + "$ref": "#/definitions/ExecModel" + }, + "execution-environment": { + "$ref": "#/definitions/ExecutionEnvironmentModel" + }, + "help-config": { + "default": false, + "description": "Help options for ansible-config command in stdout mode", + "title": "Help-Config", + "type": "boolean" + }, + "help-doc": { + "default": false, + "description": "Help options for ansible-doc command in stdout mode", + "title": "Help-Doc", + "type": "boolean" + }, + "help-inventory": { + "default": false, + "description": "Help options for ansible-inventory command in stdout mode", + "title": "Help-Inventory", + "type": "boolean" + }, + "help-playbook": { + "default": false, + "description": "Help options for ansible-playbook command in stdout mode", + "title": "Help-Playbook", + "type": "boolean" + }, + "inventory-columns": { + "description": "Specify a host attribute to show in the inventory view", + "items": { + "type": "string" + }, + "title": "Inventory-Columns", + "type": "array" + }, + "logging": { + "$ref": "#/definitions/LoggingModel" + }, + "mode": { + "default": "interactive", + "description": "Specify the user-interface mode", + "enum": [ + "stdout", + "interactive" + ], + "title": "Mode", + "type": "string" + }, + "playbook-artifact": { + "$ref": "#/definitions/PlaybookArtifactModel" + } + }, + "title": "AnsibleNavigatorModel", + "type": "object" + }, + "AnsibleRunnerModel": { + "additionalProperties": false, + "properties": { + "artifact-dir": { + "description": "The directory path to store artifacts generated by ansible-runner", + "title": "Artifact-Dir", + "type": "string" + }, + "rotate-artifacts-count": { + "description": "Keep ansible-runner artifact directories, for last n runs, if set to 0 artifact directories won't be deleted", + "title": "Rotate-Artifacts-Count", + "type": "integer" + }, + "timeout": { + "description": "The timeout value after which ansible-runner willforce stop the execution", + "title": "Timeout", + "type": "integer" + } + }, + "title": "AnsibleRunnerModel", + "type": "object" + }, + "ColorModel": { + "additionalProperties": false, + "properties": { + "enable": { + "default": false, + "description": "Enable the use of color in the display", + "title": "Enable", + "type": "boolean" + }, + "osc4": { + "default": true, + "description": "Enable or disable terminal color changing support with OSC 4", + "title": "Osc4", + "type": "boolean" + } + }, + "title": "ColorModel", + "type": "object" + }, + "DocumentationModel": { + "additionalProperties": false, + "properties": { + "plugin": { + "$ref": "#/definitions/PluginModel" + } + }, + "title": "DocumentationModel", + "type": "object" + }, + "EditorModel": { + "additionalProperties": false, + "properties": { + "command": { + "default": "vi +{line_number} {filename}", + "description": "Specify the editor command", + "title": "Command", + "type": "string" + }, + "console": { + "default": true, + "description": "Specify if the editor is console based", + "title": "Console", + "type": "boolean" + } + }, + "title": "EditorModel", + "type": "object" + }, + "EnvironmentVariablesModel": { + "additionalProperties": false, + "properties": { + "pass": { + "description": "Specify an exiting environment variable to be passed through to and set within the execution environment", + "items": { + "type": "string" + }, + "title": "Pass", + "type": "array" + }, + "set": { + "additionalProperties": { + "type": "string" + }, + "description": "Specify an environment variable and a value to be set within the execution environment", + "title": "Set", + "type": "object" + } + }, + "title": "EnvironmentVariablesModel", + "type": "object" + }, + "ExecModel": { + "additionalProperties": false, + "properties": { + "command": { + "default": "/bin/bash", + "description": "Specify the command to run within the execution environment", + "title": "Command", + "type": "string" + }, + "shell": { + "default": true, + "description": "Specify the exec command should be run in a shell", + "title": "Shell", + "type": "boolean" + } + }, + "title": "ExecModel", + "type": "object" + }, + "ExecutionEnvironmentModel": { + "additionalProperties": false, + "properties": { + "container-engine": { + "default": "auto", + "description": "Specify the container engine (auto=podman then docker)", + "enum": [ + "auto", + "podman", + "docker" + ], + "title": "Container-Engine", + "type": "string" + }, + "container-options": { + "description": "Extra parameters passed to the container engine command", + "items": { + "type": "string" + }, + "title": "Container-Options", + "type": "array" + }, + "enabled": { + "default": true, + "description": "Enable or disable the use of an execution environment", + "title": "Enabled", + "type": "boolean" + }, + "environment-variables": { + "$ref": "#/definitions/EnvironmentVariablesModel" + }, + "image": { + "default": "quay.io/ansible/creator-ee:v0.2.0", + "description": "Specify the name of the execution environment image", + "title": "Image", + "type": "string" + }, + "pull-policy": { + "default": "tag", + "description": "Specify the image pull policy.\nalways: Always pull the image\nmissing: Pull if not locally available\nnever: Never pull the image\ntag: if the image tag is 'latest', always pull the image, otherwise pull if not locally available", + "enum": [ + "always", + "missing", + "never", + "tag" + ], + "title": "Pull-Policy", + "type": "string" + }, + "volume-mounts": { + "description": "Specify volume to be bind mounted within an execution environment", + "items": { + "$ref": "#/definitions/VolumeMountsModel" + }, + "title": "Volume-Mounts", + "type": "array" + } + }, + "title": "ExecutionEnvironmentModel", + "type": "object" + }, + "LoggingModel": { + "additionalProperties": false, + "properties": { + "append": { + "default": true, + "description": "Specify if log messages should be appended to an existing log file, otherwise a new log file will be created per session", + "title": "Append", + "type": "boolean" + }, + "file": { + "default": "$PWD/ansible-navigator.", + "description": "Specify the full path for the ansible-navigator log file", + "title": "File", + "type": "string" + }, + "level": { + "default": "warning", + "description": "Specify the ansible-navigator log level", + "enum": [ + "debug", + "info", + "warning", + "error", + "critical" + ], + "title": "Level", + "type": "string" + } + }, + "title": "LoggingModel", + "type": "object" + }, + "PlaybookArtifactModel": { + "additionalProperties": false, + "properties": { + "enable": { + "default": true, + "description": "Enable or disable the creation of artifacts for completed playbooks.\nNote: not compatible with 'mode: stdout' when playbooks require user input", + "title": "Enable", + "type": "boolean" + }, + "replay": { + "description": "Specify the path for the playbook artifact to replay", + "title": "Replay", + "type": "string" + }, + "save-as": { + "default": "{playbook_dir}/{playbook_name}-artifact-{ts_utc}.json", + "description": "Specify the name for artifacts created from completed playbooks", + "title": "Save-As", + "type": "string" + } + }, + "title": "PlaybookArtifactModel", + "type": "object" + }, + "PluginModel": { + "additionalProperties": false, + "properties": { + "name": { + "description": "Specify the plugin name", + "title": "Name", + "type": "string" + }, + "type": { + "default": "module", + "description": "Specify the plugin type", + "enum": [ + "become", + "cache", + "callback", + "cliconf", + "connection", + "httpapi", + "inventory", + "lookup", + "module", + "netconf", + "shell", + "strategy", + "vars" + ], + "title": "Type", + "type": "string" + } + }, + "title": "PluginModel", + "type": "object" + }, + "VolumeMountsModel": { + "additionalProperties": false, + "properties": { + "dest": { + "title": "Dest", + "type": "string" + }, + "label": { + "title": "Label", + "type": "string" + }, + "src": { + "title": "Src", + "type": "string" + } + }, + "required": [ + "src", + "dest" + ], + "title": "VolumeMountsModel", + "type": "object" + } + }, + "examples": [ + "ansible-navigator.yml" + ], + "properties": { + "ansible-navigator": { + "$ref": "#/definitions/AnsibleNavigatorModel" + } + }, + "required": [ + "ansible-navigator" + ], + "title": "Ansible-Navigator Configuration Schema", + "type": "object" +} diff --git a/src/ansibleschemas/__main__.py b/src/ansibleschemas/__main__.py index 2420dc06..a34ddfb9 100644 --- a/src/ansibleschemas/__main__.py +++ b/src/ansibleschemas/__main__.py @@ -12,6 +12,7 @@ from ansibleschemas.galaxy import GalaxyFileModel from ansibleschemas.meta import MetaModel from ansibleschemas.molecule import MoleculeModel +from ansibleschemas.navigator import NavigatorModel from ansibleschemas.playbook import PlaybookFileModel from ansibleschemas.requirements import RequirementsFileModel from ansibleschemas.tasks import TasksListModel @@ -169,6 +170,7 @@ def main() -> None: "galaxy": GalaxyFileModel, "meta": MetaModel, "molecule": MoleculeModel, + "navigator": NavigatorModel, "playbook": PlaybookFileModel, "requirements": RequirementsFileModel, "tasks": TasksListModel, @@ -180,6 +182,7 @@ def main() -> None: "galaxy": "ansible-galaxy", "meta": "ansible-meta", "molecule": "molecule", + "navigator": "ansible-navigator", "playbook": "ansible-playbook", "requirements": "ansible-requirements", "tasks": "ansible-tasks", diff --git a/src/ansibleschemas/navigator.py b/src/ansibleschemas/navigator.py new file mode 100644 index 00000000..ba8cb455 --- /dev/null +++ b/src/ansibleschemas/navigator.py @@ -0,0 +1,280 @@ +# Used to generate JSON Validation Schema for ansible-navigator config files +# https://ansible-navigator.readthedocs.io/en/latest/settings/#the-ansible-navigator-settings-file +import sys +from typing import List, Mapping, Optional + +from pydantic import BaseModel as NavigatorBaseModel +from pydantic import Extra +from pydantic.fields import Field + +from . import consts + +if sys.version_info >= (3, 8): + from typing import Literal # pylint: disable=no-name-in-module +else: + from typing_extensions import Literal + + +class BaseModel(NavigatorBaseModel): + class Config: + @classmethod + def alias_generator(cls, string: str) -> str: + """rewrites all field names as ansible navigator uses dashes""" + return string.replace("_", "-") + + +class AnsibleModel(BaseModel): + config: Optional[str] = Field( + description="Specify the path to the ansible configuration file" + ) + cmdline: Optional[str] = Field( + description="Extra parameters passed to the corresponding command" + ) + inventories: Optional[List[str]] = Field( + description="Specify an inventory file path or host list" + ) + playbook: Optional[str] = Field(description="Specify the playbook name") + + class Config: + extra = Extra.forbid + + +class AnsibleRunnerModel(BaseModel): + artifact_dir: Optional[str] = Field( + description="The directory path to store artifacts generated by ansible-runner" + ) + rotate_artifacts_count: Optional[int] = Field( + description="Keep ansible-runner artifact directories, for last n runs, if set to 0 artifact directories won't be deleted" + ) + timeout: Optional[int] = Field( + description="The timeout value after which ansible-runner willforce stop the execution" + ) + + class Config: + extra = Extra.forbid + + +class ColorModel(BaseModel): + enable: Optional[bool] = Field( + default=False, description="Enable the use of color in the display" + ) + osc4: Optional[bool] = Field( + default=True, + description="Enable or disable terminal color changing support with OSC 4", + ) + + class Config: + extra = Extra.forbid + + +class DocumentationModel(BaseModel): + class PluginModel(BaseModel): + name: Optional[str] = Field(description="Specify the plugin name") + type: Optional[ + Literal[ + "become", + "cache", + "callback", + "cliconf", + "connection", + "httpapi", + "inventory", + "lookup", + "module", + "netconf", + "shell", + "strategy", + "vars", + ] + ] = Field( + default="module", + description="Specify the plugin type", + ) + + class Config: + extra = Extra.forbid + + plugin: Optional[PluginModel] + + class Config: + extra = Extra.forbid + + +class EditorModel(BaseModel): + command: Optional[str] = Field( + default="vi +{line_number} {filename}", + description="Specify the editor command", + ) + console: Optional[bool] = Field( + default=True, description="Specify if the editor is console based" + ) + + class Config: + extra = Extra.forbid + + +class ExecModel(BaseModel): + shell: Optional[bool] = Field( + default=True, description="Specify the exec command should be run in a shell" + ) + command: Optional[str] = Field( + default="/bin/bash", + description="Specify the command to run within the execution environment", + ) + + class Config: + extra = Extra.forbid + + +class ExecutionEnvironmentModel(BaseModel): + class EnvironmentVariablesModel(BaseModel): + pass_: Optional[List[str]] = Field( + alias="pass", + description="Specify an exiting environment variable to be passed through to and set within the execution environment", + ) + set: Optional[Mapping[str, str]] = Field( + description="Specify an environment variable and a value to be set within the execution environment" + ) + + class Config: + extra = Extra.forbid + + class VolumeMountsModel(BaseModel): + src: str + dest: str + label: Optional[str] + + class Config: + extra = Extra.forbid + + container_engine: Optional[Literal["auto", "podman", "docker"]] = Field( + default="auto", + description="Specify the container engine (auto=podman then docker)", + ) + enabled: Optional[bool] = Field( + default=True, + description="Enable or disable the use of an execution environment", + ) + environment_variables: Optional[EnvironmentVariablesModel] + image: Optional[str] = Field( + default="quay.io/ansible/creator-ee:v0.2.0", + description="Specify the name of the execution environment image", + ) + pull_policy: Optional[Literal["always", "missing", "never", "tag"]] = Field( + default="tag", + description="""Specify the image pull policy. +always: Always pull the image +missing: Pull if not locally available +never: Never pull the image +tag: if the image tag is 'latest', always pull the image, otherwise pull if not locally available""", + ) + volume_mounts: Optional[List[VolumeMountsModel]] = Field( + description="Specify volume to be bind mounted within an execution environment" + ) + container_options: Optional[List[str]] = Field( + description="Extra parameters passed to the container engine command" + ) + + class Config: + extra = Extra.forbid + + +class LoggingModel(BaseModel): + level: Optional[Literal["debug", "info", "warning", "error", "critical"]] = Field( + default="warning", description="Specify the ansible-navigator log level" + ) + append: Optional[bool] = Field( + default=True, + description="Specify if log messages should be appended to an existing log file, otherwise a new log file will be created per session", + ) + file: Optional[str] = Field( + default="$PWD/ansible-navigator.", + description="Specify the full path for the ansible-navigator log file", + ) + + class Config: + extra = Extra.forbid + + +class PlaybookArtifactModel(BaseModel): + enable: Optional[bool] = Field( + default=True, + description="""Enable or disable the creation of artifacts for completed playbooks. +Note: not compatible with 'mode: stdout' when playbooks require user input""", + ) + replay: Optional[str] = Field( + description="Specify the path for the playbook artifact to replay" + ) + save_as: Optional[str] = Field( + default="{playbook_dir}/{playbook_name}-artifact-{ts_utc}.json", + description="Specify the name for artifacts created from completed playbooks", + ) + + class Config: + extra = Extra.forbid + + +class AnsibleNavigatorModel(BaseModel): + ansible: Optional[AnsibleModel] + ansible_runner: Optional[AnsibleRunnerModel] + app: Optional[ + Literal[ + "collections", + "config", + "doc", + "exec", + "images", + "inventory", + "replay", + "run", + "welcome", + ] + ] = Field(default="welcome", description="Subcommands") + collection_doc_cache_path: Optional[str] = Field( + default="$HOME/.cache/ansible-navigator/collection_doc_cache.db", + description="The path to collection doc cache", + ) + color: Optional[ColorModel] + documentation: Optional[DocumentationModel] + editor: Optional[EditorModel] + exec: Optional[ExecModel] + execution_environment: Optional[ExecutionEnvironmentModel] + help_config: Optional[bool] = Field( + default=False, + description="Help options for ansible-config command in stdout mode", + ) + help_doc: Optional[bool] = Field( + default=False, + description="Help options for ansible-doc command in stdout mode", + ) + help_inventory: Optional[bool] = Field( + default=False, + description="Help options for ansible-inventory command in stdout mode", + ) + help_playbook: Optional[bool] = Field( + default=False, + description="Help options for ansible-playbook command in stdout mode", + ) + inventory_columns: Optional[List[str]] = Field( + description="Specify a host attribute to show in the inventory view" + ) + logging: Optional[LoggingModel] + mode: Optional[Literal["stdout", "interactive"]] = Field( + default="interactive", description="Specify the user-interface mode" + ) + playbook_artifact: Optional[PlaybookArtifactModel] + + class Config: + extra = Extra.forbid + + +class NavigatorModel(BaseModel): + ansible_navigator: AnsibleNavigatorModel + + class Config: + extra = Extra.forbid + title = "Ansible-Navigator Configuration Schema" + schema_extra = { + "$schema": consts.META_SCHEMA_URI, + "examples": ["ansible-navigator.yml"], + }