Add settings to fuel-bootstrap CLI
Change-Id: I2610333cf90827915786ee585fd9091c6ff9a17e Implements: blueprint bootstrap-images-support-in-cli
This commit is contained in:
parent
283624a1a6
commit
bd67efbada
|
@ -47,7 +47,7 @@ class BuildCommand(command.Command):
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--repo',
|
'--repo',
|
||||||
dest='repos',
|
dest='extra_repos',
|
||||||
type=str,
|
type=str,
|
||||||
metavar='REPOSITORY',
|
metavar='REPOSITORY',
|
||||||
help="Add one more repository. format 'type uri"
|
help="Add one more repository. format 'type uri"
|
||||||
|
@ -169,4 +169,6 @@ class BuildCommand(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
bs_image.make_bootstrap(parsed_args)
|
image_uuid, path = bs_image.make_bootstrap(parsed_args)
|
||||||
|
self.app.stdout.write("Bootstrap image {0} has been built: {1}\n"
|
||||||
|
.format(image_uuid, path))
|
||||||
|
|
|
@ -36,5 +36,5 @@ class ImportCommand(command.Command):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
# Cliff handles errors by himself
|
# Cliff handles errors by himself
|
||||||
image_uuid = bs_image.import_image(parsed_args.filename)
|
image_uuid = bs_image.import_image(parsed_args.filename)
|
||||||
self.app.stdout.write("Bootstrap image {0} has been imported"
|
self.app.stdout.write("Bootstrap image {0} has been imported.\n"
|
||||||
.format(image_uuid))
|
.format(image_uuid))
|
||||||
|
|
|
@ -14,14 +14,12 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
# FIXME: Move configurable consts to settings.yaml
|
# These consts shouldn't be configured
|
||||||
|
|
||||||
BOOTSTRAP_IMAGES_DIR = "/var/www/nailgun/bootstraps/"
|
# TODO(asvechnikov): add possibility to specify custom config file
|
||||||
|
CONFIG_FILE = "/etc/fuel-agent/fuel_bootstrap_cli.yaml"
|
||||||
METADATA_FILE = "metadata.yaml"
|
METADATA_FILE = "metadata.yaml"
|
||||||
SYMLINK = os.path.join(BOOTSTRAP_IMAGES_DIR, "active_bootstrap")
|
|
||||||
ASTUTE_FILE = "/etc/fuel/astute.yaml"
|
|
||||||
CONTAINER_FORMAT = "tar.gz"
|
CONTAINER_FORMAT = "tar.gz"
|
||||||
ROOTFS = {'name': 'rootfs',
|
ROOTFS = {'name': 'rootfs',
|
||||||
'mask': 'rootfs',
|
'mask': 'rootfs',
|
||||||
|
@ -43,26 +41,3 @@ BOOTSTRAP_MODULES = [
|
||||||
IMAGE_DATA = {'/': ROOTFS}
|
IMAGE_DATA = {'/': ROOTFS}
|
||||||
|
|
||||||
UBUNTU_RELEASE = 'trusty'
|
UBUNTU_RELEASE = 'trusty'
|
||||||
|
|
||||||
# Packages required for the master node to discover a bootstrap node
|
|
||||||
# Hardcoded list used for disable user-factor : when user can accidentally
|
|
||||||
# remove fuel-required packages, and create totally non-working bootstrap
|
|
||||||
DEFAULT_PACKAGES = [
|
|
||||||
"openssh-client",
|
|
||||||
"openssh-server",
|
|
||||||
"ntp",
|
|
||||||
"mcollective",
|
|
||||||
"nailgun-agent",
|
|
||||||
"nailgun-mcagents",
|
|
||||||
"network-checker",
|
|
||||||
"fuel-agent"
|
|
||||||
"ubuntu-minimal",
|
|
||||||
"live-boot",
|
|
||||||
"live-boot-initramfs-tools",
|
|
||||||
"wget",
|
|
||||||
"linux-firmware",
|
|
||||||
"linux-firmware-nonfree",
|
|
||||||
"xz-utils",
|
|
||||||
"squashfs-tools",
|
|
||||||
"msmtp-mta"
|
|
||||||
]
|
|
||||||
|
|
|
@ -43,3 +43,7 @@ class IncorrectRepository(FuelBootstrapException):
|
||||||
|
|
||||||
class IncorrectImage(FuelBootstrapException):
|
class IncorrectImage(FuelBootstrapException):
|
||||||
"""Should be raised when image has incorrect format"""
|
"""Should be raised when image has incorrect format"""
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFileNotExists(FuelBootstrapException):
|
||||||
|
"""Should be raised when default config file is not found"""
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 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 os
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from fuel_bootstrap import consts
|
||||||
|
from fuel_bootstrap import errors
|
||||||
|
|
||||||
|
|
||||||
|
class Configuration(object):
|
||||||
|
def __init__(self, config_file=None):
|
||||||
|
if not config_file:
|
||||||
|
config_file = consts.CONFIG_FILE
|
||||||
|
if os.path.exists(config_file):
|
||||||
|
with open(config_file) as f:
|
||||||
|
data = yaml.load(f)
|
||||||
|
else:
|
||||||
|
raise errors.ConfigFileNotExists(
|
||||||
|
"Default config couldn't be found in {0}"
|
||||||
|
.format(config_file))
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return self._data.get(name)
|
|
@ -25,18 +25,19 @@ from fuel_agent.utils import utils
|
||||||
|
|
||||||
from fuel_bootstrap import consts
|
from fuel_bootstrap import consts
|
||||||
from fuel_bootstrap import errors
|
from fuel_bootstrap import errors
|
||||||
|
from fuel_bootstrap import settings
|
||||||
from fuel_bootstrap.utils import data as data_util
|
from fuel_bootstrap.utils import data as data_util
|
||||||
|
|
||||||
|
CONF = settings.Configuration()
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
ACTIVE = 'active'
|
ACTIVE = 'active'
|
||||||
|
|
||||||
|
|
||||||
def get_all():
|
def get_all():
|
||||||
data = []
|
data = []
|
||||||
LOG.debug("Searching images in %s", consts.BOOTSTRAP_IMAGES_DIR)
|
LOG.debug("Searching images in %s", CONF.bootstrap_images_dir)
|
||||||
for name in os.listdir(consts.BOOTSTRAP_IMAGES_DIR):
|
for name in os.listdir(CONF.bootstrap_images_dir):
|
||||||
if not os.path.isdir(os.path.join(consts.BOOTSTRAP_IMAGES_DIR, name)):
|
if not os.path.isdir(os.path.join(CONF.bootstrap_images_dir, name)):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
data.append(parse(name))
|
data.append(parse(name))
|
||||||
|
@ -85,12 +86,13 @@ def delete(image_id):
|
||||||
|
|
||||||
|
|
||||||
def is_active(image_id):
|
def is_active(image_id):
|
||||||
return full_path(image_id) == os.path.realpath(consts.SYMLINK)
|
return full_path(image_id) == os.path.realpath(
|
||||||
|
CONF.active_bootstrap_symlink)
|
||||||
|
|
||||||
|
|
||||||
def full_path(image_id):
|
def full_path(image_id):
|
||||||
if not os.path.isabs(image_id):
|
if not os.path.isabs(image_id):
|
||||||
return os.path.join(consts.BOOTSTRAP_IMAGES_DIR, image_id)
|
return os.path.join(CONF.bootstrap_images_dir, image_id)
|
||||||
return image_id
|
return image_id
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,7 +125,7 @@ def extract_to_dir(arch_path, extract_path):
|
||||||
|
|
||||||
|
|
||||||
def make_bootstrap(params):
|
def make_bootstrap(params):
|
||||||
bootdata_builder = data_util.BootstrapDataBuilder(params)
|
bootdata_builder = data_util.BootstrapDataBuilder(vars(params))
|
||||||
bootdata = bootdata_builder.build()
|
bootdata = bootdata_builder.build()
|
||||||
|
|
||||||
LOG.info("Try to build image with data:\n%s", yaml.safe_dump(bootdata))
|
LOG.info("Try to build image with data:\n%s", yaml.safe_dump(bootdata))
|
||||||
|
@ -135,3 +137,5 @@ def make_bootstrap(params):
|
||||||
'bootstrap_build_image', '--nodebug', '-v',
|
'bootstrap_build_image', '--nodebug', '-v',
|
||||||
'--image_build_dir', params.image_build_dir,
|
'--image_build_dir', params.image_build_dir,
|
||||||
'--input_data_file', f.name)
|
'--input_data_file', f.name)
|
||||||
|
|
||||||
|
return bootdata['uuid'], bootdata['output']
|
||||||
|
|
|
@ -19,54 +19,53 @@ import os
|
||||||
import re
|
import re
|
||||||
import six
|
import six
|
||||||
import uuid
|
import uuid
|
||||||
import yaml
|
|
||||||
|
|
||||||
from fuel_bootstrap import consts
|
from fuel_bootstrap import consts
|
||||||
from fuel_bootstrap import errors
|
from fuel_bootstrap import errors
|
||||||
|
from fuel_bootstrap import settings
|
||||||
|
|
||||||
|
CONF = settings.Configuration()
|
||||||
|
|
||||||
|
|
||||||
class BootstrapDataBuilder(object):
|
class BootstrapDataBuilder(object):
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
self.astute = self._parse_astute()
|
|
||||||
|
|
||||||
self.uuid = six.text_type(uuid.uuid4())
|
self.uuid = six.text_type(uuid.uuid4())
|
||||||
|
|
||||||
self.container_format = consts.CONTAINER_FORMAT
|
self.container_format = consts.CONTAINER_FORMAT
|
||||||
|
|
||||||
self.ubuntu_release = data.ubuntu_release or consts.UBUNTU_RELEASE
|
self.ubuntu_release = \
|
||||||
self.ubuntu_repo = data.ubuntu_repo
|
data.get('ubuntu_release') or \
|
||||||
self.mos_repo = data.mos_repo
|
consts.UBUNTU_RELEASE
|
||||||
self.repos = data.repos or []
|
|
||||||
|
|
||||||
self.http_proxy = data.http_proxy or \
|
self.ubuntu_repo = data.get('ubuntu_repo')
|
||||||
self.astute['BOOTSTRAP']['HTTP_PROXY']
|
self.mos_repo = data.get('mos_repo')
|
||||||
self.https_proxy = data.https_proxy or \
|
self.extra_repos = data.get('extra_repos') or []
|
||||||
self.astute['BOOTSTRAP']['HTTPS_PROXY']
|
|
||||||
self.direct_repo_addr = data.direct_repo_addr
|
|
||||||
|
|
||||||
self.post_script_file = data.post_script_file
|
self.http_proxy = data.get('http_proxy') or CONF.http_proxy
|
||||||
self.root_ssh_authorized_file = data.root_ssh_authorized_file
|
self.https_proxy = data.get('https_proxy') or CONF.https_proxy
|
||||||
self.extra_files = data.extra_files
|
self.direct_repo_addr = data.get('direct_repo_addr')
|
||||||
|
|
||||||
self.include_kernel_module = data.include_kernel_module
|
self.post_script_file = \
|
||||||
self.blacklist_kernel_module = data.blacklist_kernel_module
|
data.get('post_script_file') or \
|
||||||
|
CONF.post_script_file
|
||||||
|
self.root_ssh_authorized_file = \
|
||||||
|
data.get('root_ssh_authorized_file') or \
|
||||||
|
CONF.root_ssh_authorized_file
|
||||||
|
self.extra_files = data.get('extra_files') or CONF.extra_files
|
||||||
|
|
||||||
self.packages = data.packages
|
self.include_kernel_module = data.get('include_kernel_module')
|
||||||
|
self.blacklist_kernel_module = data.get('blacklist_kernel_module')
|
||||||
|
|
||||||
self.label = data.label
|
self.packages = data.get('packages')
|
||||||
self.extend_kopts = data.extend_kopts
|
|
||||||
self.kernel_flavor = data.kernel_flavor
|
|
||||||
self.output = os.path.join(
|
|
||||||
data.output_dir,
|
|
||||||
"{uuid}.{format}".format(
|
|
||||||
uuid=self.uuid,
|
|
||||||
format=self.container_format))
|
|
||||||
|
|
||||||
def _parse_astute(self):
|
self.label = data.get('label') or self.uuid
|
||||||
with open(consts.ASTUTE_FILE) as f:
|
self.extend_kopts = data.get('extend_kopts')
|
||||||
data = yaml.safe_load(f)
|
self.kernel_flavor = data.get('kernel_flavor')
|
||||||
return data
|
|
||||||
|
file_name = "{0}.{1}".format(self.uuid, self.container_format)
|
||||||
|
output_dir = data.get('output_dir', CONF.output_dir)
|
||||||
|
self.output = os.path.join(output_dir, file_name)
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
return {
|
return {
|
||||||
|
@ -113,7 +112,7 @@ class BootstrapDataBuilder(object):
|
||||||
if self.direct_repo_addr:
|
if self.direct_repo_addr:
|
||||||
addrs |= set(self.direct_repo_addr)
|
addrs |= set(self.direct_repo_addr)
|
||||||
|
|
||||||
addrs.add(self.astute['ADMIN_NETWORK']['ipaddress'])
|
addrs |= set(CONF.direct_repo_adresses)
|
||||||
|
|
||||||
return list(addrs)
|
return list(addrs)
|
||||||
|
|
||||||
|
@ -122,27 +121,27 @@ class BootstrapDataBuilder(object):
|
||||||
if self.ubuntu_repo:
|
if self.ubuntu_repo:
|
||||||
repos.extend(self._parse_ubuntu_repos(self.ubuntu_repo))
|
repos.extend(self._parse_ubuntu_repos(self.ubuntu_repo))
|
||||||
else:
|
else:
|
||||||
repos.extend(self.astute['BOOTSTRAP']['MIRROR_DISTRO'])
|
repos.extend(CONF.ubuntu_repos)
|
||||||
|
|
||||||
if self.mos_repo:
|
if self.mos_repo:
|
||||||
repos.extend(self._parse_mos_repos(self.mos_repo))
|
repos.extend(self._parse_mos_repos(self.mos_repo))
|
||||||
else:
|
else:
|
||||||
repos.extend(self.astute['BOOTSTRAP']['MIRROR_MOS'])
|
repos.extend(CONF.mos_repos)
|
||||||
|
|
||||||
repo_count = 0
|
repo_count = 0
|
||||||
for repo in self.repos:
|
for repo in self.extra_repos:
|
||||||
repo_count += 1
|
repo_count += 1
|
||||||
repos.append(self._parse_repo(
|
repos.append(self._parse_repo(
|
||||||
repo,
|
repo,
|
||||||
name="extra_repo{0}".format(repo_count)))
|
name="extra_repo{0}".format(repo_count)))
|
||||||
|
|
||||||
if not self.repos:
|
if not self.extra_repos:
|
||||||
repos.extend(self.astute['BOOTSTRAP']['EXTRA_DEB_REPOS'])
|
repos.extend(CONF.extra_repos)
|
||||||
|
|
||||||
return sorted(repos, key=lambda repo: repo['priority'] or 500)
|
return sorted(repos, key=lambda repo: repo['priority'] or 500)
|
||||||
|
|
||||||
def _get_packages(self):
|
def _get_packages(self):
|
||||||
result = set(consts.DEFAULT_PACKAGES)
|
result = set(CONF.packages)
|
||||||
result.add(self.kernel_flavor)
|
result.add(self.kernel_flavor)
|
||||||
if self.packages:
|
if self.packages:
|
||||||
result |= set(self.packages)
|
result |= set(self.packages)
|
||||||
|
|
Loading…
Reference in New Issue