blob: 2615346a72dc9969272e09800a7ea9d317e06f3d [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2017-present Open Networking Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
import json
import os
import uuid
from ansible import constants
from ansible.executor import playbook_executor
from ansible.inventory.manager import InventoryManager
from ansible.parsing.dataloader import DataLoader
from ansible.plugins.callback import CallbackBase
from ansible.utils.display import Display
from ansible.vars.manager import VariableManager
from multistructlog import create_logger
from xosconfig import Config
try:
# Python 2: "reload" is built-in
# pylint: disable=W1626
reload
except NameError:
# Python 3: "reload" is part of importlib
from importlib import reload
constants = reload(constants)
log = create_logger(Config().get("logging"))
class ResultCallback(CallbackBase):
CALLBACK_VERSION = 2.0
CALLBACK_NAME = "resultcallback"
CALLBACK_TYPE = "programmatic"
def __init__(self):
super(ResultCallback, self).__init__()
self.results = []
self.uuid = str(uuid.uuid1())
self.playbook_status = "OK"
def v2_playbook_on_start(self, playbook):
self.playbook = playbook._file_name
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "playbook start",
"ansible_status": "OK",
"ansible_playbook": self.playbook,
}
log.info("PLAYBOOK START", playbook=self.playbook, **log_extra)
def v2_playbook_on_stats(self, stats):
host_stats = {}
for host in stats.processed.keys():
host_stats[host] = stats.summarize(host)
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "playbook stats",
"ansible_status": self.playbook_status,
"ansible_playbook": self.playbook,
"ansible_result": json.dumps(host_stats),
}
if self.playbook_status == "OK":
log.info("PLAYBOOK END", playbook=self.playbook, **log_extra)
else:
log.error("PLAYBOOK END", playbook=self.playbook, **log_extra)
def v2_playbook_on_play_start(self, play):
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "play start",
"ansible_status": self.playbook_status,
"ansible_playbook": self.playbook,
}
log.debug("PLAY START", play_name=play.name, **log_extra)
def v2_runner_on_ok(self, result, **kwargs):
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "task",
"ansible_status": "OK",
"ansible_result": json.dumps(result._result),
"ansible_task": result._task,
"ansible_playbook": self.playbook,
"ansible_host": result._host.get_name(),
}
log.debug("OK", task=str(result._task), **log_extra)
self.results.append(result)
def v2_runner_on_failed(self, result, **kwargs):
self.playbook_status = "FAILED"
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "task",
"ansible_status": "FAILED",
"ansible_result": json.dumps(result._result),
"ansible_task": result._task,
"ansible_playbook": self.playbook,
"ansible_host": result._host.get_name(),
}
log.error("FAILED", task=str(result._task), **log_extra)
self.results.append(result)
def v2_runner_on_async_failed(self, result, **kwargs):
self.playbook_status = "FAILED"
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "task",
"ansible_status": "ASYNC FAILED",
"ansible_result": json.dumps(result._result),
"ansible_task": result._task,
"ansible_playbook": self.playbook,
"ansible_host": result._host.get_name(),
}
log.error("ASYNC FAILED", task=str(result._task), **log_extra)
def v2_runner_on_skipped(self, result, **kwargs):
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "task",
"ansible_status": "SKIPPED",
"ansible_result": json.dumps(result._result),
"ansible_task": result._task,
"ansible_playbook": self.playbook,
"ansible_host": result._host.get_name(),
}
log.debug("SKIPPED", task=str(result._task), **log_extra)
self.results.append(result)
def v2_runner_on_unreachable(self, result, **kwargs):
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "task",
"ansible_status": "UNREACHABLE",
"ansible_result": json.dumps(result._result),
"ansible_task": result._task,
"ansible_playbook": self.playbook,
"ansible_host": result._host.get_name(),
}
log.error("UNREACHABLE", task=str(result._task), **log_extra)
self.results.append(result)
def v2_runner_retry(self, result, **kwargs):
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "task",
"ansible_status": "RETRY",
"ansible_result": json.dumps(result._result),
"ansible_task": result._task,
"ansible_playbook": self.playbook,
"ansible_host": result._host.get_name(),
}
log.warning(
"RETRYING - attempt",
task=str(result._task),
attempt=result._result["attempts"],
**log_extra
)
self.results.append(result)
def v2_playbook_on_handler_task_start(self, task, **kwargs):
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "task",
"ansible_status": "HANDLER",
"ansible_task": task.get_name().strip(),
"ansible_playbook": self.playbook,
# 'ansible_host': result._host.get_name()
}
log.debug("HANDLER", task=task.get_name().strip(), **log_extra)
def v2_playbook_on_import_for_host(self, result, imported_file):
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "import",
"ansible_status": "IMPORT",
"ansible_result": json.dumps(result._result),
"ansible_playbook": self.playbook,
"ansible_host": result._host.get_name(),
}
log.debug("IMPORT", imported_file=imported_file, **log_extra)
self.results.append(result)
def v2_playbook_on_not_import_for_host(self, result, missing_file):
log_extra = {
"xos_type": "ansible",
"ansible_uuid": self.uuid,
"ansible_type": "import",
"ansible_status": "MISSING IMPORT",
"ansible_result": json.dumps(result._result),
"ansible_playbook": self.playbook,
"ansible_host": result._host.get_name(),
}
log.debug("MISSING IMPORT", missing=missing_file, **log_extra)
self.results.append(result)
class Options(object):
"""
Options class to replace Ansible OptParser
"""
def __init__(
self,
ask_pass=None,
ask_su_pass=None,
ask_sudo_pass=None,
become=None,
become_ask_pass=None,
become_method=None,
become_user=None,
check=None,
connection=None,
diff=None,
flush_cache=None,
force_handlers=None,
forks=1,
listtags=None,
listtasks=None,
module_path=None,
new_vault_password_file=None,
one_line=None,
output_file=None,
poll_interval=None,
private_key_file=None,
remote_user=None,
scp_extra_args=None,
seconds=None,
sftp_extra_args=None,
skip_tags=None,
ssh_common_args=None,
ssh_extra_args=None,
sudo=None,
sudo_user=None,
syntax=None,
tags=None,
timeout=None,
tree=None,
vault_password_files=None,
ask_vault_pass=None,
extra_vars=None,
inventory=None,
listhosts=None,
module_paths=None,
subset=None,
verbosity=None,
):
if tags:
self.tags = tags
if skip_tags:
self.skip_tags = skip_tags
self.ask_pass = ask_pass
self.ask_su_pass = ask_su_pass
self.ask_sudo_pass = ask_sudo_pass
self.ask_vault_pass = ask_vault_pass
self.become = become
self.become_ask_pass = become_ask_pass
self.become_method = become_method
self.become_user = become_user
self.check = check
self.connection = connection
self.diff = diff
self.extra_vars = extra_vars
self.flush_cache = flush_cache
self.force_handlers = force_handlers
self.forks = forks
self.inventory = inventory
self.listhosts = listhosts
self.listtags = listtags
self.listtasks = listtasks
self.module_path = module_path
self.module_paths = module_paths
self.new_vault_password_file = new_vault_password_file
self.one_line = one_line
self.output_file = output_file
self.poll_interval = poll_interval
self.private_key_file = private_key_file
self.remote_user = remote_user
self.scp_extra_args = scp_extra_args
self.seconds = seconds
self.sftp_extra_args = sftp_extra_args
self.ssh_common_args = ssh_common_args
self.ssh_extra_args = ssh_extra_args
self.subset = subset
self.sudo = sudo
self.sudo_user = sudo_user
self.syntax = syntax
self.timeout = timeout
self.tree = tree
self.vault_password_files = vault_password_files
self.verbosity = verbosity
class Runner(object):
def __init__(
self, playbook, run_data, private_key_file=None, verbosity=0, host_file=None
):
self.playbook = playbook
self.run_data = run_data
self.options = Options()
self.options.output_file = playbook + ".result"
self.options.private_key_file = private_key_file
self.options.verbosity = verbosity
self.options.connection = "ssh" # Need a connection type "smart" or "ssh"
# self.options.become = True
self.options.become_method = "sudo"
self.options.become_user = "root"
# Set global verbosity
self.display = Display()
self.display.verbosity = self.options.verbosity
# Executor appears to have it's own
# verbosity object/setting as well
playbook_executor.verbosity = self.options.verbosity
# Become Pass Needed if not logging in as user root
# passwords = {'become_pass': become_pass}
# Gets data from YAML/JSON files
self.loader = DataLoader()
try:
self.loader.set_vault_password(os.environ["VAULT_PASS"])
except AttributeError:
pass
# Set inventory, using most of above objects
if host_file:
self.inventory = InventoryManager(loader=self.loader, sources=host_file)
else:
self.inventory = InventoryManager(loader=self.loader)
# All the variables from all the various places
self.variable_manager = VariableManager(
loader=self.loader, inventory=self.inventory
)
self.variable_manager.extra_vars = {} # self.run_data
# Setup playbook executor, but don't run until run() called
self.pbex = playbook_executor.PlaybookExecutor(
playbooks=[playbook],
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
options=self.options,
passwords={},
)
def run(self):
os.environ[
"REQUESTS_CA_BUNDLE"
] = "/usr/local/share/ca-certificates/local_certs.crt"
callback = ResultCallback()
self.pbex._tqm._stdout_callback = callback
self.pbex.run()
stats = self.pbex._tqm._stats
# os.remove(self.hosts.name)
return stats, callback.results