def test_without_ranges(self): for t in self.tests: test = self.tests[t] (host, port) = parse_address(t) assert host == test[0] assert port == test[1]
def run(self, tmp=None, task_vars=dict()): if self._play_context.check_mode: return dict(skipped=True, msg='check mode not supported for this module') # Parse out any hostname:port patterns new_name = self._task.args.get('name', self._task.args.get('hostname', None)) #vv("creating host via 'add_host': hostname=%s" % new_name) name, port = parse_address(new_name, allow_ranges=False) if not name: raise AnsibleError("Invalid inventory hostname: %s" % new_name) if port: self._task.args['ansible_ssh_port'] = port groups = self._task.args.get('groupname', self._task.args.get('groups', self._task.args.get('group', ''))) # add it to the group if that was specified new_groups = [] if groups: for group_name in groups.split(","): if group_name not in new_groups: new_groups.append(group_name.strip()) # Add any variables to the new_host host_vars = dict() for k in self._task.args.keys(): if not k in [ 'name', 'hostname', 'groupname', 'groups' ]: host_vars[k] = self._task.args[k] return dict(changed=True, add_host=dict(host_name=name, groups=new_groups, host_vars=host_vars))
def _expand_hostpattern(self, hostpattern): ''' Takes a single host pattern and returns a list of hostnames and an optional port number that applies to all of them. ''' # Can the given hostpattern be parsed as a host with an optional port # specification? try: (pattern, port) = parse_address(hostpattern, allow_ranges=True) except: # not a recognizable host pattern pattern = hostpattern port = None # Once we have separated the pattern, we expand it into list of one or # more hostnames, depending on whether it contains any [x:y] ranges. if detect_range(pattern): hostnames = expand_hostname_range(pattern) else: hostnames = [pattern] return (hostnames, port)
def test_with_ranges(self): for t in self.range_tests: test = self.range_tests[t] (host, port) = parse_address(t, allow_ranges=True) assert host == test[0] assert port == test[1]
def parse_inventory(self, host_list): if isinstance(host_list, string_types): if "," in host_list: host_list = host_list.split(",") host_list = [ h for h in host_list if h and h.strip() ] self.parser = None # Always create the 'all' and 'ungrouped' groups, even if host_list is # empty: in this case we will subsequently an the implicit 'localhost' to it. ungrouped = Group('ungrouped') all = Group('all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) if host_list is None: pass elif isinstance(host_list, list): for h in host_list: try: (host, port) = parse_address(h, allow_ranges=False) except AnsibleError as e: display.vvv("Unable to parse address from hostname, leaving unchanged: %s" % to_unicode(e)) host = h port = None all.add_host(Host(host, port)) elif self._loader.path_exists(host_list): #TODO: switch this to a plugin loader and a 'condition' per plugin on which it should be tried, restoring 'inventory pllugins' if self.is_directory(host_list): # Ensure basedir is inside the directory host_list = os.path.join(self.host_list, "") self.parser = InventoryDirectory(loader=self._loader, groups=self.groups, filename=host_list) else: self.parser = get_file_parser(host_list, self.groups, self._loader) vars_loader.add_directory(self.basedir(), with_subdir=True) if not self.parser: # should never happen, but JIC raise AnsibleError("Unable to parse %s as an inventory source" % host_list) else: display.warning("Host file not found: %s" % to_unicode(host_list)) self._vars_plugins = [ x for x in vars_loader.all(self) ] # set group vars from group_vars/ files and vars plugins for g in self.groups: group = self.groups[g] group.vars = combine_vars(group.vars, self.get_group_variables(group.name)) # set host vars from host_vars/ files and vars plugins for host in self.get_hosts(): host.vars = combine_vars(host.vars, self.get_host_variables(host.name))
def test_without_ranges(self): for t in self.tests: test = self.tests[t] try: (host, port) = parse_address(t) except: host = None port = None assert host == test[0] assert port == test[1]
def test_with_ranges(self): for t in self.range_tests: test = self.range_tests[t] try: (host, port) = parse_address(t, allow_ranges=True) except: host = None port = None assert host == test[0] assert port == test[1]
def run(self, tmp=None, task_vars=None): self._supports_check_mode = True result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect # Parse out any hostname:port patterns new_name = self._task.args.get('name', self._task.args.get('hostname', self._task.args.get('host', None))) if new_name is None: result['failed'] = True result['msg'] = 'name or hostname arg needs to be provided' return result display.vv("creating host via 'add_host': hostname=%s" % new_name) try: name, port = parse_address(new_name, allow_ranges=False) except: # not a parsable hostname, but might still be usable name = new_name port = None if port: self._task.args['ansible_ssh_port'] = port groups = self._task.args.get('groupname', self._task.args.get('groups', self._task.args.get('group', ''))) # add it to the group if that was specified new_groups = [] if groups: if isinstance(groups, list): group_list = groups elif isinstance(groups, string_types): group_list = groups.split(",") else: raise AnsibleError("Groups must be specified as a list.", obj=self._task) for group_name in group_list: if group_name not in new_groups: new_groups.append(group_name.strip()) # Add any variables to the new_host host_vars = dict() special_args = frozenset(('name', 'hostname', 'groupname', 'groups')) for k in self._task.args.keys(): if k not in special_args: host_vars[k] = self._task.args[k] result['changed'] = True result['add_host'] = dict(host_name=name, groups=new_groups, host_vars=host_vars) return result
def _split_pattern(self, pattern): """ Takes a string containing host patterns separated by commas (or a list thereof) and returns a list of single patterns (which may not contain commas). Whitespace is ignored. Also accepts ':' as a separator for backwards compatibility, but it is not recommended due to the conflict with IPv6 addresses and host ranges. Example: 'a,b[1], c[2:3] , d' -> ['a', 'b[1]', 'c[2:3]', 'd'] """ if isinstance(pattern, list): return list(itertools.chain(*map(self._split_pattern, pattern))) if ';' in pattern: display.deprecated("Use ',' instead of ':' or ';' to separate host patterns", version=2.0, removed=True) # If it's got commas in it, we'll treat it as a straightforward # comma-separated list of patterns. elif ',' in pattern: patterns = re.split('\s*,\s*', pattern) # If it doesn't, it could still be a single pattern. This accounts for # non-separator uses of colons: IPv6 addresses and [x:y] host ranges. else: (base, port) = parse_address(pattern, allow_ranges=True) if base: patterns = [pattern] # The only other case we accept is a ':'-separated list of patterns. # This mishandles IPv6 addresses, and is retained only for backwards # compatibility. else: patterns = re.findall( r'''(?: # We want to match something comprising: [^\s:\[\]] # (anything other than whitespace or ':[]' | # ...or... \[[^\]]*\] # a single complete bracketed expression) )+ # occurring once or more ''', pattern, re.X ) if len(patterns) > 1: display.deprecated("Use ',' instead of ':' or ';' to separate host patterns", version=2.0) return [p.strip() for p in patterns]
def parse_inventory(self, host_list): if isinstance(host_list, string_types): if "," in host_list: host_list = host_list.split(",") host_list = [ h for h in host_list if h and h.strip() ] self.parser = None # Always create the 'all' and 'ungrouped' groups, even if host_list is # empty: in this case we will subsequently an the implicit 'localhost' to it. ungrouped = Group(name='ungrouped') all = Group('all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) if host_list is None: pass elif isinstance(host_list, list): for h in host_list: (host, port) = parse_address(h, allow_ranges=False) all.add_host(Host(host, port)) elif self._loader.path_exists(host_list): #TODO: switch this to a plugin loader and a 'condition' per plugin on which it should be tried, restoring 'inventory pllugins' if self._loader.is_directory(host_list): # Ensure basedir is inside the directory host_list = os.path.join(self.host_list, "") self.parser = InventoryDirectory(loader=self._loader, groups=self.groups, filename=host_list) else: self.parser = get_file_parser(host_list, self.groups, self._loader) vars_loader.add_directory(self.basedir(), with_subdir=True) if not self.parser: # should never happen, but JIC raise AnsibleError("Unable to parse %s as an inventory source" % host_list) self._vars_plugins = [ x for x in vars_loader.all(self) ] # FIXME: shouldn't be required, since the group/host vars file # management will be done in VariableManager # get group vars from group_vars/ files and vars plugins for group in self.groups.values(): group.vars = combine_vars(group.vars, self.get_group_variables(group.name)) # get host vars from host_vars/ files and vars plugins for host in self.get_hosts(): host.vars = combine_vars(host.vars, self.get_host_variables(host.name))
def run(self, tmp=None, task_vars=None): if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) if self._play_context.check_mode: result['skipped'] = True result['msg'] = 'check mode not supported for this module' return result # Parse out any hostname:port patterns new_name = self._task.args.get('name', self._task.args.get('hostname', None)) display.vv("creating host via 'add_host': hostname=%s" % new_name) try: name, port = parse_address(new_name, allow_ranges=False) except: # not a parsable hostname, but might still be usable name = new_name port = None if port: self._task.args['ansible_ssh_port'] = port groups = self._task.args.get('groupname', self._task.args.get('groups', self._task.args.get('group', ''))) # add it to the group if that was specified new_groups = [] if groups: for group_name in groups.split(","): if group_name not in new_groups: new_groups.append(group_name.strip()) # Add any variables to the new_host host_vars = dict() special_args = frozenset(('name', 'hostname', 'groupname', 'groups')) for k in self._task.args.keys(): if k not in special_args: host_vars[k] = self._task.args[k] result['changed'] = True result['add_host'] = dict(host_name=name, groups=new_groups, host_vars=host_vars) return result
def parse(self, inventory, loader, host_list, cache=True): ''' parses the inventory file ''' super(InventoryModule, self).parse(inventory, loader, host_list) try: for h in host_list.split(','): h = h.strip() if h: try: (host, port) = parse_address(h, allow_ranges=False) except AnsibleError as e: self.display.vvv("Unable to parse address from hostname, leaving unchanged: %s" % to_native(e)) host = h port = None if host not in self.inventory.hosts: self.inventory.add_host(host, group='ungrouped', port=port) except Exception as e: raise AnsibleParserError("Invalid data from string, could not parse: %s" % to_native(e))
def _expand_hostpattern(self, hostpattern): ''' Takes a single host pattern and returns a list of hostnames and an optional port number that applies to all of them. ''' # Can the given hostpattern be parsed as a host with an optional port # specification? (pattern, port) = parse_address(hostpattern, allow_ranges=True) if not pattern: self._raise_error("Can't parse '%s' as host[:port]" % hostpattern) # Once we have separated the pattern, we expand it into list of one or # more hostnames, depending on whether it contains any [x:y] ranges. if detect_range(pattern): hostnames = expand_hostname_range(pattern) else: hostnames = [pattern] return (hostnames, port)
def _expand_hostpattern(self, hostpattern): ''' Takes a single host pattern and returns a list of hostnames and an optional port number that applies to all of them. ''' # Can the given hostpattern be parsed as a host with an optional port # specification? try: (pattern, port) = parse_address(hostpattern, allow_ranges=True) except Exception: # not a recognizable host pattern pattern = hostpattern port = None # Once we have separated the pattern, we expand it into list of one or # more hostnames, depending on whether it contains any [x:y] ranges. if detect_range(pattern): hostnames = expand_hostname_range(pattern) else: hostnames = [pattern] return (hostnames, port)
def parse(self, inventory, loader, host_list, cache=True): ''' parses the inventory file ''' super(InventoryModule, self).parse(inventory, loader, host_list) try: for h in host_list.split(','): if h: try: (host, port) = parse_address(h, allow_ranges=False) except AnsibleError as e: self.display.vvv( "Unable to parse address from hostname, leaving unchanged: %s" % to_native(e)) host = h port = None if host not in self.inventory.hosts: self.inventory.add_host(host, group='ungrouped', port=port) except Exception as e: raise AnsibleParserError( "Invalid data from string, could not parse: %s" % str(e))
def _populate(self): raw_params = dict( docker_host=self.get_option('docker_host'), tls=self.get_option('tls'), tls_verify=self.get_option('validate_certs'), key_path=self.get_option('client_key'), cacert_path=self.get_option('ca_cert'), cert_path=self.get_option('client_cert'), tls_hostname=self.get_option('tls_hostname'), api_version=self.get_option('api_version'), timeout=self.get_option('timeout'), ssl_version=self.get_option('ssl_version'), debug=None, ) update_tls_hostname(raw_params) connect_params = get_connect_params(raw_params, fail_function=self._fail) self.client = docker.DockerClient(**connect_params) self.inventory.add_group('all') self.inventory.add_group('manager') self.inventory.add_group('worker') self.inventory.add_group('leader') self.inventory.add_group('nonleaders') if self.get_option('include_host_uri'): if self.get_option('include_host_uri_port'): host_uri_port = str(self.get_option('include_host_uri_port')) elif self.get_option('tls') or self.get_option('validate_certs'): host_uri_port = '2376' else: host_uri_port = '2375' try: self.nodes = self.client.nodes.list() for self.node in self.nodes: self.node_attrs = self.client.nodes.get(self.node.id).attrs self.inventory.add_host(self.node_attrs['ID']) self.inventory.add_host(self.node_attrs['ID'], group=self.node_attrs['Spec']['Role']) self.inventory.set_variable(self.node_attrs['ID'], 'ansible_host', self.node_attrs['Status']['Addr']) if self.get_option('include_host_uri'): self.inventory.set_variable( self.node_attrs['ID'], 'ansible_host_uri', 'tcp://' + self.node_attrs['Status']['Addr'] + ':' + host_uri_port) if self.get_option('verbose_output'): self.inventory.set_variable( self.node_attrs['ID'], 'docker_swarm_node_attributes', self.node_attrs) if 'ManagerStatus' in self.node_attrs: if self.node_attrs['ManagerStatus'].get('Leader'): # This is workaround of bug in Docker when in some cases the Leader IP is 0.0.0.0 # Check moby/moby#35437 for details swarm_leader_ip = parse_address(self.node_attrs['ManagerStatus']['Addr'])[0] or \ self.node_attrs['Status']['Addr'] if self.get_option('include_host_uri'): self.inventory.set_variable( self.node_attrs['ID'], 'ansible_host_uri', 'tcp://' + swarm_leader_ip + ':' + host_uri_port) self.inventory.set_variable(self.node_attrs['ID'], 'ansible_host', swarm_leader_ip) self.inventory.add_host(self.node_attrs['ID'], group='leader') else: self.inventory.add_host(self.node_attrs['ID'], group='nonleaders') else: self.inventory.add_host(self.node_attrs['ID'], group='nonleaders') # Use constructed if applicable strict = self.get_option('strict') # Composed variables self._set_composite_vars(self.get_option('compose'), self.node_attrs, self.node_attrs['ID'], strict=strict) # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group self._add_host_to_composed_groups(self.get_option('groups'), self.node_attrs, self.node_attrs['ID'], strict=strict) # Create groups based on variable values and add the corresponding hosts to it self._add_host_to_keyed_groups(self.get_option('keyed_groups'), self.node_attrs, self.node_attrs['ID'], strict=strict) except Exception as e: raise AnsibleError( 'Unable to fetch hosts from Docker swarm API, this was the original exception: %s' % to_native(e))
def parse_inventory(self, host_list): if isinstance(host_list, string_types): if "," in host_list: host_list = host_list.split(",") host_list = [ h for h in host_list if h and h.strip() ] self.parser = None # Always create the 'all' and 'ungrouped' groups, even if host_list is # empty: in this case we will subsequently an the implicit 'localhost' to it. ungrouped = Group('ungrouped') all = Group('all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) if host_list is None: pass elif isinstance(host_list, list): for h in host_list: try: (host, port) = parse_address(h, allow_ranges=False) except AnsibleError as e: display.vvv("Unable to parse address from hostname, leaving unchanged: %s" % to_text(e)) host = h port = None new_host = Host(host, port) if h in C.LOCALHOST: # set default localhost from inventory to avoid creating an implicit one. Last localhost defined 'wins'. if self.localhost is not None: display.warning("A duplicate localhost-like entry was found (%s). First found localhost was %s" % (h, self.localhost.name)) display.vvvv("Set default localhost to %s" % h) self.localhost = new_host all.add_host(new_host) elif self._loader.path_exists(host_list): # TODO: switch this to a plugin loader and a 'condition' per plugin on which it should be tried, restoring 'inventory pllugins' if self.is_directory(host_list): # Ensure basedir is inside the directory host_list = os.path.join(self.host_list, "") self.parser = InventoryDirectory(loader=self._loader, groups=self.groups, filename=host_list) else: self.parser = get_file_parser(host_list, self.groups, self._loader) vars_loader.add_directory(self._basedir, with_subdir=True) if not self.parser: # should never happen, but JIC raise AnsibleError("Unable to parse %s as an inventory source" % host_list) else: display.warning("Host file not found: %s" % to_text(host_list)) self._vars_plugins = [ x for x in vars_loader.all(self) ] # set group vars from group_vars/ files and vars plugins for g in self.groups: group = self.groups[g] group.vars = combine_vars(group.vars, self.get_group_variables(group.name)) self.get_group_vars(group) # get host vars from host_vars/ files and vars plugins for host in self.get_hosts(ignore_limits=True, ignore_restrictions=True): host.vars = combine_vars(host.vars, self.get_host_variables(host.name)) self.get_host_vars(host)
def parse_inventory(self, host_list): if isinstance(host_list, string_types): if "," in host_list: host_list = host_list.split(",") host_list = [ h for h in host_list if h and h.strip() ] self.parser = None # Always create the 'all' and 'ungrouped' groups, even if host_list is # empty: in this case we will subsequently add the implicit 'localhost' to it. ungrouped = Group('ungrouped') all = Group('all') all.add_child_group(ungrouped) base_groups = frozenset([all, ungrouped]) self.groups = dict(all=all, ungrouped=ungrouped) if host_list is None: pass elif isinstance(host_list, list): for h in host_list: try: (host, port) = parse_address(h, allow_ranges=False) except AnsibleError as e: display.vvv("Unable to parse address from hostname, leaving unchanged: %s" % to_text(e)) host = h port = None new_host = Host(host, port) if h in C.LOCALHOST: # set default localhost from inventory to avoid creating an implicit one. Last localhost defined 'wins'. if self.localhost is not None: display.warning("A duplicate localhost-like entry was found (%s). First found localhost was %s" % (h, self.localhost.name)) display.vvvv("Set default localhost to %s" % h) self.localhost = new_host all.add_host(new_host) elif self._loader.path_exists(host_list): # TODO: switch this to a plugin loader and a 'condition' per plugin on which it should be tried, restoring 'inventory pllugins' if self.is_directory(host_list): # Ensure basedir is inside the directory host_list = os.path.join(self.host_list, "") self.parser = InventoryDirectory(loader=self._loader, groups=self.groups, filename=host_list) else: self.parser = get_file_parser(host_list, self.groups, self._loader) vars_loader.add_directory(self._basedir, with_subdir=True) if not self.parser: # should never happen, but JIC raise AnsibleError("Unable to parse %s as an inventory source" % host_list) else: display.warning("Host file not found: %s" % to_text(host_list)) self._vars_plugins = [ x for x in vars_loader.all(self) ] ### POST PROCESS groups and hosts after specific parser was invoked group_names = set() # set group vars from group_vars/ files and vars plugins for g in self.groups: group = self.groups[g] group.vars = combine_vars(group.vars, self.get_group_variables(group.name)) self.get_group_vars(group) group_names.add(group.name) host_names = set() # get host vars from host_vars/ files and vars plugins for host in self.get_hosts(ignore_limits=True, ignore_restrictions=True): host.vars = combine_vars(host.vars, self.get_host_variables(host.name)) self.get_host_vars(host) host_names.add(host.name) mygroups = host.get_groups() # ensure hosts are always in 'all' if all not in mygroups: all.add_host(host) if ungrouped in mygroups: # clear ungrouped of any incorrectly stored by parser if set(mygroups).difference(base_groups): host.remove_group(ungrouped) else: # add ungrouped hosts to ungrouped length = len(mygroups) if length == 0 or (length == 1 and all in mygroups): ungrouped.add_host(host) # warn if overloading identifier as both group and host for conflict in group_names.intersection(host_names): display.warning("Found both group and host with same name: %s" % conflict)
def run(self, tmp=None, task_vars=None): self._supports_check_mode = True result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect args = self._task.args raw = args.pop('_raw_params', {}) if isinstance(raw, Mapping): # TODO: create 'conflict' detection in base class to deal with repeats and aliases and warn user args = combine_vars(raw, args) else: raise AnsibleActionFail( 'Invalid raw parameters passed, requires a dictonary/mapping got a %s' % type(raw)) # Parse out any hostname:port patterns new_name = args.get('name', args.get('hostname', args.get('host', None))) if new_name is None: raise AnsibleActionFail( 'name, host or hostname needs to be provided') display.vv("creating host via 'add_host': hostname=%s" % new_name) try: name, port = parse_address(new_name, allow_ranges=False) except Exception: # not a parsable hostname, but might still be usable name = new_name port = None if port: args['ansible_ssh_port'] = port groups = args.get('groupname', args.get('groups', args.get('group', ''))) # add it to the group if that was specified new_groups = [] if groups: if isinstance(groups, list): group_list = groups elif isinstance(groups, string_types): group_list = groups.split(",") else: raise AnsibleActionFail("Groups must be specified as a list.", obj=self._task) for group_name in group_list: if group_name not in new_groups: new_groups.append(group_name.strip()) # Add any variables to the new_host host_vars = {} special_args = frozenset(('name', 'hostname', 'groupname', 'groups')) for k in args.keys(): if k not in special_args: host_vars[k] = args[k] result['changed'] = False result['add_host'] = dict(host_name=name, groups=new_groups, host_vars=host_vars) return result