fuel-ccp/fuel_ccp/cli.py

291 lines
9.4 KiB
Python

import argparse
import logging
import os.path
import signal
import sys
from cliff import app
from cliff import command
from cliff import commandmanager
from cliff import lister
import fuel_ccp
from fuel_ccp import build
from fuel_ccp import cleanup
from fuel_ccp.common import utils
from fuel_ccp import config
from fuel_ccp import dependencies
from fuel_ccp import deploy
from fuel_ccp import fetch
from fuel_ccp import status
from fuel_ccp import validate
from fuel_ccp.validation import service as validation_service
CONF = config.CONF
LOG = logging.getLogger(__name__)
class BaseCommand(command.Command):
def get_parser(self, *args, **kwargs):
parser = super(BaseCommand, self).get_parser(*args, **kwargs)
parser.set_defaults(**CONF.action._dict)
return parser
class Build(BaseCommand):
"""Build CCP docker images"""
def get_parser(self, *args, **kwargs):
parser = super(Build, self).get_parser(*args, **kwargs)
parser.add_argument('-c', '--components',
nargs='+',
help='CCP component to build')
return parser
def take_action(self, parsed_args):
if CONF.builder.push and not CONF.registry.address:
raise RuntimeError('No registry specified, cannot push')
if CONF.repositories.clone:
do_fetch()
config.load_component_defaults()
build.build_components(components=parsed_args.components)
class Deploy(BaseCommand):
"""Deploy CCP"""
def get_parser(self, *args, **kwargs):
parser = super(Deploy, self).get_parser(*args, **kwargs)
parser.add_argument('-c', '--components',
nargs='+',
help='CCP component to build')
parser.add_argument("--dry-run",
action='store_true',
help="Print k8s objects definitions without"
"actual creation")
parser.add_argument('--export-dir',
help='Directory to export created k8s objects')
return parser
def take_action(self, parsed_args):
if CONF.repositories.clone:
do_fetch()
config.load_component_defaults()
# only these two are being implicitly passed
CONF.action._update(
dry_run=parsed_args.dry_run,
export_dir=parsed_args.export_dir,
)
components_map = utils.get_deploy_components_info()
components = parsed_args.components
if components:
components = set(components)
validation_service.validate_service_definitions(
components_map, components)
deploy.deploy_components(components_map, components)
def do_fetch():
fetch.fetch_repositories()
class Fetch(BaseCommand):
"""Fetch all repos with components definitions"""
def take_action(self, parsed_args):
do_fetch()
class Validate(BaseCommand):
"""Validate CCP components"""
def get_parser(self, *args, **kwargs):
parser = super(Validate, self).get_parser(*args, **kwargs)
parser.add_argument('-c', '--components',
nargs='+',
help='CCP components to validate')
parser.add_argument('-t', '--types',
nargs="+",
help="List of validation types to perform. "
"If not specified - perform all "
"supported validation types")
return parser
def take_action(self, parsed_args):
if CONF.repositories.clone:
do_fetch()
config.load_component_defaults()
components = parsed_args.components
if components:
components = set(components)
validate.validate(components=components, types=parsed_args.types)
class Cleanup(BaseCommand):
"""Remove all OpenStack resources and destroy CCP deployment"""
def get_parser(self, *args, **kwargs):
parser = super(Cleanup, self).get_parser(*args, **kwargs)
# Making auth url configurable at least until Ingress/LB support will
# be implemented
parser.add_argument('--auth-url',
help='The URL of Keystone authentication '
'server')
parser.add_argument('--skip-os-cleanup',
action='store_true',
help='Skip cleanup of OpenStack environment')
return parser
def take_action(self, parsed_args):
config.load_component_defaults()
cleanup.cleanup(auth_url=parsed_args.auth_url,
skip_os_cleanup=parsed_args.skip_os_cleanup)
class ShowDep(BaseCommand):
"""Show dependencies of CCP components"""
def get_parser(self, *args, **kwargs):
parser = super(ShowDep, self).get_parser(*args, **kwargs)
parser.add_argument('components',
nargs='+',
help='CCP components to show dependencies')
return parser
def take_action(self, parsed_args):
if CONF.repositories.clone:
do_fetch()
config.load_component_defaults()
dependencies.show_dep(parsed_args.components)
class ConfigDump(BaseCommand):
"""Dump full current configuration to stdout"""
def get_parser(self, *args, **kwargs):
parser = super(ConfigDump, self).get_parser(*args, **kwargs)
return parser
def take_action(self, parsed_args):
if CONF.repositories.clone:
do_fetch()
config.load_component_defaults()
config.dump_yaml(self.app.stdout)
class ShowStatus(lister.Lister):
"""Show status of deployment"""
def get_parser(self, *args, **kwargs):
parser = super(ShowStatus, self).get_parser(*args, **kwargs)
parser.set_defaults(**CONF.action._dict)
parser.add_argument("-l", "--long",
action="store_true",
help="show all components status")
parser.add_argument("-s", "--short",
action="store_true",
help="show cluster status (ready or not)")
parser.add_argument("components",
nargs="*",
help="CCP conponents to show status")
return parser
def take_action(self, parsed_args):
config.load_component_defaults()
if parsed_args.long:
return status.show_long_status()
elif parsed_args.short:
return status.show_short_status()
else:
return status.show_long_status(parsed_args.components)
class ImagesList(BaseCommand, lister.Lister):
"""Get images mathcing list of components"""
def get_parser(self, *args, **kwargs):
parser = super(ImagesList, self).get_parser(*args, **kwargs)
parser.add_argument('components',
nargs='*',
help='CCP components to get images for')
return parser
def take_action(self, parsed_args):
dockerfiles = build.get_dockerfiles(match=not parsed_args.components)
for component in parsed_args.components:
build.match_dockerfiles_by_component(dockerfiles, component)
return (
('Name',),
((d['name'],) for d in dockerfiles.values() if d['match']),
)
def signal_handler(signo, frame):
sys.exit(-signo)
class CCPApp(app.App):
CONSOLE_MESSAGE_FORMAT = \
'%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
def __init__(self, **kwargs):
super(CCPApp, self).__init__(
description='Containerized Control Plane tool',
version=fuel_ccp.__version__,
command_manager=commandmanager.CommandManager('ccp.cli'),
deferred_help=True,
**kwargs
)
self.config_file = None
@staticmethod
def add_config_file_argument(parser):
parser.add_argument(
'--config-file',
metavar='PATH',
help=('Path to a config file to use.'))
@staticmethod
def get_config_file(argv):
parser = argparse.ArgumentParser(add_help=False)
CCPApp.add_config_file_argument(parser)
parsed_args, _ = parser.parse_known_args(argv)
if parsed_args.config_file:
return os.path.abspath(os.path.expanduser(parsed_args.config_file))
else:
return config.find_config()
def run(self, argv):
self.config_file = self.get_config_file(argv)
config.setup_config(self.config_file)
self.add_config_file_argument(self.parser)
defaults = {k: CONF[k] for k in ['debug', 'verbose_level', 'log_file']}
if CONF.debug: # We're used to having DEBUG logging with debug conf
defaults['verbose_level'] = 2
self.parser.set_defaults(**defaults)
return super(CCPApp, self).run(argv)
def configure_logging(self):
super(CCPApp, self).configure_logging()
if self.config_file:
LOG.debug('Loaded config from file %s', self.config_file)
else:
LOG.debug('No config file loaded')
def main(argv=None):
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
if argv is None:
argv = sys.argv[1:]
CCPApp().run(argv)
if __name__ == '__main__':
main()