File: //lib/python3.9/site-packages/ansible_collections/ansible/netcommon/plugins/modules/grpc_config.py
# -*- coding: utf-8 -*-
# Copyright 2022 Red Hat
# GNU General Public License v3.0+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: grpc_config
version_added: "3.1.0"
author:
- "Gomathi Selvi S (@GomathiselviS)"
short_description: Fetch configuration/state data from gRPC enabled target hosts.
description:
- gRPC is a high performance, open-source universal RPC framework.
- This module allows the user to append configs to an existing configuration in a gRPC
enabled devices.
options:
config:
description:
- This option specifies the string which acts as a filter to restrict the portions of
the data to be retrieved from the target host device. If this option is not specified the entire
configuration or state data is returned in response provided it is supported by target host.
type: str
state:
description: action to be performed
type: str
backup:
description:
- This argument will cause the module to create a full backup of the current C(running-config)
from the remote device before any changes are made. If the C(backup_options)
value is not given, the backup file is written to the C(backup) folder in the
playbook root directory or role root directory, if playbook is part of an ansible
role. If the directory does not exist, it is created.
type: bool
default: no
backup_options:
description:
- This is a dict object containing configurable options related to backup file
path. The value of this option is read only when C(backup) is set to I(yes),
if C(backup) is set to I(no) this option will be silently ignored.
suboptions:
filename:
description:
- The filename to be used to store the backup configuration. If the filename
is not given it will be generated based on the hostname, current time and
date in format defined by <hostname>_config.<current-date>@<current-time>
type: str
dir_path:
description:
- This option provides the path ending with directory name in which the backup
configuration file will be stored. If the directory does not exist it will
be first created and the filename is either the value of C(filename) or
default filename as described in C(filename) options description. If the
path value is not given in that case a I(backup) directory will be created
in the current working directory and backup configuration will be copied
in C(filename) within I(backup) directory.
type: path
type: dict
requirements:
- grpcio
- protobuf
notes:
- This module requires the gRPC system service be enabled on
the target host being managed.
- This module supports the use of connection=connection=ansible.netcommon.grpc
- This module requires the value of 'ansible_network_os' or 'grpc_type' configuration option
(refer ansible.netcommon.grpc connection plugin documentation)
be defined as an inventory variable.
- Tested against iosxrv 9k version 6.1.2.
"""
EXAMPLES = """
- name: Merge static route config
ansible.netcommon.grpc_config:
config:
Cisco-IOS-XR-ip-static-cfg:router-static:
default-vrf:
address-family:
vrfipv4:
vrf-unicast:
vrf-prefixes:
vrf-prefix:
- prefix: "1.2.3.6"
prefix-length: 32
vrf-route:
vrf-next-hop-table:
vrf-next-hop-next-hop-address:
- next-hop-address: "10.0.2.2"
state: merged
- name: Merge bgp config
ansible.netcommon.grpc_config:
config: "{{ lookup('file', 'bgp.json') }}"
state: merged
- name: Find diff
diff: True
ansible.netcommon.grpc_config:
config: "{{ lookup('file', 'bgp_start.yml') }}"
state: merged
- name: Backup running config
ansible.netcommon.grpc_config:
backup: yes
"""
RETURN = """
stdout:
description: The raw string containing response object
received from the gRPC server.
returned: error mesage, when failure happens.
empty , when the operation is successful
type: str
sample: '...'
stdout_lines:
description: The value of stdout split into a list
returned: always apart from low-level errors (such as action plugin)
type: list
sample: ['...', '...']
backup_path:
description: The full path to the backup file
returned: when backup is yes
type: str
sample: /playbooks/ansible/backup/config.2022-07-16@22:28:34
diff:
description: If --diff option in enabled while running, the before and after configuration change are
returned as part of before and after key.
returned: when diff is enabled
type: dict
"""
import json
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import ConnectionError
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
dict_diff,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.grpc.grpc import (
delete_config,
merge_config,
replace_config,
run_cli,
sanitize_content,
validate_config,
)
try:
import yaml
HAS_YAML = True
except ImportError:
HAS_YAML = False
def main():
"""entry point for module execution"""
backup_spec = dict(filename=dict(), dir_path=dict(type="path"))
argument_spec = dict(
config=dict(type="str"),
state=dict(type="str"),
backup=dict(type="bool", default=False),
backup_options=dict(type="dict", options=backup_spec),
)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)
config = {}
if module.params["config"]:
config = json.dumps(yaml.safe_load(module.params["config"]))
config = json.loads(config)
state = module.params["state"]
result = {
"changed": False,
}
before = None
after = None
before_diff = None
after_diff = None
output = ""
try:
if module.params["backup"] or state in [
"merged",
"replaced",
"deleted",
]:
if not module.check_mode:
response, err = run_cli(module, "show running-config", "text")
before = to_text(
response, errors="surrogate_then_replace"
).strip()
if module._diff or module.check_mode:
before_diff = validate_config(module, config)
if module.check_mode:
diff = dict_diff(before_diff, config)
if diff:
result["changed"] = True
result["diff"] = diff
else:
if module.params["backup"]:
result["__backup__"] = before.strip()
if state == "merged":
output = merge_config(module, config)
elif state == "replaced":
output = replace_config(module, config)
elif state == "deleted":
output = delete_config(module, config)
if state:
response, err = run_cli(module, "show running-config", "text")
after = to_text(
response, errors="surrogate_then_replace"
).strip()
if before:
before = sanitize_content(before)
if after:
after = sanitize_content(after)
if before != after:
result["changed"] = True
if module._diff:
after_diff = validate_config(module, config)
result["diff"] = {
"before": before_diff,
"after": after_diff,
}
except ConnectionError as exc:
module.fail_json(
msg=to_text(exc, errors="surrogate_then_replace"), code=exc.code
)
result["stdout"] = output
module.exit_json(**result)
if __name__ == "__main__":
main()