Merge "Add debug logging to dynamic inventory"
This commit is contained in:
commit
bcc984cb0b
@ -111,6 +111,16 @@ disk.
|
||||
This check does not do YAML syntax validation, though it will fail if there
|
||||
are unparseable errors.
|
||||
|
||||
Writing Debug Logs
|
||||
------------------
|
||||
|
||||
The ``--debug/-d`` parameter allows writing of a detailed log file for
|
||||
debugging the inventory script's behavior. The output is written to
|
||||
``inventory.log`` in the current working directory.
|
||||
|
||||
The ``inventory.log`` file is appended to, not overwritten.
|
||||
|
||||
Like ``--check``, this flag is not invoked when running from ansible.
|
||||
|
||||
Inspecting and Managing the Inventory
|
||||
-------------------------------------
|
||||
|
@ -19,6 +19,7 @@ import argparse
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import netaddr
|
||||
import os
|
||||
import Queue
|
||||
@ -28,6 +29,8 @@ import tarfile
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
logger = logging.getLogger('osa-inventory')
|
||||
|
||||
USED_IPS = set()
|
||||
INVENTORY_SKEL = {
|
||||
'_meta': {
|
||||
@ -146,6 +149,15 @@ def args(arg_list):
|
||||
action='store_true',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-d',
|
||||
'--debug',
|
||||
help=('Output debug messages to log file. '
|
||||
'File is appended to, not overwritten'),
|
||||
action='store_true',
|
||||
default=False,
|
||||
)
|
||||
|
||||
return vars(parser.parse_args(arg_list))
|
||||
|
||||
|
||||
@ -192,6 +204,7 @@ def _parse_belongs_to(key, belongs_to, inventory):
|
||||
"""
|
||||
for item in belongs_to:
|
||||
if key not in inventory[item]['children']:
|
||||
logger.debug("Adding %s to %s", key, item)
|
||||
append_if(array=inventory[item]['children'], item=key)
|
||||
|
||||
|
||||
@ -236,12 +249,16 @@ def _build_container_hosts(container_affinity, container_hosts, type_and_name,
|
||||
cuuid = '%s' % uuid.uuid4()
|
||||
cuuid = cuuid.split('-')[0]
|
||||
container_host_name = '%s-%s' % (type_and_name, cuuid)
|
||||
logger.debug("Generated container name %s",
|
||||
container_host_name)
|
||||
hostvars_options = hostvars[container_host_name] = {}
|
||||
if container_host_type not in inventory:
|
||||
inventory[container_host_type] = {
|
||||
"hosts": [],
|
||||
}
|
||||
|
||||
logger.debug("Adding container %s to %s",
|
||||
container_host_name, container_host_type)
|
||||
append_if(
|
||||
array=inventory[container_host_type]["hosts"],
|
||||
item=container_host_name
|
||||
@ -283,6 +300,8 @@ def _append_container_types(inventory, host_type):
|
||||
if 'container_name' in hdata:
|
||||
if hdata['container_name'].startswith(host_type):
|
||||
if 'physical_host' not in hdata:
|
||||
logger.debug("Set physical host for %s to %s",
|
||||
_host, host_type)
|
||||
hdata['physical_host'] = host_type
|
||||
|
||||
|
||||
@ -299,6 +318,7 @@ def _append_to_host_groups(inventory, container_type, assignment, host_type,
|
||||
"""
|
||||
physical_group_type = '%s_all' % container_type.split('_')[0]
|
||||
if physical_group_type not in inventory:
|
||||
logger.debug("Added %s group to inventory", physical_group_type)
|
||||
inventory[physical_group_type] = {'hosts': []}
|
||||
|
||||
iph = inventory[physical_group_type]['hosts']
|
||||
@ -321,15 +341,23 @@ def _append_to_host_groups(inventory, container_type, assignment, host_type,
|
||||
hdata['physical_host'] = host_type
|
||||
|
||||
if container.startswith('%s-' % type_and_name):
|
||||
logger.debug("Added host %s to %s hosts",
|
||||
container, assignment)
|
||||
append_if(array=iah, item=container)
|
||||
elif is_metal is True:
|
||||
if component == assignment:
|
||||
logger.debug("Added is_metal host %s to %s hosts",
|
||||
container, assignment)
|
||||
append_if(array=iah, item=container)
|
||||
|
||||
if container.startswith('%s-' % type_and_name):
|
||||
logger.debug("Added host %s to %s hosts",
|
||||
container, physical_group_type)
|
||||
append_if(array=iph, item=container)
|
||||
elif is_metal is True:
|
||||
if container.startswith(host_type):
|
||||
logger.debug("Added is_metal host %s to %s hosts",
|
||||
container, physical_group_type)
|
||||
append_if(array=iph, item=container)
|
||||
|
||||
# Append any options in config to the host_vars of a container
|
||||
@ -352,6 +380,7 @@ def _append_to_host_groups(inventory, container_type, assignment, host_type,
|
||||
)
|
||||
|
||||
if limit is None or (component and limit in component):
|
||||
logger.debug("Set options for %s", hname)
|
||||
hdata[_keys] = options
|
||||
|
||||
|
||||
@ -389,6 +418,7 @@ def _add_container_hosts(assignment, config, container_name, container_type,
|
||||
# This section will ensure that we are not it by the following bug:
|
||||
# https://bugzilla.mindrot.org/show_bug.cgi?id=2239
|
||||
type_and_name = '%s_%s' % (host_type, container_name)
|
||||
logger.debug("Generated container name %s", type_and_name)
|
||||
max_hostname_len = 52
|
||||
if len(type_and_name) > max_hostname_len:
|
||||
raise SystemExit(
|
||||
@ -409,6 +439,7 @@ def _add_container_hosts(assignment, config, container_name, container_type,
|
||||
physical_host['container_types'] = container_host_type
|
||||
|
||||
# Add all of the containers into the inventory
|
||||
logger.debug("Building containers for host %s", container_name)
|
||||
_build_container_hosts(
|
||||
container_affinity,
|
||||
container_hosts,
|
||||
@ -445,9 +476,11 @@ def user_defined_setup(config, inventory):
|
||||
for key, value in config.iteritems():
|
||||
if key.endswith('hosts'):
|
||||
if key not in inventory:
|
||||
logger.debug("Key %s was added to inventory", key)
|
||||
inventory[key] = {'hosts': []}
|
||||
|
||||
if value is None:
|
||||
logger.debug("Key %s had no value", key)
|
||||
return
|
||||
|
||||
for _key, _value in value.iteritems():
|
||||
@ -461,6 +494,7 @@ def user_defined_setup(config, inventory):
|
||||
'is_metal': True,
|
||||
'physical_host_group': key
|
||||
})
|
||||
logger.debug("Hostvars info updated for %s", key)
|
||||
|
||||
# If the entry is missing the properties key add it.
|
||||
properties = hvs[_key].get('properties')
|
||||
@ -474,6 +508,8 @@ def user_defined_setup(config, inventory):
|
||||
hvs[_key][_k] = _v
|
||||
|
||||
USED_IPS.add(_value['ip'])
|
||||
logger.debug("Attempting to add host %s to group %s",
|
||||
_key, key)
|
||||
append_if(array=inventory[key]['hosts'], item=_key)
|
||||
|
||||
|
||||
@ -488,6 +524,7 @@ def skel_setup(environment, inventory):
|
||||
continue
|
||||
for _key, _value in value.iteritems():
|
||||
if _key not in inventory:
|
||||
logger.debug("Key %s added to inventory", _key)
|
||||
inventory[_key] = {}
|
||||
if _key.endswith('container'):
|
||||
if 'hosts' not in inventory[_key]:
|
||||
@ -495,12 +532,15 @@ def skel_setup(environment, inventory):
|
||||
else:
|
||||
if 'children' not in inventory[_key]:
|
||||
inventory[_key]['children'] = []
|
||||
# TODO(nrb): This line is duplicated above;
|
||||
# is that necessary?
|
||||
if 'hosts' not in inventory[_key]:
|
||||
inventory[_key]['hosts'] = []
|
||||
|
||||
if 'belongs_to' in _value:
|
||||
for assignment in _value['belongs_to']:
|
||||
if assignment not in inventory:
|
||||
logger.debug("Created group %s", assignment)
|
||||
inventory[assignment] = {}
|
||||
if 'children' not in inventory[assignment]:
|
||||
inventory[assignment]['children'] = []
|
||||
@ -702,7 +742,9 @@ def container_skel_load(container_skel, inventory, config):
|
||||
:param inventory: ``dict`` Living dictionary of inventory
|
||||
:param config: ``dict`` User defined information
|
||||
"""
|
||||
logger.debug("Loading container skeleton")
|
||||
if 'lxc_hosts' not in inventory.keys():
|
||||
logger.debug("Created lxc_hosts group.")
|
||||
inventory['lxc_hosts'] = {'hosts': []}
|
||||
for key, value in container_skel.iteritems():
|
||||
contains_in = value.get('contains', False)
|
||||
@ -809,6 +851,7 @@ def _set_used_ips(user_defined_config, inventory):
|
||||
)
|
||||
USED_IPS.update([str(i) for i in ip_range])
|
||||
else:
|
||||
logger.debug("IP %s set as used", split_ip[0])
|
||||
USED_IPS.add(split_ip[0])
|
||||
|
||||
# Find all used IP addresses and ensure that they are not used again
|
||||
@ -817,6 +860,7 @@ def _set_used_ips(user_defined_config, inventory):
|
||||
for network_entry in networks.values():
|
||||
address = network_entry.get('address')
|
||||
if address:
|
||||
logger.debug("IP %s set as used", address)
|
||||
USED_IPS.add(address)
|
||||
|
||||
|
||||
@ -850,6 +894,8 @@ def _ensure_inventory_uptodate(inventory, container_skel):
|
||||
for host in hosts:
|
||||
container = host_vars[host]
|
||||
if 'properties' in type_vars:
|
||||
logger.debug("Copied propeties for %s from skeleton",
|
||||
container)
|
||||
container['properties'] = type_vars['properties']
|
||||
|
||||
|
||||
@ -874,10 +920,12 @@ def _parse_global_variables(user_cidr, inventory, user_defined_config):
|
||||
inventory['all']['vars'].update(
|
||||
user_defined_config['global_overrides']
|
||||
)
|
||||
logger.debug("Applied global_overrides")
|
||||
|
||||
# Remove global overrides that were deleted from inventory, too
|
||||
for key in inventory['all']['vars'].keys():
|
||||
if key not in user_defined_config['global_overrides']:
|
||||
logger.debug("Deleting key %s from inventory", key)
|
||||
del inventory['all']['vars'][key]
|
||||
|
||||
|
||||
@ -890,6 +938,8 @@ def append_if(array, item):
|
||||
"""
|
||||
if item not in array:
|
||||
array.append(item)
|
||||
# TODO(nrb): Would be nice to change this to return true/false
|
||||
# for logging purposes.
|
||||
return array
|
||||
|
||||
|
||||
@ -926,6 +976,7 @@ def _extra_config(user_defined_config, base_dir):
|
||||
user_defined_config,
|
||||
yaml.safe_load(f.read()) or {}
|
||||
)
|
||||
logger.debug("Merged overrides from file %s", name)
|
||||
|
||||
|
||||
def _check_same_ip_to_multiple_host(config):
|
||||
@ -946,6 +997,7 @@ def _check_same_ip_to_multiple_host(config):
|
||||
if ips_to_hostnames_mapping[ip] != hostname:
|
||||
info = (ip, ips_to_hostnames_mapping[ip], hostname)
|
||||
raise MultipleHostsWithOneIPError(*info)
|
||||
logger.debug("No hosts with duplicated IPs found")
|
||||
|
||||
|
||||
def _check_multiple_ips_to_host(config):
|
||||
@ -967,12 +1019,14 @@ def _check_multiple_ips_to_host(config):
|
||||
if not current_ip == new_ip:
|
||||
raise MultipleIpForHostError(hostname, current_ip,
|
||||
new_ip)
|
||||
logger.debug("No hosts with multiple IPs found.")
|
||||
return True
|
||||
|
||||
|
||||
def _check_lxc_hosts(config):
|
||||
if 'lxc_hosts' in config.keys():
|
||||
raise LxcHostsDefined()
|
||||
logger.debug("lxc_hosts group not defined")
|
||||
|
||||
|
||||
def _check_config_settings(cidr_networks, config, container_skel):
|
||||
@ -1022,6 +1076,8 @@ def _check_config_settings(cidr_networks, config, container_skel):
|
||||
not p_net.get('is_container_address')):
|
||||
raise ProviderNetworkMisconfiguration(q_name)
|
||||
|
||||
logger.debug("Provider network information OK")
|
||||
|
||||
# look for same ip address assigned to different hosts
|
||||
_check_same_ip_to_multiple_host(config)
|
||||
|
||||
@ -1042,7 +1098,7 @@ def load_environment(config_path, environment):
|
||||
|
||||
if os.path.isdir(env_plugins):
|
||||
_extra_config(user_defined_config=environment, base_dir=env_plugins)
|
||||
|
||||
logger.debug("Loaded environment from %s", config_path)
|
||||
return environment
|
||||
|
||||
|
||||
@ -1072,6 +1128,7 @@ def load_user_configuration(config_path):
|
||||
'No openstack_user_config files are available in either \n%s'
|
||||
'\nor \n%s/conf.d directory' % (config_path, config_path)
|
||||
)
|
||||
logger.debug("User configuration loaded from: %s", user_config_file)
|
||||
return user_defined_config
|
||||
|
||||
|
||||
@ -1085,6 +1142,7 @@ def make_backup(config_path, inventory_file_path):
|
||||
basename = os.path.basename(inventory_file_path)
|
||||
backup_name = get_backup_name(basename)
|
||||
tar.add(inventory_file_path, arcname=backup_name)
|
||||
logger.debug("Backup written to %s", inventory_backup_file)
|
||||
|
||||
|
||||
def get_backup_name(basename):
|
||||
@ -1097,16 +1155,25 @@ def get_inventory(config_path, inventory_file_path):
|
||||
if os.path.isfile(inventory_file_path):
|
||||
with open(inventory_file_path, 'rb') as f:
|
||||
dynamic_inventory = json.loads(f.read())
|
||||
logger.debug("Loaded existing inventory from %s",
|
||||
inventory_file_path)
|
||||
|
||||
make_backup(config_path, inventory_file_path)
|
||||
else:
|
||||
dynamic_inventory = copy.deepcopy(INVENTORY_SKEL)
|
||||
logger.debug("No existing inventory, created fresh skeleton.")
|
||||
|
||||
return dynamic_inventory
|
||||
|
||||
|
||||
def main(all_args):
|
||||
"""Run the main application."""
|
||||
if all_args.get('debug'):
|
||||
log_fmt = "%(lineno)d - %(funcName)s: %(message)s"
|
||||
logging.basicConfig(format=log_fmt, filename='inventory.log')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.info("Beginning new inventory run")
|
||||
|
||||
# Get the path to the user configuration files
|
||||
config_path = find_config_path(
|
||||
user_config_path=all_args.get('config')
|
||||
@ -1151,10 +1218,12 @@ def main(all_args):
|
||||
_set_used_ips(user_defined_config, dynamic_inventory)
|
||||
user_defined_setup(user_defined_config, dynamic_inventory)
|
||||
skel_setup(environment, dynamic_inventory)
|
||||
logger.debug("Loading physical skel.")
|
||||
skel_load(
|
||||
environment.get('physical_skel'),
|
||||
dynamic_inventory
|
||||
)
|
||||
logger.debug("Loading component skel")
|
||||
skel_load(
|
||||
environment.get('component_skel'),
|
||||
dynamic_inventory
|
||||
@ -1202,9 +1271,14 @@ def main(all_args):
|
||||
)
|
||||
)
|
||||
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
num_hosts = len(dynamic_inventory['_meta']['hostvars'])
|
||||
logger.debug("%d hosts found." % num_hosts)
|
||||
|
||||
# Save new dynamic inventory
|
||||
with open(dynamic_inventory_file, 'wb') as f:
|
||||
f.write(dynamic_inventory_json)
|
||||
logger.info("Inventory written")
|
||||
|
||||
return dynamic_inventory_json
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- A new debug flag has been added to ``dynamic_inventory.py``. This
|
||||
should make it easier to understand what's happening with the inventory
|
||||
script, and provide a way to gather output for more detailed bug reports.
|
||||
See the developer docs for more details.
|
@ -39,10 +39,13 @@ def cleanup():
|
||||
os.remove(f_file)
|
||||
|
||||
|
||||
def get_inventory(clean=True):
|
||||
def get_inventory(clean=True, extra_args=None):
|
||||
"Return the inventory mapping in a dict."
|
||||
args = {'config': TARGET_DIR}
|
||||
if extra_args:
|
||||
args.update(extra_args)
|
||||
try:
|
||||
inventory_string = di.main({'config': TARGET_DIR})
|
||||
inventory_string = di.main(args)
|
||||
inventory = json.loads(inventory_string)
|
||||
return inventory
|
||||
finally:
|
||||
@ -1064,6 +1067,31 @@ class TestNetworkEntry(unittest.TestCase):
|
||||
self.assertEqual(entry['interface'], 'eth1')
|
||||
|
||||
|
||||
class TestDebugLogging(unittest.TestCase):
|
||||
@mock.patch('dynamic_inventory.logging')
|
||||
@mock.patch('dynamic_inventory.logger')
|
||||
def test_logging_enabled(self, mock_logger, mock_logging):
|
||||
# Shadow the real value so tests don't complain about it
|
||||
mock_logging.DEBUG = 10
|
||||
|
||||
get_inventory(extra_args={"debug": True})
|
||||
|
||||
self.assertTrue(mock_logging.basicConfig.called)
|
||||
self.assertTrue(mock_logger.info.called)
|
||||
self.assertTrue(mock_logger.debug.called)
|
||||
|
||||
@mock.patch('dynamic_inventory.logging')
|
||||
@mock.patch('dynamic_inventory.logger')
|
||||
def test_logging_disabled(self, mock_logger, mock_logging):
|
||||
get_inventory(extra_args={"debug": False})
|
||||
|
||||
self.assertFalse(mock_logging.basicConfig.called)
|
||||
# Even though logging is disabled, we still call these
|
||||
# all over the place; they just choose not to do anything.
|
||||
self.assertTrue(mock_logger.info.called)
|
||||
self.assertTrue(mock_logger.debug.called)
|
||||
|
||||
|
||||
class TestLxcHosts(TestConfigCheckBase):
|
||||
|
||||
def test_lxc_hosts_group_present(self):
|
||||
|
Loading…
Reference in New Issue
Block a user