Files
python-tripleoclient/rdomanager_oscplugin/v1/overcloud_image.py
John Trowbridge 6562c04ffd Add ntp element to overcloud-full images
The RDO promote job is failing to deploy because at some
point we started expecting for ntpd to be installed in
the overcloud images.

Error: Could not start Service[ntp]: Execution of
'/usr/bin/systemctl start ntpd' returned 6: Failed to issue
method call: Unit ntpd.service failed to load: No such file
or directory.
Wrapped exception:
Execution of '/usr/bin/systemctl start ntpd' returned 6:
Failed to issue method call: Unit ntpd.service failed to
load: No such file or directory.

Change-Id: If5020f20bfb2f2750837e831d7f22843f2501736
2015-07-17 09:43:38 -04:00

555 lines
21 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 logging
import os
import re
import requests
import shutil
import stat
import subprocess
from cliff import command
from openstackclient.common import exceptions
from openstackclient.common import utils
class BuildOvercloudImage(command.Command):
"""Build images for the overcloud"""
auth_required = False
log = logging.getLogger(__name__ + ".BuildOvercloudImage")
TRIPLEOPUPPETELEMENTS = "/usr/share/tripleo-puppet-elements"
INSTACKUNDERCLOUDELEMENTS = "/usr/share/instack-undercloud"
PUPPET_COMMON_ELEMENTS = [
'sysctl',
'hosts',
'baremetal',
'dhcp-all-interfaces',
'os-collect-config',
'heat-config-puppet',
'heat-config-script',
'puppet-modules',
'hiera',
'os-net-config',
'stable-interface-names',
'grub2-deprecated',
'-p python-psutil,python-debtcollector,plotnetcfg,sos',
]
OVERCLOUD_FULL_DIB_EXTRA_ARGS = [
'overcloud-full',
'overcloud-controller',
'overcloud-compute',
'overcloud-ceph-storage',
'ntp',
] + PUPPET_COMMON_ELEMENTS
DISCOVERY_IMAGE_ELEMENT = [
'ironic-discoverd-ramdisk-instack',
'delorean-rdo-management',
]
DEPLOY_IMAGE_ELEMENT = [
'deploy-ironic'
]
IMAGE_TYPES = [
'deploy-ramdisk',
'discovery-ramdisk',
'fedora-user',
'overcloud-full',
]
def get_parser(self, prog_name):
parser = super(BuildOvercloudImage, self).get_parser(prog_name)
image_group = parser.add_mutually_exclusive_group(required=True)
image_group.add_argument(
"--all",
dest="all",
action="store_true",
help="Build all images",
)
image_group.add_argument(
"--type",
dest="image_types",
metavar='<image type>',
choices=self.IMAGE_TYPES,
action="append",
help="Build image by name. One of "
"%s" % ", ".join(self.IMAGE_TYPES),
)
parser.add_argument(
"--base-image",
help="Image file to use as a base for new images",
)
parser.add_argument(
"--instack-undercloud-elements",
dest="instack_undercloud_elements",
default=os.environ.get(
"INSTACKUNDERCLOUDELEMENTS", self.INSTACKUNDERCLOUDELEMENTS),
help="Path to Instack Undercloud elements",
)
parser.add_argument(
"--tripleo-puppet-elements",
dest="tripleo_puppet_elements",
default=os.environ.get(
"TRIPLEOPUPPETELEMENTS", self.TRIPLEOPUPPETELEMENTS),
help="Path to TripleO Puppet elements",
)
parser.add_argument(
"--elements-path",
dest="elements_path",
default=os.environ.get(
"ELEMENTS_PATH",
os.pathsep.join([
self.TRIPLEOPUPPETELEMENTS,
self.INSTACKUNDERCLOUDELEMENTS,
'/usr/share/tripleo-image-elements',
'/usr/share/diskimage-builder/elements',
'/usr/share/openstack-heat-templates/'
'software-config/elements',
])),
help="Full elements path, separated by %s" % os.pathsep,
)
parser.add_argument(
"--tmp-dir",
dest="tmp_dir",
default=os.environ.get("TMP_DIR", "/var/tmp"),
help="Path to a temporary directory for creating images",
)
parser.add_argument(
"--node-arch",
dest="node_arch",
default=os.environ.get("NODE_ARCH", "amd64"),
help="Architecture of image to build",
)
parser.add_argument(
"--node-dist",
dest="node_dist",
default=os.environ.get("NODE_DIST", ""),
help="Distribution of image to build",
)
parser.add_argument(
"--registration-method",
dest="reg_method",
default=os.environ.get("REG_METHOD", "disable"),
help="Registration method",
)
parser.add_argument(
"--run-rhos-release",
dest="run_rhos_release",
action='store_true',
default=(os.environ.get('RUN_RHOS_RELEASE', '0') == '1'),
help="Use RHOS release for repo management (debug only)"
)
parser.add_argument(
"--use-delorean-trunk",
dest="use_delorean_trunk",
action='store_true',
default=(os.environ.get('USE_DELOREAN_TRUNK', '0') == '1'),
help="Use Delorean trunk repo",
)
parser.add_argument(
"--delorean-trunk-repo",
dest="delorean_trunk_repo",
default=os.environ.get(
'DELOREAN_TRUNK_REPO',
'http://trunk.rdoproject.org/kilo/centos7/latest-RDO-kilo-CI/'
),
help="URL to Delorean trunk repo",
)
parser.add_argument(
"--delorean-repo-file",
dest="delorean_repo_file",
default=os.environ.get('DELOREAN_REPO_FILE', 'delorean-kilo.repo'),
help="Filename for delorean repo config file",
)
parser.add_argument(
"--overcloud-full-dib-extra-args",
dest="overcloud_full_dib_extra_args",
default=os.environ.get(
"OVERCLOUD_FULL_DIB_EXTRA_ARGS",
" ".join(self.OVERCLOUD_FULL_DIB_EXTRA_ARGS)),
help="Extra args for Overcloud Full",
)
parser.add_argument(
"--overcloud-full-name",
dest="overcloud_full_name",
default=os.environ.get('OVERCLOUD_FULL_NAME', 'overcloud-full'),
help="Name of overcloud full image",
)
parser.add_argument(
"--fedora-user-name",
dest="fedora_user_name",
default=os.environ.get('FEDORA_USER_NAME', 'fedora-user'),
help="Name of Fedora user image",
)
parser.add_argument(
"--deploy-name",
dest="deploy_name",
default=os.environ.get('DEPLOY_NAME', 'deploy-ramdisk-ironic'),
help="Name of deployment ramdisk image",
)
parser.add_argument(
"--discovery-name",
dest="discovery_name",
default=os.environ.get('DISCOVERY_NAME', 'discovery-ramdisk'),
help="Name of discovery ramdisk image",
)
parser.add_argument(
"--deploy-image-element",
dest="deploy_image_element",
default=os.environ.get(
'DEPLOY_IMAGE_ELEMENT',
" ".join(self.DEPLOY_IMAGE_ELEMENT)),
help="DIB elements for deploy image",
)
parser.add_argument(
"--discovery-image-element",
dest="discovery_image_element",
default=os.environ.get(
'DISCOVERY_IMAGE_ELEMENT',
" ".join(self.DISCOVERY_IMAGE_ELEMENT)),
help="DIB elements for discovery image",
)
return parser
def _disk_image_create(self, args):
subprocess.call('disk-image-create {0}'.format(args), shell=True)
def _ramdisk_image_create(self, args):
subprocess.call('ramdisk-image-create {0}'.format(args), shell=True)
def _env_var_or_set(self, key_name, default_value):
os.environ[key_name] = os.environ.get(key_name, default_value)
def _prepare_env_variables(self, parsed_args):
self._env_var_or_set('ELEMENTS_PATH', parsed_args.elements_path)
self._env_var_or_set('TMP_DIR', parsed_args.tmp_dir)
self._env_var_or_set('DIB_DEFAULT_INSTALLTYPE', 'package')
self._env_var_or_set(
'DELOREAN_TRUNK_REPO', parsed_args.delorean_trunk_repo)
self._env_var_or_set(
'DELOREAN_REPO_FILE', parsed_args.delorean_repo_file)
# Needed for corosync to be able to use hostnames
# https://bugs.launchpad.net/tripleo/+bug/1447497
os.environ['DIB_CLOUD_INIT_ETC_HOSTS'] = ''
# Attempt to detect host distribution if not specified
if not parsed_args.node_dist:
with open('/etc/redhat-release', 'r') as f:
release = f.readline()
if re.match('Red Hat Enterprise Linux', release):
parsed_args.node_dist = 'rhel7'
elif re.match('CentOS', release):
parsed_args.node_dist = 'centos7'
elif re.match('Fedora', release):
parsed_args.node_dist = 'fedora'
else:
raise Exception(
"Could not detect distribution from "
"/etc/redhat-release!")
dib_common_elements = []
if re.match('rhel7', parsed_args.node_dist):
os.environ['REG_METHOD'] = parsed_args.reg_method
if parsed_args.run_rhos_release:
self._env_var_or_set('RHOS_RELEASE', '6')
dib_common_elements.append('rhos-release')
os.environ['DELOREAN_REPO_URL'] = parsed_args.delorean_trunk_repo
elif re.match('centos7', parsed_args.node_dist):
os.environ['DELOREAN_REPO_URL'] = parsed_args.delorean_trunk_repo
dib_common_elements.extend([
'selinux-permissive',
'centos-cloud-repo',
])
parsed_args.discovery_image_element = " ".join([
'delorean-rdo-management',
'ironic-discoverd-ramdisk-instack',
'centos-cr',
])
dib_common_elements.extend([
'element-manifest',
'network-gateway',
])
self._env_var_or_set('RHOS', '0')
self._env_var_or_set('RHOS_RELEASE', '0')
if parsed_args.node_dist in ['rhel7', 'centos7']:
self._env_var_or_set('FS_TYPE', 'xfs')
if os.environ.get('RHOS') == '0':
os.environ['RDO_RELEASE'] = 'kilo'
dib_common_elements.extend([
'epel',
'rdo-release',
])
elif not os.environ.get('RHOS_RELEASE') == '0':
dib_common_elements.append('rhos-release')
self._env_var_or_set('PACKAGES', '1')
if os.environ.get('PACKAGES') == '1':
dib_common_elements.extend([
'undercloud-package-install',
'pip-and-virtualenv-override',
])
if parsed_args.use_delorean_trunk:
dib_common_elements.append('delorean-repo')
parsed_args.dib_common_elements = " ".join(dib_common_elements)
def _build_image_ramdisk(self, parsed_args, ramdisk_type):
image_name = vars(parsed_args)["%s_name" % ramdisk_type]
if (not os.path.isfile("%s.initramfs" % image_name) or
not os.path.isfile("%s.kernel" % image_name)):
args = ("-a %(arch)s -o %(name)s "
"--ramdisk-element dracut-ramdisk %(node_dist)s "
"%(image_element)s %(dib_common_elements)s "
"2>&1 | tee dib-%(ramdisk_type)s.log" %
{
'arch': parsed_args.node_arch,
'name': image_name,
'node_dist': parsed_args.node_dist,
'image_element':
vars(parsed_args)["%s_image_element" %
ramdisk_type],
'dib_common_elements':
parsed_args.dib_common_elements,
'ramdisk_type': ramdisk_type,
})
self._ramdisk_image_create(args)
def _build_image_ramdisks(self, parsed_args):
self._build_image_ramdisk_deploy(parsed_args)
self._build_image_ramdisk_discovery(parsed_args)
def _build_image_ramdisk_deploy(self, parsed_args):
self._build_image_ramdisk(parsed_args, 'deploy')
def _build_image_ramdisk_discovery(self, parsed_args):
self._build_image_ramdisk(parsed_args, 'discovery')
def _build_image_overcloud(self, parsed_args, node_type):
image_name = "%s.qcow2" % vars(parsed_args)['overcloud_%s_name' %
node_type]
if not os.path.isfile(image_name):
args = ("-a %(arch)s -o %(name)s "
"%(node_dist)s %(overcloud_dib_extra_args)s "
"%(dib_common_elements)s 2>&1 | "
"tee dib-overcloud-%(image_type)s.log" %
{
'arch': parsed_args.node_arch,
'name': image_name,
'node_dist': parsed_args.node_dist,
'overcloud_dib_extra_args':
vars(parsed_args)["overcloud_%s_dib_extra_args" %
node_type],
'dib_common_elements':
parsed_args.dib_common_elements,
'image_type': node_type,
})
self._disk_image_create(args)
def _build_image_overcloud_full(self, parsed_args):
self._build_image_overcloud(parsed_args, 'full')
def _build_image_fedora_user(self, parsed_args):
image_name = "%s.qcow2" % parsed_args.fedora_user_name
if not os.path.isfile(image_name):
if os.path.isfile('~/.cache/image-create/fedora-21.x86_64.qcow2'):
# Just copy the already downloaded Fedora cloud image as
# fedora-user.qcow2
shutil.copy2(
'~/.cache/image-create/fedora-21.x86_64.qcow2',
image_name)
else:
# Download the image
r = requests.get(
'http://cloud.fedoraproject.org/fedora-21.x86_64.qcow2')
with open(image_name, 'wb') as f:
f.write(r.content)
# The perms always seem to be wrong when copying out of the cache,
# so fix them
os.chmod(
image_name,
stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
self._prepare_env_variables(parsed_args)
self.log.debug("Environment: %s" % os.environ)
if parsed_args.all:
self._build_image_ramdisks(parsed_args)
self._build_image_overcloud_full(parsed_args)
self._build_image_fedora_user(parsed_args)
else:
for image_type in parsed_args.image_types:
{
'deploy-ramdisk': self._build_image_ramdisk_deploy,
'discovery-ramdisk': self._build_image_ramdisk_discovery,
'fedora-user': self._build_image_fedora_user,
'overcloud-full': self._build_image_overcloud_full,
}[image_type](parsed_args)
class UploadOvercloudImage(command.Command):
"""Create overcloud glance images from existing image files."""
auth_required = False
log = logging.getLogger(__name__ + ".UploadOvercloudImage")
def _env_variable_or_set(self, key_name, default_value):
os.environ[key_name] = os.environ.get(key_name, default_value)
def _delete_image_if_exists(self, image_client, name):
try:
image = utils.find_resource(image_client.images, name)
image_client.images.delete(image.id)
except exceptions.CommandError:
self.log.debug('Image "%s" have already not existed, '
'no problem.' % name)
def _check_file_exists(self, file_path):
if not os.path.isfile(file_path):
print('ERROR: Required file "%s" does not exist' % file_path)
exit(1)
def _read_image_file_pointer(self, dirname, filename):
filepath = os.path.join(dirname, filename)
self._check_file_exists(filepath)
return open(filepath, 'rb')
def _copy_file(self, src, dest):
subprocess.call('sudo cp -f "{0}" "{1}"'.format(src, dest), shell=True)
def _load_image(self, image_path, image_name, image_client):
self.log.debug("uploading images to glance")
kernel_id = image_client.images.create(
name='%s-vmlinuz' % image_name,
is_public=True,
disk_format='aki',
data=self._read_image_file_pointer(image_path,
'%s.vmlinuz' % image_name)
).id
ramdisk_id = image_client.images.create(
name='%s-initrd' % image_name,
is_public=True,
disk_format='ari',
data=self._read_image_file_pointer(image_path,
'%s.initrd' % image_name)
).id
image_client.images.create(
name=image_name,
is_public=True,
disk_format='qcow2',
container_format='bare',
properties={'kernel_id': kernel_id, 'ramdisk_id': ramdisk_id},
data=self._read_image_file_pointer(image_path,
'%s.qcow2' % image_name)
)
def get_parser(self, prog_name):
parser = super(UploadOvercloudImage, self).get_parser(prog_name)
parser.add_argument(
"--image-path",
default=os.environ.get('IMAGE_PATH', './'),
help="Path to directory containing image files",
)
parser.add_argument(
"--os-image",
default=os.environ.get('OS_IMAGE', 'overcloud-full.qcow2'),
help="OpenStack disk image filename",
)
parser.add_argument(
"--http-boot",
default=os.environ.get('HTTP_BOOT', '/httpboot'),
help="Root directory for dicovery images",
)
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
image_client = self.app.client_manager.image
self._env_variable_or_set('DEPLOY_NAME', 'deploy-ramdisk-ironic')
self._env_variable_or_set('DISCOVERY_NAME', 'discovery-ramdisk')
self.log.debug("check image files")
image_files = [
'%s.initramfs' % os.environ['DEPLOY_NAME'],
'%s.kernel' % os.environ['DEPLOY_NAME'],
'%s.initramfs' % os.environ['DISCOVERY_NAME'],
'%s.kernel' % os.environ['DISCOVERY_NAME'],
parsed_args.os_image
]
for image in image_files:
self._check_file_exists(os.path.join(parsed_args.image_path,
image))
self.log.debug("prepare glance images")
self._load_image(parsed_args.image_path,
parsed_args.os_image.split('.')[0],
image_client)
self._delete_image_if_exists(image_client, 'bm_deploy_kernel')
self._delete_image_if_exists(image_client, 'bm_deploy_ramdisk')
image_client.images.create(
name='bm-deploy-kernel',
is_public=True,
disk_format='aki',
data=self._read_image_file_pointer(parsed_args.image_path,
'%s.kernel' %
os.environ['DEPLOY_NAME'])
)
image_client.images.create(
name='bm-deploy-ramdisk',
is_public=True,
disk_format='ari',
data=self._read_image_file_pointer(parsed_args.image_path,
'%s.initramfs' %
os.environ['DEPLOY_NAME'])
)
self.log.debug("copy discovery images to HTTP BOOT dir")
self._copy_file(
os.path.join(parsed_args.image_path,
'%s.kernel' % os.environ['DISCOVERY_NAME']),
os.path.join(parsed_args.http_boot, 'discovery.kernel')
)
self._copy_file(
os.path.join(parsed_args.image_path,
'%s.initramfs' % os.environ['DISCOVERY_NAME']),
os.path.join(parsed_args.http_boot, 'discovery.ramdisk')
)