1 ##############################################################################
2 # Copyright (c) 2016 Tim Rozet (trozet@redhat.com) and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
25 if isinstance(var, bool):
28 return var.lower() in ("true", "yes")
31 def parse_yaml(yaml_file):
32 with open(yaml_file) as f:
33 parsed_dict = yaml.safe_load(f)
37 def dump_yaml(data, file):
39 Dumps data to a file as yaml
40 :param data: yaml to be written to file
41 :param file: filename to write to
44 logging.debug("Writing file {} with "
45 "yaml data:\n{}".format(file, yaml.safe_dump(data)))
46 with open(file, "w") as fh:
47 yaml.safe_dump(data, fh, default_flow_style=False)
50 def dict_objects_to_str(dictionary):
51 if isinstance(dictionary, list):
53 for element in dictionary:
54 if isinstance(element, dict):
55 tmp_list.append(dict_objects_to_str(element))
57 tmp_list.append(str(element))
59 elif not isinstance(dictionary, dict):
60 if not isinstance(dictionary, bool):
61 return str(dictionary)
64 return dict((k, dict_objects_to_str(v)) for
65 k, v in dictionary.items())
68 def run_ansible(ansible_vars, playbook, host='localhost', user='root',
69 tmp_dir=None, dry_run=False):
71 Executes ansible playbook and checks for errors
72 :param ansible_vars: dictionary of variables to inject into ansible run
73 :param playbook: playbook to execute
74 :param tmp_dir: temp directory to store ansible command
75 :param dry_run: Do not actually apply changes
78 logging.info("Executing ansible playbook: {}".format(playbook))
79 inv_host = "{},".format(host)
80 if host == 'localhost':
84 ansible_command = ['ansible-playbook', '--become', '-i', inv_host,
85 '-u', user, '-c', conn_type, '-T', '30',
88 ansible_command.append('--check')
90 if isinstance(ansible_vars, dict) and ansible_vars:
91 logging.debug("Ansible variables to be set:\n{}".format(
92 pprint.pformat(ansible_vars)))
93 ansible_command.append('--extra-vars')
94 ansible_command.append(json.dumps(ansible_vars))
96 ansible_tmp = os.path.join(tmp_dir,
97 os.path.basename(playbook) + '.rerun')
98 # FIXME(trozet): extra vars are printed without single quotes
99 # so a dev has to add them manually to the command to rerun
100 # the playbook. Need to test if we can just add the single quotes
101 # to the json dumps to the ansible command and see if that works
102 with open(ansible_tmp, 'w') as fh:
103 fh.write("ANSIBLE_HOST_KEY_CHECKING=FALSE {}".format(
104 ' '.join(ansible_command)))
106 my_env = os.environ.copy()
107 my_env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
108 logging.info("Executing playbook...this may take some time")
109 p = subprocess.Popen(ansible_command,
110 stdin=subprocess.PIPE,
111 stdout=subprocess.PIPE,
114 universal_newlines=True)
116 x = p.stdout.readline()
120 # append lines to task
122 # log the line and read another
123 x = p.stdout.readline()
124 # deliver the task to info when we get a blank line
127 logging.info(task.replace('\\n', '\n'))
129 x = p.stdout.readline()
130 # clean up and get return code
135 e = "Ansible playbook failed. See Ansible logs for details."
140 def fetch_upstream_and_unpack(dest, url, targets):
142 Fetches targets from a url destination and downloads them if they are
143 newer. Also unpacks tar files in dest dir.
144 :param dest: Directory to download and unpack files to
145 :param url: URL where target files are located
146 :param targets: List of target files to download
149 os.makedirs(dest, exist_ok=True)
150 assert isinstance(targets, list)
151 for target in targets:
152 download_target = True
153 target_url = urllib.parse.urljoin(url, target)
154 target_dest = os.path.join(dest, target)
155 logging.debug("Fetching and comparing upstream target: \n{}".format(
158 u = urllib.request.urlopen(target_url)
159 except urllib.error.URLError as e:
160 logging.error("Failed to fetch target url. Error: {}".format(
163 if os.path.isfile(target_dest):
164 logging.debug("Previous file found: {}".format(target_dest))
166 headers = metadata.items()
167 target_url_date = None
168 for header in headers:
169 if isinstance(header, tuple) and len(header) == 2:
170 if header[0] == 'Last-Modified':
171 target_url_date = header[1]
173 if target_url_date is not None:
174 target_dest_mtime = os.path.getmtime(target_dest)
175 target_url_mtime = time.mktime(
176 datetime.datetime.strptime(target_url_date,
179 if target_url_mtime > target_dest_mtime:
180 logging.debug('URL target is newer than disk...will '
183 logging.info('URL target does not need to be downloaded')
184 download_target = False
186 logging.debug('Unable to find last modified url date')
188 urllib.request.urlretrieve(target_url, filename=target_dest)
189 logging.info("Target downloaded: {}".format(target))
190 if target.endswith('.tar'):
191 logging.info('Unpacking tar file')
192 tar = tarfile.open(target_dest)
193 tar.extractall(path=dest)