bareon/contrib/fuel_bootstrap/fuel_bootstrap_cli/fuel_bootstrap/utils/bootstrap_image.py

206 lines
6.5 KiB
Python

# Copyright 2015 Mirantis, 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 shutil
import tarfile
import tempfile
import yaml
from bareon.utils import utils
from fuel_bootstrap import consts
from fuel_bootstrap import errors
from fuel_bootstrap.objects import master_node_settings
from fuel_bootstrap import settings
from fuel_bootstrap.utils import data as data_util
CONF = settings.Configuration()
LOG = logging.getLogger(__name__)
ACTIVE = 'active'
def get_all():
data = []
LOG.debug("Searching images in %s", CONF.bootstrap_images_dir)
for name in os.listdir(CONF.bootstrap_images_dir):
if not os.path.isdir(os.path.join(CONF.bootstrap_images_dir, name)):
continue
try:
data.append(parse(name))
except errors.IncorrectImage as e:
LOG.debug("Image [%s] is skipped due to %s", name, e)
return data
def parse(image_uuid):
LOG.debug("Trying to parse [%s] image", image_uuid)
dir_path = full_path(image_uuid)
if os.path.islink(dir_path) or not os.path.isdir(dir_path):
raise errors.IncorrectImage("There are no such image [{0}]."
.format(image_uuid))
metafile = os.path.join(dir_path, consts.METADATA_FILE)
if not os.path.exists(metafile):
raise errors.IncorrectImage("Image [{0}] doen's contain metadata file."
.format(image_uuid))
with open(metafile) as f:
try:
data = yaml.safe_load(f)
except yaml.YAMLError as e:
raise errors.IncorrectImage("Couldn't parse metadata file for"
" image [{0}] due to {1}"
.format(image_uuid, e))
if data.get('uuid') != os.path.basename(dir_path):
raise errors.IncorrectImage("UUID from metadata file [{0}] doesn't"
" equal directory name [{1}]"
.format(data.get('uuid'), image_uuid))
data['status'] = ACTIVE if is_active(data['uuid']) else ''
data.setdefault('label', '')
return data
def delete(image_uuid):
dir_path = full_path(image_uuid)
image = parse(image_uuid)
if image['status'] == ACTIVE:
raise errors.ActiveImageException("Image [{0}] is active and can't be"
" deleted.".format(image_uuid))
shutil.rmtree(dir_path)
return image_uuid
def is_active(image_uuid):
return full_path(image_uuid) == os.path.realpath(
CONF.active_bootstrap_symlink)
def full_path(image_uuid):
if not os.path.isabs(image_uuid):
return os.path.join(CONF.bootstrap_images_dir, image_uuid)
return image_uuid
def import_image(arch_path):
extract_dir = tempfile.mkdtemp()
extract_to_dir(arch_path, extract_dir)
metafile = os.path.join(extract_dir, consts.METADATA_FILE)
with open(metafile) as f:
try:
data = yaml.safe_load(f)
except yaml.YAMLError as e:
raise errors.IncorrectImage("Couldn't parse metadata file"
" due to {0}".format(e))
image_uuid = data['uuid']
dir_path = full_path(image_uuid)
if os.path.exists(dir_path):
raise errors.ImageAlreadyExists("Image [{0}] already exists."
.format(image_uuid))
shutil.move(extract_dir, dir_path)
os.chmod(dir_path, 0o755)
for root, dirs, files in os.walk(dir_path):
for d in dirs:
os.chmod(os.path.join(root, d), 0o755)
for f in files:
os.chmod(os.path.join(root, f), 0o755)
return image_uuid
def extract_to_dir(arch_path, extract_path):
LOG.info("Try extract %s to %s", arch_path, extract_path)
tarfile.open(arch_path, 'r').extractall(extract_path)
def make_bootstrap(data=None):
if not data:
data = {}
bootdata_builder = data_util.BootstrapDataBuilder(data)
bootdata = bootdata_builder.build()
LOG.info("Try to build image with data:\n%s", yaml.safe_dump(bootdata))
with tempfile.NamedTemporaryFile() as f:
f.write(yaml.safe_dump(bootdata))
f.flush()
opts = ['fa_mkbootstrap', '--nouse-syslog', '--data_driver',
'bootstrap_build_image', '--nodebug', '-v',
'--input_data_file', f.name]
if data.get('image_build_dir'):
opts.extend(['--image_build_dir', data['image_build_dir']])
utils.execute(*opts)
return bootdata['bootstrap']['uuid'], bootdata['output']
def activate(image_uuid=""):
is_centos = image_uuid.lower() == 'centos'
symlink = CONF.active_bootstrap_symlink
if os.path.lexists(symlink):
os.unlink(symlink)
LOG.debug("Symlink %s was deleted", symlink)
if not is_centos:
parse(image_uuid)
dir_path = full_path(image_uuid)
os.symlink(dir_path, symlink)
LOG.debug("Symlink %s to %s directory has been created",
symlink, dir_path)
else:
LOG.warning("WARNING: switching to depracated centos-bootstrap")
# FIXME: Do normal activation when it become clear how to do it
flavor = 'centos' if is_centos else 'ubuntu'
utils.execute('fuel-bootstrap-image-set', flavor)
return image_uuid
def call_wrapped_method(name, notify_webui, **kwargs):
wrapped_methods = {
'build': make_bootstrap,
'activate': activate
}
failed = False
try:
return wrapped_methods[name](**kwargs)
except Exception:
failed = True
raise
finally:
if notify_webui:
notify_webui_about_results(failed, consts.ERROR_MSG)
def notify_webui_about_results(failed, error_message):
mn_settings = master_node_settings.MasterNodeSettings()
settings = mn_settings.get()
settings['settings'].setdefault('bootstrap', {}).setdefault('error', {})
if not failed:
error_message = ""
settings['settings']['bootstrap']['error']['value'] = error_message
mn_settings.update(settings)