Browse Source

Generate nodepool logging configuration

Generate a logging configuration template for nodepool that splits
image-build logs out into separate files.

A tool to generate and maintain this file based on the nodepool
configuration is added.

The logging configuration template this tool creates is added
alongside the main configuration.  This is passed to the deployment
puppet module via a parameter.

Change-Id: I4723c68e5c83b536560e0a8bd7b73e312b46fc02
Depends-on: Iec77fb62c353d09186140b23912caf02225a643b
changes/36/164036/3
Ian Wienand 4 years ago
parent
commit
3bafd2c691
3 changed files with 1392 additions and 0 deletions
  1. +2
    -0
      modules/openstack_project/manifests/nodepool_prod.pp
  2. +1146
    -0
      modules/openstack_project/templates/nodepool/nodepool.logging.conf.erb
  3. +244
    -0
      tools/nodepool_log_config.py

+ 2
- 0
modules/openstack_project/manifests/nodepool_prod.pp View File

@@ -5,6 +5,7 @@ class openstack_project::nodepool_prod(
$mysql_password,
$nodepool_ssh_private_key = '',
$nodepool_template = 'openstack_project/nodepool/nodepool.yaml.erb',
$nodepool_logging_template = 'openstack_project/nodepool/nodepool.logging.conf.erb',
$vhost_name = 'nodepool.openstack.org',
$sysadmins = [],
$statsd_host = '',
@@ -44,6 +45,7 @@ class openstack_project::nodepool_prod(
scripts_dir => $::project_config::nodepool_scripts_dir,
elements_dir => $::project_config::nodepool_elements_dir,
require => $::project_config::config_dir,
logging_conf_template => $nodepool_logging_template,
}

file { '/etc/nodepool/nodepool.yaml':

+ 1146
- 0
modules/openstack_project/templates/nodepool/nodepool.logging.conf.erb
File diff suppressed because it is too large
View File


+ 244
- 0
tools/nodepool_log_config.py View File

@@ -0,0 +1,244 @@
#!/usr/bin/env python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


"""Generate a sample logging configuration file

use log_config_generator.generate_log_config() to generate a sample
logging configuration file.

The sample splits up the log output for general logging and image
builds and applys some sensible rotation defaults.
"""

import argparse
import logging
import yaml

# default paths and outputs
MODULES_PATH = '../modules/openstack_project/templates/nodepool'
CONFIG_FILE = MODULES_PATH + '/nodepool.yaml.erb'
LOGGING_CONFIG_FILE = MODULES_PATH + '/nodepool.logging.conf.erb'
LOG_DIR = '/var/log/nodepool'
IMAGE_LOG_DIR = '<%= @image_log_document_root %>'


_BASIC_FILE = """

#
# THIS FILE HAS BEEN AUTOGENERATED
# Regenerate it with tools/nodepool_log_config.py
#

[loggers]
keys=root,nodepool,requests,image,%(logger_titles)s

[handlers]
keys=console,debug,normal,image,%(handler_titles)s

[formatters]
keys=simple

[logger_root]
level=WARNING
handlers=console

[logger_requests]
level=WARNING
handlers=debug,normal
qualname=requests

[logger_nodepool]
level=DEBUG
handlers=debug,normal
qualname=nodepool

[logger_image]
level=INFO
handlers=image
qualname=nodepool.image.build
propagate=0

[handler_console]
level=WARNING
class=StreamHandler
formatter=simple
args=(sys.stdout,)

[handler_debug]
level=DEBUG
class=logging.handlers.TimedRotatingFileHandler
formatter=simple
args=('%(log_dir)s/debug.log', 'H', 8, 30,)

[handler_normal]
level=INFO
class=logging.handlers.TimedRotatingFileHandler
formatter=simple
args=('%(log_dir)s/nodepool.log', 'H', 8, 30,)

[handler_image]
level=INFO
class=logging.handlers.TimedRotatingFileHandler
formatter=simple
args=('%(image_log_dir)s/image.log', 'H', 8, 30,)

[formatter_simple]
format=%%(asctime)s %%(levelname)s %%(name)s: %%(message)s
datefmt=

# ==== individual image loggers ====

%(image_loggers_and_handlers)s"""

_IMAGE_HANDLER = """
[handler_%(title)s]
level=INFO
class=logging.handlers.TimedRotatingFileHandler
formatter=simple
args=('%(image_log_dir)s/%(filename)s', 'H', 8, 30,)
"""

_IMAGE_LOGGER = """
[logger_%(title)s]
level=INFO
handlers=%(handler)s
qualname=nodepool.image.build.%(qualname)s
propagate=0
"""


def _get_providers_and_images(config_file):
ret = []
config = yaml.load(config_file)
for provider in config['providers']:
for image in provider['images']:
ret.append((provider['name'], image['name']))
logging.debug("Added %d providers & images" % len(ret))
return ret


def _generate_logger_and_handler(image_log_dir, provider, image):
handler = _IMAGE_HANDLER % {
'image_log_dir': image_log_dir,
'title': '%s_%s' % (provider, image),
'filename': '%s.%s.log' % (provider, image),
}
logger = _IMAGE_LOGGER % {
'title': '%s_%s' % (provider, image),
'handler': '%s_%s' % (provider, image),
'qualname': '%s.%s' % (provider, image),
}

return {
'handler_title': '%s_%s' % (provider, image),
'logger_title': '%s_%s' % (provider, image),
'handler': handler,
'logger': logger,
}


def generate_log_config(config, log_dir, image_log_dir, output):

"""Generate a sample logging file

The logging output will have the correct formatters and handlers
to split all image-build logs out into separate files grouped by
provider. e.g.

providers:
- name: foo
...
- images:
- name: image1
...
- name: image2
...
- name: moo
...
- images:
- name: image1
...
- name: image2
...

Will result in log files (in `image_log_dir`) of foo.image1.log,
foo.image2.log, moo.image1.log, moo.image2.log

:param config: input config file
:param log_dir: directory for main log file
:param image_log_dir: directory for image build logs
:param output: open file handle to output sample configuration to

"""

loggers_and_handlers = []
logging.debug("Reading config file %s" % config.name)
for (provider, image) in _get_providers_and_images(config):
loggers_and_handlers.append(
_generate_logger_and_handler(image_log_dir, provider, image))

logger_titles = []
handler_titles = []
image_loggers_and_handlers = ""
for item in loggers_and_handlers:
logger_titles.append(item['logger_title'])
handler_titles.append(item['handler_title'])
image_loggers_and_handlers += item['logger'] + item['handler']

final_output = _BASIC_FILE % {
'log_dir': log_dir,
'image_log_dir': image_log_dir,
'logger_titles': ','.join(logger_titles),
'handler_titles': ','.join(handler_titles),
'image_loggers_and_handlers': image_loggers_and_handlers,
}

logging.debug("Writing output to %s" % output.name)
output.write(final_output)
output.flush()
logging.debug("Done!")


def main():

parser = argparse.ArgumentParser()
parser.add_argument('-d', '--debug', action='store_true',
help="Enable debugging")
parser.add_argument('-c', '--config', default=CONFIG_FILE,
help="Config file to read in "
"(default: %s)" % CONFIG_FILE,
type=argparse.FileType('r'))
parser.add_argument('-o', '--output', default=LOGGING_CONFIG_FILE,
help="Output file "
"(default: %s)" % LOGGING_CONFIG_FILE,
type=argparse.FileType('w'))
parser.add_argument('-l', '--log-dir', default=LOG_DIR,
help="Output directory for logs "
"(default: %s)" % LOG_DIR)
parser.add_argument('-i', '--image-log-dir', default=IMAGE_LOG_DIR,
help="Output directory for image logs "
"(default: %s)" % IMAGE_LOG_DIR)
args = parser.parse_args()

logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)

generate_log_config(args.config,
args.log_dir,
args.image_log_dir,
args.output)


if __name__ == "__main__":
main()

Loading…
Cancel
Save