diff -Nru ansible-1.6.1-quantal-unstable/ansible.egg-info/PKG-INFO ansible-1.6.2-quantal-unstable/ansible.egg-info/PKG-INFO --- ansible-1.6.1-quantal-unstable/ansible.egg-info/PKG-INFO 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/ansible.egg-info/PKG-INFO 2014-05-23 21:16:25.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: ansible -Version: 1.6.1 +Version: 1.6.2 Summary: Radically simple IT automation Home-page: http://ansible.com/ Author: Michael DeHaan diff -Nru ansible-1.6.1-quantal-unstable/debian/changelog ansible-1.6.2-quantal-unstable/debian/changelog --- ansible-1.6.1-quantal-unstable/debian/changelog 2014-05-23 13:07:51.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/debian/changelog 2014-05-28 07:22:21.000000000 +0000 @@ -1,8 +1,12 @@ -ansible (1.6.1-quantal-unstable-ppa1) quantal; urgency=low +ansible (1.6.2-quantal-unstable-ppa) quantal; urgency=low - * 1.6.1 release + * 1.6.2 release + + -- Michael DeHaan Fri, 23 May 2014 17:30:00 -0500 - -- Michael DeHaan Wed, 07 May 2014 13:30:00 -0500 +ansible (1.6.1) unstable; urgency=low + + * 1.6.1 release ansible (1.6) unstable; urgency=low diff -Nru ansible-1.6.1-quantal-unstable/lib/ansible/__init__.py ansible-1.6.2-quantal-unstable/lib/ansible/__init__.py --- ansible-1.6.1-quantal-unstable/lib/ansible/__init__.py 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/lib/ansible/__init__.py 2014-05-23 21:16:25.000000000 +0000 @@ -14,5 +14,5 @@ # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -__version__ = '1.6.1' +__version__ = '1.6.2' __author__ = 'Michael DeHaan' diff -Nru ansible-1.6.1-quantal-unstable/lib/ansible/inventory/dir.py ansible-1.6.2-quantal-unstable/lib/ansible/inventory/dir.py --- ansible-1.6.1-quantal-unstable/lib/ansible/inventory/dir.py 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/lib/ansible/inventory/dir.py 2014-05-23 21:16:25.000000000 +0000 @@ -36,17 +36,11 @@ self.parsers = [] self.hosts = {} self.groups = {} - + for i in self.names: - - if i.endswith("~") or i.endswith(".orig") or i.endswith(".bak"): - continue - if i.endswith(".ini"): - # configuration file for an inventory script - continue - if i.endswith(".retry"): - # this file is generated on a failed playbook and should only be - # used when run specifically + + # Skip files that end with certain extensions or characters + if any(i.endswith(ext) for ext in ("~", ".orig", ".bak", ".ini", ".retry", ".pyc", ".pyo")): continue # Skip hidden files if i.startswith('.') and not i.startswith('./'): diff -Nru ansible-1.6.1-quantal-unstable/lib/ansible/module_utils/basic.py ansible-1.6.2-quantal-unstable/lib/ansible/module_utils/basic.py --- ansible-1.6.1-quantal-unstable/lib/ansible/module_utils/basic.py 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/lib/ansible/module_utils/basic.py 2014-05-23 21:16:25.000000000 +0000 @@ -43,6 +43,7 @@ # of an ansible module. The source of this common code lives # in lib/ansible/module_common.py +import locale import os import re import pipes @@ -182,6 +183,7 @@ self.supports_check_mode = supports_check_mode self.check_mode = False self.no_log = no_log + self.cleanup_files = [] self.aliases = {} @@ -190,6 +192,10 @@ if k not in self.argument_spec: self.argument_spec[k] = v + # check the locale as set by the current environment, and + # reset to LANG=C if it's an invalid/unavailable locale + self._check_locale() + (self.params, self.args) = self._load_params() self._legal_inputs = ['CHECKMODE', 'NO_LOG'] @@ -560,6 +566,24 @@ kwargs['state'] = 'absent' return kwargs + def _check_locale(self): + ''' + Uses the locale module to test the currently set locale + (per the LANG and LC_CTYPE environment settings) + ''' + try: + # setting the locale to '' uses the default locale + # as it would be returned by locale.getdefaultlocale() + locale.setlocale(locale.LC_ALL, '') + except locale.Error, e: + # fallback to the 'C' locale, which may cause unicode + # issues but is preferable to simply failing because + # of an unknown locale + locale.setlocale(locale.LC_ALL, 'C') + os.environ['LANG'] = 'C' + os.environ['LC_CTYPE'] = 'C' + except Exception, e: + self.fail_json(msg="An unknown error was encountered while attempting to validate the locale: %s" % e) def _handle_aliases(self): aliases_results = {} #alias:canon @@ -898,11 +922,20 @@ def from_json(self, data): return json.loads(data) + def add_cleanup_file(self, path): + if path not in self.cleanup_files: + self.cleanup_files.append(path) + + def do_cleanup_files(self): + for path in self.cleanup_files: + self.cleanup(path) + def exit_json(self, **kwargs): ''' return from the module, without error ''' self.add_path_info(kwargs) if not 'changed' in kwargs: kwargs['changed'] = False + self.do_cleanup_files() print self.jsonify(kwargs) sys.exit(0) @@ -911,6 +944,7 @@ self.add_path_info(kwargs) assert 'msg' in kwargs, "implementation error -- msg to explain the error is required" kwargs['failed'] = True + self.do_cleanup_files() print self.jsonify(kwargs) sys.exit(1) @@ -958,7 +992,7 @@ self.fail_json(msg='Could not make backup of %s to %s: %s' % (fn, backupdest, e)) return backupdest - def cleanup(self,tmpfile): + def cleanup(self, tmpfile): if os.path.exists(tmpfile): try: os.unlink(tmpfile) @@ -1010,7 +1044,8 @@ if self.selinux_enabled(): self.set_context_if_different( tmp_dest.name, context, False) - if dest_stat: + tmp_stat = os.stat(tmp_dest.name) + if dest_stat and (tmp_stat.st_uid != dest_stat.st_uid or tmp_stat.st_gid != dest_stat.st_gid): os.chown(tmp_dest.name, dest_stat.st_uid, dest_stat.st_gid) os.rename(tmp_dest.name, dest) except (shutil.Error, OSError, IOError), e: diff -Nru ansible-1.6.1-quantal-unstable/lib/ansible/module_utils/urls.py ansible-1.6.2-quantal-unstable/lib/ansible/module_utils/urls.py --- ansible-1.6.1-quantal-unstable/lib/ansible/module_utils/urls.py 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/lib/ansible/module_utils/urls.py 2014-05-23 21:16:25.000000000 +0000 @@ -50,6 +50,8 @@ except: HAS_SSL=False +import os +import re import socket import tempfile @@ -77,6 +79,58 @@ -----END CERTIFICATE----- """ +def generic_urlparse(parts): + ''' + Returns a dictionary of url parts as parsed by urlparse, + but accounts for the fact that older versions of that + library do not support named attributes (ie. .netloc) + ''' + generic_parts = dict() + if hasattr(parts, 'netloc'): + # urlparse is newer, just read the fields straight + # from the parts object + generic_parts['scheme'] = parts.scheme + generic_parts['netloc'] = parts.netloc + generic_parts['path'] = parts.path + generic_parts['params'] = parts.params + generic_parts['query'] = parts.query + generic_parts['fragment'] = parts.fragment + generic_parts['username'] = parts.username + generic_parts['password'] = parts.password + generic_parts['hostname'] = parts.hostname + generic_parts['port'] = parts.port + else: + # we have to use indexes, and then parse out + # the other parts not supported by indexing + generic_parts['scheme'] = parts[0] + generic_parts['netloc'] = parts[1] + generic_parts['path'] = parts[2] + generic_parts['params'] = parts[3] + generic_parts['query'] = parts[4] + generic_parts['fragment'] = parts[5] + # get the username, password, etc. + try: + netloc_re = re.compile(r'^((?:\w)+(?::(?:\w)+)?@)?([A-Za-z0-9.-]+)(:\d+)?$') + (auth, hostname, port) = netloc_re.match(parts[1]) + if port: + # the capture group for the port will include the ':', + # so remove it and convert the port to an integer + port = int(port[1:]) + if auth: + # the capture group above inclues the @, so remove it + # and then split it up based on the first ':' found + auth = auth[:-1] + username, password = auth.split(':', 1) + generic_parts['username'] = username + generic_parts['password'] = password + generic_parts['hostname'] = hostnme + generic_parts['port'] = port + except: + generic_parts['username'] = None + generic_parts['password'] = None + generic_parts['hostname'] = None + generic_parts['port'] = None + return generic_parts class RequestWithMethod(urllib2.Request): ''' @@ -103,6 +157,7 @@ http://stackoverflow.com/questions/1087227/validate-ssl-certificates-with-python http://techknack.net/python-urllib2-handlers/ ''' + CONNECT_COMMAND = "CONNECT %s:%s HTTP/1.0\r\nConnection: close\r\n" def __init__(self, module, hostname, port): self.module = module @@ -161,13 +216,42 @@ return (tmp_path, paths_checked) + def validate_proxy_response(self, response, valid_codes=[200]): + ''' + make sure we get back a valid code from the proxy + ''' + try: + (http_version, resp_code, msg) = re.match(r'(HTTP/\d\.\d) (\d\d\d) (.*)', response).groups() + if int(resp_code) not in valid_codes: + raise Exception + except: + self.module.fail_json(msg='Connection to proxy failed') + def http_request(self, req): tmp_ca_cert_path, paths_checked = self.get_ca_certs() + https_proxy = os.environ.get('https_proxy') try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - ssl_s = ssl.wrap_socket(s, ca_certs=tmp_ca_cert_path, cert_reqs=ssl.CERT_REQUIRED) - ssl_s.connect((self.hostname, self.port)) - ssl_s.close() + if https_proxy: + proxy_parts = generic_urlparse(urlparse.urlparse(https_proxy)) + s.connect((proxy_parts.get('hostname'), proxy_parts.get('port'))) + if proxy_parts.get('scheme') == 'http': + s.sendall(self.CONNECT_COMMAND % (self.hostname, self.port)) + if proxy_parts.get('username'): + credentials = "%s:%s" % (proxy_parts.get('username',''), proxy_parts.get('password','')) + s.sendall('Proxy-Authorization: Basic %s\r\n' % credentials.encode('base64').strip()) + s.sendall('\r\n') + connect_result = s.recv(4096) + self.validate_proxy_response(connect_result) + ssl_s = ssl.wrap_socket(s, ca_certs=tmp_ca_cert_path, cert_reqs=ssl.CERT_REQUIRED) + else: + self.module.fail_json(msg='Unsupported proxy scheme: %s. Currently ansible only supports HTTP proxies.' % proxy_parts.get('scheme')) + else: + s.connect((self.hostname, self.port)) + ssl_s = ssl.wrap_socket(s, ca_certs=tmp_ca_cert_path, cert_reqs=ssl.CERT_REQUIRED) + # close the ssl connection + #ssl_s.unwrap() + s.close() except (ssl.SSLError, socket.error), e: # fail if we tried all of the certs but none worked if 'connection refused' in str(e).lower(): @@ -207,7 +291,7 @@ def fetch_url(module, url, data=None, headers=None, method=None, - use_proxy=False, force=False, last_mod_time=None, timeout=10): + use_proxy=True, force=False, last_mod_time=None, timeout=10): ''' Fetches a file from an HTTP/FTP server using urllib2 ''' @@ -227,6 +311,8 @@ # Get validate_certs from the module params validate_certs = module.params.get('validate_certs', True) + # FIXME: change the following to use the generic_urlparse function + # to remove the indexed references for 'parsed' parsed = urlparse.urlparse(url) if parsed[0] == 'https': if not HAS_SSL and validate_certs: diff -Nru ansible-1.6.1-quantal-unstable/lib/ansible/playbook/__init__.py ansible-1.6.2-quantal-unstable/lib/ansible/playbook/__init__.py --- ansible-1.6.1-quantal-unstable/lib/ansible/playbook/__init__.py 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/lib/ansible/playbook/__init__.py 2014-05-23 21:16:25.000000000 +0000 @@ -179,7 +179,46 @@ # ***************************************************** - def _load_playbook_from_file(self, path, vars={}): + def _get_playbook_vars(self, play_ds, existing_vars): + ''' + Gets the vars specified with the play and blends them + with any existing vars that have already been read in + ''' + new_vars = existing_vars.copy() + if 'vars' in play_ds: + if isinstance(play_ds['vars'], dict): + new_vars.update(play_ds['vars']) + elif isinstance(play_ds['vars'], list): + for v in play_ds['vars']: + new_vars.update(v) + return new_vars + + # ***************************************************** + + def _get_include_info(self, play_ds, basedir, existing_vars={}): + ''' + Gets any key=value pairs specified with the included file + name and returns the merged vars along with the path + ''' + new_vars = existing_vars.copy() + tokens = shlex.split(play_ds.get('include', '')) + for t in tokens[1:]: + (k,v) = t.split("=", 1) + new_vars[k] = template(basedir, v, new_vars) + + return (new_vars, tokens[0]) + + # ***************************************************** + + def _get_playbook_vars_files(self, play_ds, existing_vars_files): + new_vars_files = list(existing_vars_files) + if 'vars_files' in play_ds: + new_vars_files = utils.list_union(new_vars_files, play_ds['vars_files']) + return new_vars_files + + # ***************************************************** + + def _load_playbook_from_file(self, path, vars={}, vars_files=[]): ''' run top level error checking on playbooks and allow them to include other playbooks. ''' @@ -201,36 +240,25 @@ # a playbook (list of plays) decided to include some other list of plays # from another file. The result is a flat list of plays in the end. - tokens = shlex.split(play['include']) - - incvars = vars.copy() - if 'vars' in play: - if isinstance(play['vars'], dict): - incvars.update(play['vars']) - elif isinstance(play['vars'], list): - for v in play['vars']: - incvars.update(v) - - # allow key=value parameters to be specified on the include line - # to set variables - - for t in tokens[1:]: - - (k,v) = t.split("=", 1) - incvars[k] = template(basedir, v, incvars) + play_vars = self._get_playbook_vars(play, vars) + play_vars_files = self._get_playbook_vars_files(play, vars_files) + inc_vars, inc_path = self._get_include_info(play, basedir, play_vars) + play_vars.update(inc_vars) - included_path = utils.path_dwim(basedir, template(basedir, tokens[0], incvars)) - (plays, basedirs) = self._load_playbook_from_file(included_path, incvars) + included_path = utils.path_dwim(basedir, template(basedir, inc_path, play_vars)) + (plays, basedirs) = self._load_playbook_from_file(included_path, vars=play_vars, vars_files=play_vars_files) for p in plays: # support for parameterized play includes works by passing # those variables along to the subservient play if 'vars' not in p: p['vars'] = {} if isinstance(p['vars'], dict): - p['vars'].update(incvars) + p['vars'].update(play_vars) elif isinstance(p['vars'], list): # nobody should really do this, but handle vars: a=1 b=2 - p['vars'].extend([{k:v} for k,v in incvars.iteritems()]) + p['vars'].extend([{k:v} for k,v in play_vars.iteritems()]) + # now add in the vars_files + p['vars_files'] = utils.list_union(p.get('vars_files', []), play_vars_files) accumulated_plays.extend(plays) play_basedirs.extend(basedirs) @@ -320,7 +348,7 @@ def _trim_unavailable_hosts(self, hostlist=[]): ''' returns a list of hosts that haven't failed and aren't dark ''' - return [ h for h in self.inventory.list_hosts(hostlist) if (h not in self.stats.failures) and (h not in self.stats.dark)] + return [ h for h in hostlist if (h not in self.stats.failures) and (h not in self.stats.dark)] # ***************************************************** @@ -505,6 +533,7 @@ # push any variables down to the system setup_results = ansible.runner.Runner( + basedir=self.basedir, pattern=play.hosts, module_name='setup', module_args={}, diff -Nru ansible-1.6.1-quantal-unstable/lib/ansible/playbook/play.py ansible-1.6.2-quantal-unstable/lib/ansible/playbook/play.py --- ansible-1.6.1-quantal-unstable/lib/ansible/playbook/play.py 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/lib/ansible/playbook/play.py 2014-05-23 21:16:25.000000000 +0000 @@ -294,26 +294,24 @@ else: self.included_roles.append(dep) + def _merge_conditional(cur_conditionals, new_conditionals): + if isinstance(new_conditionals, (basestring, bool)): + cur_conditionals.append(new_conditionals) + elif isinstance(new_conditionals, list): + cur_conditionals.extend(new_conditionals) + # pass along conditionals from roles to dep roles - if type(role) is dict: - if 'when' in passed_vars: - if 'when' in dep_vars: - tmpcond = [] - - if type(passed_vars['when']) is str: - tmpcond.append(passed_vars['when']) - elif type(passed_vars['when']) is list: - tmpcond += passed_vars['when'] - - if type(dep_vars['when']) is str: - tmpcond.append(dep_vars['when']) - elif type(dep_vars['when']) is list: - tmpcond += dep_vars['when'] - - if len(tmpcond) > 0: - dep_vars['when'] = tmpcond - else: - dep_vars['when'] = passed_vars['when'] + passed_when = passed_vars.get('when') + role_when = role_vars.get('when') + dep_when = dep_vars.get('when') + + tmpcond = [] + _merge_conditional(tmpcond, passed_when) + _merge_conditional(tmpcond, role_when) + _merge_conditional(tmpcond, dep_when) + + if len(tmpcond) > 0: + dep_vars['when'] = tmpcond self._build_role_dependencies([dep], dep_stack, passed_vars=dep_vars, level=level+1) dep_stack.append([dep,dep_path,dep_vars,dep_defaults_data]) @@ -565,7 +563,10 @@ task_vars = utils.combine_vars(task_vars, x['vars']) if 'when' in x: - included_additional_conditions.append(x['when']) + if isinstance(x['when'], (basestring, bool)): + included_additional_conditions.append(x['when']) + elif isinstance(x['when'], list): + included_additional_conditions.extend(x['when']) new_role = None if 'role_name' in x: @@ -785,7 +786,7 @@ """ update a host's varscache with new var data """ data = utils.combine_vars(inject, data) - self.playbook.VARS_CACHE[host].update(data) + self.playbook.VARS_CACHE[host] = utils.combine_vars(self.playbook.VARS_CACHE.get(host, {}), data) self.playbook.callbacks.on_import_for_host(host, filename4) def process_files(filename, filename2, filename3, filename4, host=None): @@ -855,5 +856,4 @@ # finally, update the VARS_CACHE for the host, if it is set if host is not None: - self.playbook.VARS_CACHE[host].update(self.vars) self.playbook.VARS_CACHE[host].update(self.playbook.extra_vars) diff -Nru ansible-1.6.1-quantal-unstable/lib/ansible/runner/action_plugins/synchronize.py ansible-1.6.2-quantal-unstable/lib/ansible/runner/action_plugins/synchronize.py --- ansible-1.6.1-quantal-unstable/lib/ansible/runner/action_plugins/synchronize.py 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/lib/ansible/runner/action_plugins/synchronize.py 2014-05-23 21:16:25.000000000 +0000 @@ -32,11 +32,12 @@ if 'vars' in self.inject: if '_original_file' in self.inject['vars']: # roles + original_path = path path = utils.path_dwim_relative(self.inject['_original_file'], 'files', path, self.runner.basedir) - elif 'inventory_dir' in self.inject['vars']: - # non-roles - abs_dir = os.path.abspath(self.inject['vars']['inventory_dir']) - path = os.path.join(abs_dir, path) + if original_path and original_path[-1] == '/' and path[-1] != '/': + # make sure the dwim'd path ends in a trailing "/" + # if the original path did + path += '/' return path diff -Nru ansible-1.6.1-quantal-unstable/lib/ansible/runner/__init__.py ansible-1.6.2-quantal-unstable/lib/ansible/runner/__init__.py --- ansible-1.6.1-quantal-unstable/lib/ansible/runner/__init__.py 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/lib/ansible/runner/__init__.py 2014-05-23 21:16:25.000000000 +0000 @@ -566,11 +566,14 @@ # merge the VARS and SETUP caches for this host combined_cache = self.setup_cache.copy() - combined_cache.get(host, {}).update(self.vars_cache.get(host, {})) + combined_cache.setdefault(host, {}).update(self.vars_cache.get(host, {})) hostvars = HostVars(combined_cache, self.inventory, vault_password=self.vault_pass) # use combined_cache and host_variables to template the module_vars + # we update the inject variables with the data we're about to template + # since some of the variables we'll be replacing may be contained there too module_vars_inject = utils.combine_vars(combined_cache.get(host, {}), host_variables) + module_vars_inject.update(self.module_vars) module_vars = template.template(self.basedir, self.module_vars, module_vars_inject) inject = {} @@ -890,7 +893,7 @@ if (module_name == 'async_status' and "finished" in data) or module_name != 'async_status': if changed_when is not None and 'skipped' not in data: data['changed'] = utils.check_conditional(changed_when, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars) - if failed_when is not None: + if failed_when is not None and 'skipped' not in data: data['failed_when_result'] = data['failed'] = utils.check_conditional(failed_when, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars) if is_chained: diff -Nru ansible-1.6.1-quantal-unstable/lib/ansible/utils/__init__.py ansible-1.6.2-quantal-unstable/lib/ansible/utils/__init__.py --- ansible-1.6.1-quantal-unstable/lib/ansible/utils/__init__.py 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/lib/ansible/utils/__init__.py 2014-05-23 21:16:25.000000000 +0000 @@ -1005,6 +1005,22 @@ return False return True +def _listify(a): + if not isinstance(a, (list, tuple)): + return [a,] + else: + return a + +def list_union(a, b): + set_a = set(_listify(a)) + set_b = set(_listify(b)) + return list(set_a.union(set_b)) + +def list_intersection(a, b): + set_a = set(_listify(a)) + set_b = set(_listify(b)) + return list(set_a.intersection(set_b)) + def safe_eval(expr, locals={}, include_exceptions=False): ''' this is intended for allowing things like: diff -Nru ansible-1.6.1-quantal-unstable/library/cloud/docker ansible-1.6.2-quantal-unstable/library/cloud/docker --- ansible-1.6.1-quantal-unstable/library/cloud/docker 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/cloud/docker 2014-05-23 21:16:25.000000000 +0000 @@ -384,7 +384,7 @@ self.env = None if self.module.params.get('env'): - self.env = dict(map(lambda x: x.split("="), self.module.params.get('env'))) + self.env = dict(map(lambda x: x.split("=", 1), self.module.params.get('env'))) # connect to docker server docker_url = urlparse(module.params.get('docker_url')) @@ -507,14 +507,16 @@ running_image, running_tag = self.get_split_image_tag(i['Image']) running_command = i['Command'].strip() - name_matches = (name and name in i['Names']) + name_matches = False + if i["Names"]: + name_matches = (name and name in i['Names']) image_matches = (running_image == image) tag_matches = (not tag or running_tag == tag) # if a container has an entrypoint, `command` will actually equal # '{} {}'.format(entrypoint, command) command_matches = (not command or running_command.endswith(command)) - if name_matches or (image_matches and tag_matches and command_matches): + if name_matches or (name is None and image_matches and tag_matches and command_matches): details = self.client.inspect_container(i['Id']) details = _docker_id_quirk(details) deployed.append(details) @@ -524,7 +526,7 @@ def get_running_containers(self): running = [] for i in self.get_deployed_containers(): - if i['State']['Running'] == True and i['State']['Ghost'] == False: + if i['State']['Running'] == True and i['State'].get('Ghost', False) == False: running.append(i) return running diff -Nru ansible-1.6.1-quantal-unstable/library/cloud/ec2 ansible-1.6.2-quantal-unstable/library/cloud/ec2 --- ansible-1.6.1-quantal-unstable/library/cloud/ec2 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/cloud/ec2 2014-05-23 21:16:25.000000000 +0000 @@ -175,7 +175,7 @@ instance_ids: version_added: "1.3" description: - - list of instance ids, currently only used when state='absent' + - list of instance ids, currently used for states: absent, running, stopped required: false default: null aliases: [] @@ -1025,7 +1025,7 @@ return (changed, instance_dict_array, terminated_instance_ids) -def startstop_instances(module, ec2, instance_ids): +def startstop_instances(module, ec2, instance_ids, state): """ Starts or stops a list of existing instances @@ -1033,9 +1033,10 @@ ec2: authenticated ec2 connection object instance_ids: The list of instances to start in the form of [ {id: }, ..] + state: Intended state ("running" or "stopped") Returns a dictionary of instance information - about the instances started. + about the instances started/stopped. If the instance was not able to change state, "changed" will be set to False. @@ -1050,18 +1051,15 @@ if not isinstance(instance_ids, list) or len(instance_ids) < 1: module.fail_json(msg='instance_ids should be a list of instances, aborting') - dest_state = module.params.get('state') - dest_state_ec2 = 'stopped' if dest_state == 'stopped' else 'running' - # Check that our instances are not in the state we want to take them to # and change them to our desired state running_instances_array = [] for res in ec2.get_all_instances(instance_ids): for inst in res.instances: - if not inst.state == dest_state_ec2: + if inst.state != state: instance_dict_array.append(get_instance_info(inst)) try: - if dest_state == 'running': + if state == 'running': inst.start() else: inst.stop() @@ -1070,21 +1068,14 @@ changed = True ## Wait for all the instances to finish starting or stopping - instids = [ i.id for i in res.instances ] - this_res = [] - num_running = 0 wait_timeout = time.time() + wait_timeout - while wait_timeout > time.time() and num_running < len(instids): - res_list = res.connection.get_all_instances(instids) - if len(res_list) > 0: - this_res = res_list[0] - num_running = len([ i for i in this_res.instances if i.state == dest_state_ec2 ]) - else: - # got a bad response of some sort, possibly due to - # stale/cached data. Wait a second and then try again - time.sleep(1) - continue - if wait and num_running < len(instids): + while wait and wait_timeout > time.time(): + matched_instances = [] + for res in ec2.get_all_instances(instance_ids): + for i in res.instances: + if i.state == state: + matched_instances.append(i) + if len(matched_instances) < len(instance_ids): time.sleep(5) else: break @@ -1093,10 +1084,7 @@ # waiting took too long module.fail_json(msg = "wait for instances running timeout on %s" % time.asctime()) - for inst in this_res.instances: - running_instances_array.append(inst.id) - - return (changed, instance_dict_array, running_instances_array) + return (changed, instance_dict_array, instance_ids) def main(): @@ -1147,21 +1135,23 @@ tagged_instances = [] - if module.params.get('state') == 'absent': + state = module.params.get('state') + + if state == 'absent': instance_ids = module.params.get('instance_ids') if not isinstance(instance_ids, list): module.fail_json(msg='termination_list needs to be a list of instances to terminate') (changed, instance_dict_array, new_instance_ids) = terminate_instances(module, ec2, instance_ids) - elif module.params.get('state') == 'running' or module.params.get('state') == 'stopped': + elif state in ('running', 'stopped'): instance_ids = module.params.get('instance_ids') if not isinstance(instance_ids, list): module.fail_json(msg='running list needs to be a list of instances to run: %s' % instance_ids) - (changed, instance_dict_array, new_instance_ids) = startstop_instances(module, ec2, instance_ids) + (changed, instance_dict_array, new_instance_ids) = startstop_instances(module, ec2, instance_ids, state) - elif module.params.get('state') == 'present': + elif state == 'present': # Changed is always set to true when provisioning new instances if not module.params.get('image'): module.fail_json(msg='image parameter is required for new instance') diff -Nru ansible-1.6.1-quantal-unstable/library/cloud/ec2_group ansible-1.6.2-quantal-unstable/library/cloud/ec2_group --- ansible-1.6.1-quantal-unstable/library/cloud/ec2_group 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/cloud/ec2_group 2014-05-23 21:16:25.000000000 +0000 @@ -317,7 +317,8 @@ # when using a vpc, but no egress rules are specified, # we add in a default allow all out rule, which was the # default behavior before egress rules were added - if 'out--1-None-None-None-0.0.0.0/0' not in groupRules: + default_egress_rule = 'out--1-None-None-None-0.0.0.0/0' + if default_egress_rule not in groupRules: ec2.authorize_security_group_egress( group_id=group.id, ip_protocol=-1, @@ -327,6 +328,9 @@ cidr_ip='0.0.0.0/0' ) changed = True + else: + # make sure the default egress rule is not removed + del groupRules[default_egress_rule] # Finally, remove anything left in the groupRules -- these will be defunct rules for rule in groupRules.itervalues(): diff -Nru ansible-1.6.1-quantal-unstable/library/cloud/rds ansible-1.6.2-quantal-unstable/library/cloud/rds --- ansible-1.6.1-quantal-unstable/library/cloud/rds 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/cloud/rds 2014-05-23 21:16:25.000000000 +0000 @@ -370,6 +370,9 @@ else: return 'vpc_security_groups' + # Package up the optional parameters + params = {} + # Validate parameters for each command if command == 'create': required_vars = [ 'instance_name', 'db_engine', 'size', 'instance_type', 'username', 'password' ] @@ -413,9 +416,6 @@ if module.params.get(v): module.fail_json(msg = str("Parameter %s invalid for %s command" % (v, command))) - # Package up the optional parameters - params = {} - if db_engine: params["engine"] = db_engine diff -Nru ansible-1.6.1-quantal-unstable/library/cloud/route53 ansible-1.6.2-quantal-unstable/library/cloud/route53 --- ansible-1.6.1-quantal-unstable/library/cloud/route53 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/cloud/route53 2014-05-23 21:16:25.000000000 +0000 @@ -223,7 +223,7 @@ # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round # tripping of things like * and @. decoded_name = rset.name.replace(r'\052', '*') - decoded_name = rset.name.replace(r'\100', '@') + decoded_name = decoded_name.replace(r'\100', '@') if rset.type == type_in and decoded_name == record_in: found_record = True diff -Nru ansible-1.6.1-quantal-unstable/library/cloud/vsphere_guest ansible-1.6.2-quantal-unstable/library/cloud/vsphere_guest --- ansible-1.6.1-quantal-unstable/library/cloud/vsphere_guest 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/cloud/vsphere_guest 2014-05-23 21:16:25.000000000 +0000 @@ -976,7 +976,15 @@ for k, v in value.items(): if k in self.check_dict[key]: if not isinstance(self.check_dict[key][k], v): - self.recursive_missing.append((k, v)) + try: + if v == int: + self.check_dict[key][k] = int(self.check_dict[key][k]) + elif v == basestring: + self.check_dict[key][k] = str(self.check_dict[key][k]) + else: + raise ValueError + except ValueError: + self.recursive_missing.append((k, v)) else: self.recursive_missing.append((k, v)) diff -Nru ansible-1.6.1-quantal-unstable/library/files/copy ansible-1.6.2-quantal-unstable/library/files/copy --- ansible-1.6.1-quantal-unstable/library/files/copy 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/files/copy 2014-05-23 21:16:25.000000000 +0000 @@ -204,6 +204,8 @@ os.unlink(dest) open(dest, 'w').close() if validate: + if "%s" not in validate: + module.fail_json(msg="validate must contain %%s: %s" % (validate)) (rc,out,err) = module.run_command(validate % src) if rc != 0: module.fail_json(msg="failed to validate: rc:%s error:%s" % (rc,err)) diff -Nru ansible-1.6.1-quantal-unstable/library/files/file ansible-1.6.2-quantal-unstable/library/files/file --- ansible-1.6.1-quantal-unstable/library/files/file 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/files/file 2014-05-23 21:16:25.000000000 +0000 @@ -184,7 +184,7 @@ absrc = src if not os.path.isabs(absrc): - absrc = os.path.normpath('%s/%s' % (os.path.dirname(path), absrc)) + absrc = os.path.abspath(src) if not os.path.exists(absrc) and not force: module.fail_json(path=path, src=src, msg='src file does not exist, use "force=yes" if you really want to create the link: %s' % absrc) diff -Nru ansible-1.6.1-quantal-unstable/library/files/lineinfile ansible-1.6.2-quantal-unstable/library/files/lineinfile --- ansible-1.6.1-quantal-unstable/library/files/lineinfile 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/files/lineinfile 2014-05-23 21:16:25.000000000 +0000 @@ -154,6 +154,8 @@ validate = module.params.get('validate', None) valid = not validate if validate: + if "%s" not in validate: + module.fail_json(msg="validate must contain %%s: %s" % (validate)) (rc, out, err) = module.run_command(validate % tmpfile) valid = rc == 0 if rc != 0: diff -Nru ansible-1.6.1-quantal-unstable/library/files/replace ansible-1.6.2-quantal-unstable/library/files/replace --- ansible-1.6.1-quantal-unstable/library/files/replace 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/files/replace 2014-05-23 21:16:25.000000000 +0000 @@ -90,6 +90,8 @@ validate = module.params.get('validate', None) valid = not validate if validate: + if "%s" not in validate: + module.fail_json(msg="validate must contain %%s: %s" % (validate)) (rc, out, err) = module.run_command(validate % tmpfile) valid = rc == 0 if rc != 0: diff -Nru ansible-1.6.1-quantal-unstable/library/network/uri ansible-1.6.2-quantal-unstable/library/network/uri --- ansible-1.6.1-quantal-unstable/library/network/uri 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/network/uri 2014-05-23 21:16:25.000000000 +0000 @@ -406,7 +406,6 @@ # Write the file out if requested if dest is not None: if resp['status'] == 304: - status_code = 304 changed = False else: write_file(module, url, dest, content) diff -Nru ansible-1.6.1-quantal-unstable/library/packaging/apt_repository ansible-1.6.2-quantal-unstable/library/packaging/apt_repository --- ansible-1.6.1-quantal-unstable/library/packaging/apt_repository 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/packaging/apt_repository 2014-05-23 21:16:25.000000000 +0000 @@ -320,6 +320,8 @@ headers = dict(Accept='application/json') response, info = fetch_url(self.module, lp_api, headers=headers) + if info['status'] != 200: + self.module.fail_json(msg="failed to fetch PPA information, error was: %s" % info['msg']) return json.load(response) def _expand_ppa(self, path): diff -Nru ansible-1.6.1-quantal-unstable/library/source_control/git ansible-1.6.2-quantal-unstable/library/source_control/git --- ansible-1.6.1-quantal-unstable/library/source_control/git 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/library/source_control/git 2014-05-23 21:16:25.000000000 +0000 @@ -181,7 +181,15 @@ def write_ssh_wrapper(): module_dir = get_module_path() - fd, wrapper_path = tempfile.mkstemp(prefix=module_dir + '/') + try: + # make sure we have full permission to the module_dir, which + # may not be the case if we're sudo'ing to a non-root user + if os.access(module_dir, os.W_OK|os.R_OK|os.X_OK): + fd, wrapper_path = tempfile.mkstemp(prefix=module_dir + '/') + else: + raise OSError + except (IOError, OSError): + fd, wrapper_path = tempfile.mkstemp() fh = os.fdopen(fd, 'w+b') template = """#!/bin/sh if [ -z "$GIT_SSH_OPTS" ]; then @@ -502,6 +510,7 @@ if key_file or ssh_opts: ssh_wrapper = write_ssh_wrapper() set_git_ssh(ssh_wrapper, key_file, ssh_opts) + module.add_cleanup_file(path=ssh_wrapper) # add the git repo's hostkey if module.params['ssh_opts'] is not None: diff -Nru ansible-1.6.1-quantal-unstable/packaging/rpm/ansible.spec ansible-1.6.2-quantal-unstable/packaging/rpm/ansible.spec --- ansible-1.6.1-quantal-unstable/packaging/rpm/ansible.spec 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/packaging/rpm/ansible.spec 2014-05-23 21:16:25.000000000 +0000 @@ -1,11 +1,12 @@ %define name ansible +%define ansible_version $VERSION %if 0%{?rhel} == 5 %define __python /usr/bin/python26 %endif Name: %{name} -Version: 1.6.1 +Version: %{ansible_version} Release: 1%{?dist} Url: http://www.ansible.com Summary: SSH-based application deployment, configuration management, and IT orchestration platform @@ -114,6 +115,9 @@ %changelog +* Fri May 23 2014 Michael DeHaan - 1.6.2 +- Release 1.6.2 + * Wed May 07 2014 Michael DeHaan - 1.6.1 - Release 1.6.1 diff -Nru ansible-1.6.1-quantal-unstable/PKG-INFO ansible-1.6.2-quantal-unstable/PKG-INFO --- ansible-1.6.1-quantal-unstable/PKG-INFO 2014-05-07 20:52:08.000000000 +0000 +++ ansible-1.6.2-quantal-unstable/PKG-INFO 2014-05-23 21:16:25.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: ansible -Version: 1.6.1 +Version: 1.6.2 Summary: Radically simple IT automation Home-page: http://ansible.com/ Author: Michael DeHaan