9d2a6dae35
In python2 opens files as ascii by default, we were getting errors when trying to write out unicode to log files. This change pulls in the codecs module for python2 to support writing unicode out to files. In python3, all strings are unicode so there is no issues when writing them out to a file. Change-Id: Id740253a0e6143cfcdd4f7fe2b5460d9f64fa01e Closes-Bug: #1665114
140 lines
4.7 KiB
Python
140 lines
4.7 KiB
Python
# Copyright 2015 Red Hat, Inc.
|
|
#
|
|
# 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.
|
|
#
|
|
|
|
|
|
import abc
|
|
import logging
|
|
import os
|
|
import six
|
|
import subprocess
|
|
import sys
|
|
|
|
from tripleo_common.image.exception import ImageBuilderException
|
|
|
|
if sys.version_info[0] < 3:
|
|
import codecs
|
|
_open = open
|
|
open = codecs.open
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class ImageBuilder(object):
|
|
"""Base representation of an image building method"""
|
|
|
|
@staticmethod
|
|
def get_builder(builder):
|
|
if builder == 'dib':
|
|
return DibImageBuilder()
|
|
raise ImageBuilderException('Unknown image builder type')
|
|
|
|
@abc.abstractmethod
|
|
def build_image(self, image_path, image_type, node_dist, arch, elements,
|
|
options, packages, extra_options={}):
|
|
"""Build a disk image"""
|
|
pass
|
|
|
|
|
|
class DibImageBuilder(ImageBuilder):
|
|
"""Build images using diskimage-builder"""
|
|
|
|
logger = logging.getLogger(__name__ + '.DibImageBuilder')
|
|
handler = logging.StreamHandler(sys.stdout)
|
|
|
|
# NOTE(bnemec): This may not play nicely with callers other than the
|
|
# openstackclient. However, since at this time there are no such other
|
|
# callers we can deal with that if/when it happens.
|
|
def _configure_logging(self):
|
|
"""Ensure our info level log output gets seen
|
|
|
|
The default openstackclient logging level is warning, which means
|
|
our info messages for the image build are not visible to the user.
|
|
By adding our own local handler we can ensure that the messages get
|
|
logged in a visible way.
|
|
|
|
To avoid duplicate log messages, we need to not propagate them to
|
|
parent loggers. Otherwise we end up with both our handler and the
|
|
parent handler logging warning and above messages.
|
|
"""
|
|
if not self.logger.handlers:
|
|
self.logger.addHandler(self.handler)
|
|
self.logger.propagate = False
|
|
|
|
def build_image(self, image_path, image_type, node_dist, arch, elements,
|
|
options, packages, extra_options={}):
|
|
self._configure_logging()
|
|
env = os.environ.copy()
|
|
|
|
elements_path = env.get('ELEMENTS_PATH')
|
|
if elements_path is None:
|
|
env['ELEMENTS_PATH'] = os.pathsep.join([
|
|
"/usr/share/tripleo-puppet-elements",
|
|
"/usr/share/instack-undercloud",
|
|
'/usr/share/tripleo-image-elements',
|
|
])
|
|
os.environ.update(env)
|
|
|
|
cmd = ['disk-image-create', '-a', arch, '-o', image_path,
|
|
'-t', image_type]
|
|
|
|
if packages:
|
|
cmd.append('-p')
|
|
cmd.append(','.join(packages))
|
|
|
|
if options:
|
|
for option in options:
|
|
cmd.extend(option.split(' '))
|
|
|
|
skip_base = extra_options.get('skip_base', False)
|
|
if skip_base:
|
|
cmd.append('-n')
|
|
|
|
docker_target = extra_options.get('docker_target')
|
|
if docker_target:
|
|
cmd.append('--docker-target')
|
|
cmd.append(docker_target)
|
|
|
|
environment = extra_options.get('environment')
|
|
if environment:
|
|
os.environ.update(environment)
|
|
|
|
if node_dist:
|
|
cmd.append(node_dist)
|
|
|
|
cmd.extend(elements)
|
|
|
|
log_file = '%s.log' % image_path
|
|
|
|
self.logger.info('Running %s' % cmd)
|
|
self.logger.info('Logging output to %s' % log_file)
|
|
process = subprocess.Popen(cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT)
|
|
with open(log_file, 'w', encoding='utf-8') as f:
|
|
while True:
|
|
line = process.stdout.readline()
|
|
try:
|
|
line = line.decode('utf-8')
|
|
except AttributeError:
|
|
# In Python 3 there is no decode method, but we don't need
|
|
# to decode because strings are always unicode.
|
|
pass
|
|
if line:
|
|
self.logger.info(line.rstrip())
|
|
f.write(line)
|
|
if line == '' and process.poll() is not None:
|
|
break
|
|
if process.returncode != 0:
|
|
raise subprocess.CalledProcessError(process.returncode, cmd)
|