1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
##############################################################################
# Copyright (c) 2016 Tim Rozet (trozet@redhat.com) and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
import json
import logging
import os
import pprint
import subprocess
import yaml
def str2bool(var):
if isinstance(var, bool):
return var
else:
return var.lower() in ("true", "yes")
def parse_yaml(yaml_file):
with open(yaml_file) as f:
parsed_dict = yaml.safe_load(f)
return parsed_dict
def dump_yaml(data, file):
"""
Dumps data to a file as yaml
:param data: yaml to be written to file
:param file: filename to write to
:return:
"""
logging.debug("Writing file {} with "
"yaml data:\n{}".format(file, yaml.safe_dump(data)))
with open(file, "w") as fh:
yaml.safe_dump(data, fh, default_flow_style=False)
def dict_objects_to_str(dictionary):
if isinstance(dictionary, list):
tmp_list = []
for element in dictionary:
if isinstance(element, dict):
tmp_list.append(dict_objects_to_str(element))
else:
tmp_list.append(str(element))
return tmp_list
elif not isinstance(dictionary, dict):
if not isinstance(dictionary, bool):
return str(dictionary)
else:
return dictionary
return dict((k, dict_objects_to_str(v)) for
k, v in dictionary.items())
def run_ansible(ansible_vars, playbook, host='localhost', user='root',
tmp_dir=None, dry_run=False):
"""
Executes ansible playbook and checks for errors
:param ansible_vars: dictionary of variables to inject into ansible run
:param playbook: playbook to execute
:param tmp_dir: temp directory to store ansible command
:param dry_run: Do not actually apply changes
:return: None
"""
logging.info("Executing ansible playbook: {}".format(playbook))
inv_host = "{},".format(host)
if host == 'localhost':
conn_type = 'local'
else:
conn_type = 'smart'
ansible_command = ['ansible-playbook', '--become', '-i', inv_host,
'-u', user, '-c', conn_type, playbook, '-vv']
if dry_run:
ansible_command.append('--check')
if isinstance(ansible_vars, dict) and ansible_vars:
logging.debug("Ansible variables to be set:\n{}".format(
pprint.pformat(ansible_vars)))
ansible_command.append('--extra-vars')
ansible_command.append(json.dumps(ansible_vars))
if tmp_dir:
ansible_tmp = os.path.join(tmp_dir,
os.path.basename(playbook) + '.rerun')
# FIXME(trozet): extra vars are printed without single quotes
# so a dev has to add them manually to the command to rerun
# the playbook. Need to test if we can just add the single quotes
# to the json dumps to the ansible command and see if that works
with open(ansible_tmp, 'w') as fh:
fh.write("ANSIBLE_HOST_KEY_CHECKING=FALSE {}".format(
' '.join(ansible_command)))
try:
my_env = os.environ.copy()
my_env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
logging.info("Executing playbook...this may take some time")
logging.info(subprocess.check_output(ansible_command, env=my_env,
stderr=subprocess.STDOUT).decode('utf-8'))
except subprocess.CalledProcessError as e:
logging.error("Error executing ansible: {}".format(
pprint.pformat(e.output.decode('utf-8'))))
raise
|