Fix python3 and nonascii handling in inventory plugins

Fixes #30663
pull/30671/head
Toshio Kuratomi 7 years ago
parent f12c6e0946
commit 72bdf7aa7e

@ -60,18 +60,18 @@ class BaseInventoryPlugin(object):
def verify_file(self, path): def verify_file(self, path):
''' Verify if file is usable by this plugin, base does minimal accessability check ''' ''' Verify if file is usable by this plugin, base does minimal accessability check '''
b_path = to_bytes(path) b_path = to_bytes(path, errors='surrogate_or_strict')
return (os.path.exists(b_path) and os.access(b_path, os.R_OK)) return (os.path.exists(b_path) and os.access(b_path, os.R_OK))
def get_cache_prefix(self, path): def get_cache_prefix(self, path):
''' create predictable unique prefix for plugin/inventory ''' ''' create predictable unique prefix for plugin/inventory '''
m = hashlib.sha1() m = hashlib.sha1()
m.update(to_bytes(self.NAME)) m.update(to_bytes(self.NAME, errors='surrogate_or_strict'))
d1 = m.hexdigest() d1 = m.hexdigest()
n = hashlib.sha1() n = hashlib.sha1()
n.update(to_bytes(path)) n.update(to_bytes(path, errors='surrogate_or_strict'))
d2 = n.hexdigest() d2 = n.hexdigest()
return 's_'.join([d1[:5], d2[:5]]) return 's_'.join([d1[:5], d2[:5]])

@ -36,9 +36,9 @@ class InventoryModule(BaseInventoryPlugin):
def verify_file(self, host_list): def verify_file(self, host_list):
valid = False valid = False
b_path = to_bytes(host_list) b_path = to_bytes(host_list, errors='surrogate_or_strict')
if not os.path.exists(b_path) and ',' in host_list: if not os.path.exists(b_path) and ',' in host_list:
valid = True valid = True
return valid return valid
def parse(self, inventory, loader, host_list, cache=True): def parse(self, inventory, loader, host_list, cache=True):
@ -61,7 +61,7 @@ class InventoryModule(BaseInventoryPlugin):
if host not in self.inventory.hosts: if host not in self.inventory.hosts:
self.inventory.add_host(host, group='ungrouped', port=port) self.inventory.add_host(host, group='ungrouped', port=port)
except Exception as e: except Exception as e:
raise AnsibleParserError("Invalid data from string, could not parse: %s" % str(e)) raise AnsibleParserError("Invalid data from string, could not parse: %s" % to_native(e))
def _expand_hostpattern(self, hostpattern): def _expand_hostpattern(self, hostpattern):
''' '''

@ -27,8 +27,7 @@ EXAMPLES = r'''
import os import os
from ansible.errors import AnsibleError, AnsibleParserError from ansible.errors import AnsibleError, AnsibleParserError
from ansible.module_utils.six import string_types from ansible.module_utils._text import to_bytes, to_native
from ansible.module_utils._text import to_bytes, to_text, to_native
from ansible.parsing.utils.addresses import parse_address from ansible.parsing.utils.addresses import parse_address
from ansible.plugins.inventory import BaseInventoryPlugin from ansible.plugins.inventory import BaseInventoryPlugin
@ -40,9 +39,9 @@ class InventoryModule(BaseInventoryPlugin):
def verify_file(self, host_list): def verify_file(self, host_list):
valid = False valid = False
b_path = to_bytes(host_list) b_path = to_bytes(host_list, errors='surrogate_or_strict')
if not os.path.exists(b_path) and ',' in host_list: if not os.path.exists(b_path) and ',' in host_list:
valid = True valid = True
return valid return valid
def parse(self, inventory, loader, host_list, cache=True): def parse(self, inventory, loader, host_list, cache=True):
@ -64,4 +63,4 @@ class InventoryModule(BaseInventoryPlugin):
if host not in self.inventory.hosts: if host not in self.inventory.hosts:
self.inventory.add_host(host, group='ungrouped', port=port) self.inventory.add_host(host, group='ungrouped', port=port)
except Exception as e: except Exception as e:
raise AnsibleParserError("Invalid data from string, could not parse: %s" % str(e)) raise AnsibleParserError("Invalid data from string, could not parse: %s" % to_native(e))

@ -105,7 +105,7 @@ class InventoryModule(BaseFileInventoryPlugin):
if self.loader: if self.loader:
(b_data, private) = self.loader._get_file_contents(path) (b_data, private) = self.loader._get_file_contents(path)
else: else:
b_path = to_bytes(path) b_path = to_bytes(path, errors='surrogate_or_strict')
with open(b_path, 'rb') as fh: with open(b_path, 'rb') as fh:
b_data = fh.read() b_data = fh.read()
@ -366,14 +366,14 @@ class InventoryModule(BaseFileInventoryPlugin):
# [naughty:children] # only get coal in their stockings # [naughty:children] # only get coal in their stockings
self.patterns['section'] = re.compile( self.patterns['section'] = re.compile(
r'''^\[ to_text(r'''^\[
([^:\]\s]+) # group name (see groupname below) ([^:\]\s]+) # group name (see groupname below)
(?::(\w+))? # optional : and tag name (?::(\w+))? # optional : and tag name
\] \]
\s* # ignore trailing whitespace \s* # ignore trailing whitespace
(?:\#.*)? # and/or a comment till the (?:\#.*)? # and/or a comment till the
$ # end of the line $ # end of the line
''', re.X ''', errors='surrogate_or_strict'), re.X
) )
# FIXME: What are the real restrictions on group names, or rather, what # FIXME: What are the real restrictions on group names, or rather, what
@ -382,10 +382,10 @@ class InventoryModule(BaseFileInventoryPlugin):
# precise rules in order to support better diagnostics. # precise rules in order to support better diagnostics.
self.patterns['groupname'] = re.compile( self.patterns['groupname'] = re.compile(
r'''^ to_text(r'''^
([^:\]\s]+) ([^:\]\s]+)
\s* # ignore trailing whitespace \s* # ignore trailing whitespace
(?:\#.*)? # and/or a comment till the (?:\#.*)? # and/or a comment till the
$ # end of the line $ # end of the line
''', re.X ''', errors='surrogate_or_strict'), re.X
) )

@ -49,10 +49,11 @@ simple_config_file:
import os import os
from collections import MutableMapping
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from ansible.errors import AnsibleParserError from ansible.errors import AnsibleParserError
from ansible.module_utils._text import to_bytes, to_text from ansible.module_utils._text import to_bytes, to_native, to_text
from ansible.plugins.inventory import BaseInventoryPlugin from ansible.plugins.inventory import BaseInventoryPlugin
@ -60,14 +61,14 @@ class InventoryModule(BaseInventoryPlugin):
''' Host inventory parser for ansible using local virtualbox. ''' ''' Host inventory parser for ansible using local virtualbox. '''
NAME = 'virtualbox' NAME = 'virtualbox'
VBOX = "VBoxManage" VBOX = b"VBoxManage"
def _query_vbox_data(self, host, property_path): def _query_vbox_data(self, host, property_path):
ret = None ret = None
try: try:
cmd = [self.VBOX, 'guestproperty', 'get', host, property_path] cmd = [self.VBOX, b'guestproperty', b'get', to_bytes(host, errors='surrogate_or_strict'), to_bytes(property_path, errors='surrogate_or_strict')]
x = Popen(cmd, stdout=PIPE) x = Popen(cmd, stdout=PIPE)
ipinfo = x.stdout.read() ipinfo = to_text(x.stdout.read(), errors='surrogate_or_strict')
if 'Value' in ipinfo: if 'Value' in ipinfo:
a, ip = ipinfo.split(':', 1) a, ip = ipinfo.split(':', 1)
ret = ip.strip() ret = ip.strip()
@ -81,7 +82,7 @@ class InventoryModule(BaseInventoryPlugin):
for host in hostvars: for host in hostvars:
# create vars from vbox properties # create vars from vbox properties
if data.get('query') and isinstance(data['query'], dict): if data.get('query') and isinstance(data['query'], MutableMapping):
for varname in data['query']: for varname in data['query']:
hostvars[host][varname] = self._query_vbox_data(host, data['query'][varname]) hostvars[host][varname] = self._query_vbox_data(host, data['query'][varname])
@ -168,7 +169,7 @@ class InventoryModule(BaseInventoryPlugin):
try: try:
config_data = self.loader.load_from_file(path) config_data = self.loader.load_from_file(path)
except Exception as e: except Exception as e:
raise AnsibleParserError(e) raise AnsibleParserError(to_native(e))
if not config_data or config_data.get('plugin') != self.NAME: if not config_data or config_data.get('plugin') != self.NAME:
# this is not my config file # this is not my config file
@ -182,26 +183,26 @@ class InventoryModule(BaseInventoryPlugin):
pass pass
if not source_data: if not source_data:
pwfile = to_bytes(config_data.get('settings_password_file')) b_pwfile = to_bytes(config_data.get('settings_password_file'), errors='surrogate_or_strict')
running = config_data.get('running_only', False) running = config_data.get('running_only', False)
# start getting data # start getting data
cmd = [self.VBOX, 'list', '-l'] cmd = [self.VBOX, b'list', b'-l']
if running: if running:
cmd.append('runningvms') cmd.append(b'runningvms')
else: else:
cmd.append('vms') cmd.append(b'vms')
if pwfile and os.path.exists(pwfile): if b_pwfile and os.path.exists(b_pwfile):
cmd.append('--settingspwfile') cmd.append(b'--settingspwfile')
cmd.append(pwfile) cmd.append(b_pwfile)
try: try:
p = Popen(cmd, stdout=PIPE) p = Popen(cmd, stdout=PIPE)
except Exception as e: except Exception as e:
AnsibleParserError(e) AnsibleParserError(to_native(e))
source_data = p.stdout.readlines() source_data = p.stdout.read()
inventory.cache[cache_key] = to_text(source_data) inventory.cache[cache_key] = to_text(source_data, errors='surrogate_or_strict')
self._populate_from_source(source_data, config_data) self._populate_from_source(source_data.splitlines(), config_data)

@ -51,11 +51,12 @@ all: # keys must be unique, i.e. only one 'hosts' per group
import re import re
import os import os
from collections import MutableMapping
from ansible import constants as C from ansible import constants as C
from ansible.errors import AnsibleParserError from ansible.errors import AnsibleParserError
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from ansible.module_utils._text import to_bytes, to_text from ansible.module_utils._text import to_native
from ansible.parsing.utils.addresses import parse_address from ansible.parsing.utils.addresses import parse_address
from ansible.plugins.inventory import BaseFileInventoryPlugin, detect_range, expand_hostname_range from ansible.plugins.inventory import BaseFileInventoryPlugin, detect_range, expand_hostname_range
@ -74,9 +75,8 @@ class InventoryModule(BaseFileInventoryPlugin):
def verify_file(self, path): def verify_file(self, path):
valid = False valid = False
b_path = to_bytes(path) if super(InventoryModule, self).verify_file(path):
if super(InventoryModule, self).verify_file(b_path): file_name, ext = os.path.splitext(path)
file_name, ext = os.path.splitext(b_path)
if ext and ext in C.YAML_FILENAME_EXTENSIONS: if ext and ext in C.YAML_FILENAME_EXTENSIONS:
valid = True valid = True
return valid return valid
@ -96,11 +96,11 @@ class InventoryModule(BaseFileInventoryPlugin):
# We expect top level keys to correspond to groups, iterate over them # We expect top level keys to correspond to groups, iterate over them
# to get host, vars and subgroups (which we iterate over recursivelly) # to get host, vars and subgroups (which we iterate over recursivelly)
if isinstance(data, dict): if isinstance(data, MutableMapping):
for group_name in data: for group_name in data:
self._parse_group(group_name, data[group_name]) self._parse_group(group_name, data[group_name])
else: else:
raise AnsibleParserError("Invalid data from file, expected dictionary and got:\n\n%s" % data) raise AnsibleParserError("Invalid data from file, expected dictionary and got:\n\n%s" % to_native(data))
def _parse_group(self, group, group_data): def _parse_group(self, group, group_data):
@ -108,7 +108,7 @@ class InventoryModule(BaseFileInventoryPlugin):
self.inventory.add_group(group) self.inventory.add_group(group)
if isinstance(group_data, dict): if isinstance(group_data, MutableMapping):
# make sure they are dicts # make sure they are dicts
for section in ['vars', 'children', 'hosts']: for section in ['vars', 'children', 'hosts']:
if section in group_data and isinstance(group_data[section], string_types): if section in group_data and isinstance(group_data[section], string_types):
@ -167,4 +167,4 @@ class InventoryModule(BaseFileInventoryPlugin):
''' '''
Compiles the regular expressions required to parse the inventory and stores them in self.patterns. Compiles the regular expressions required to parse the inventory and stores them in self.patterns.
''' '''
self.patterns['groupname'] = re.compile(r'''^[A-Za-z_][A-Za-z0-9_]*$''') self.patterns['groupname'] = re.compile(u'''^[A-Za-z_][A-Za-z0-9_]*$''')

Loading…
Cancel
Save