Source code for kollacli.commands.host

# Copyright(c) 2016, Oracle and/or its affiliates.  All Rights Reserved.
#
#    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 argparse
import getpass
import logging
import os
import traceback
import yaml

import kollacli.i18n as u

from kollacli.api.client import ClientApi
from kollacli.api.exceptions import ClientException
from kollacli.commands.exceptions import CommandError
from kollacli.common.utils import convert_lists_to_string
from kollacli.common.utils import get_setup_user

from cliff.command import Command
from cliff.lister import Lister

LOG = logging.getLogger(__name__)
CLIENT = ClientApi()


[docs]class HostAdd(Command): """Add host to openstack-kolla."""
[docs] def get_parser(self, prog_name): parser = super(HostAdd, self).get_parser(prog_name) parser.add_argument('hostname', metavar='<hostname>', help=u._('Host name or ip address')) return parser
[docs] def take_action(self, parsed_args): try: hostname = parsed_args.hostname.strip() CLIENT.host_add([hostname]) except ClientException as e: raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc())
[docs]class HostDestroy(Command): """Destroy. Stops and removes all kolla related docker containers on either the specified host or all hosts if the hostname all is used. """
[docs] def get_parser(self, prog_name): parser = super(HostDestroy, self).get_parser(prog_name) parser.add_argument('hostname', metavar='<hostname | all>', help=u._('Host name or ip address or "all"')) parser.add_argument('--stop', action='store_true', help=u._('Stop rather than kill')) parser.add_argument('--includedata', action='store_true', help=u._('Destroy data containers')) parser.add_argument('--removeimages', action='store_true', help=u._('Remove docker images')) return parser
[docs] def take_action(self, parsed_args): try: hostname = parsed_args.hostname.strip() hostnames = [hostname] if hostname == 'all': hostnames = _get_all_hostnames() destroy_type = 'kill' if parsed_args.stop: destroy_type = 'stop' include_data = False if parsed_args.includedata: include_data = True remove_images = False if parsed_args.removeimages: remove_images = True if not include_data: question = ('This will delete all containers and data' ', are you sure? (y/n)') answer = raw_input(question) while answer != 'y' and answer != 'n': answer = raw_input(question) if answer is 'n': LOG.info('Aborting destroy') return verbose_level = self.app.options.verbose_level job = CLIENT.async_host_destroy(hostnames, destroy_type, verbose_level, include_data, remove_images) status = job.wait() if verbose_level > 2: LOG.info('\n\n' + 80 * '=') LOG.info(u._('DEBUG command output:\n{out}') .format(out=job.get_console_output())) if status != 0: raise CommandError(u._('Job failed:\n{msg}') .format(msg=job.get_error_message())) elif verbose_level > 1: # log any ansible warnings msg = job.get_error_message() if msg: LOG.warn(msg) except ClientException as e: raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc())
[docs]class HostRemove(Command): """Remove host from openstack-kolla."""
[docs] def get_parser(self, prog_name): parser = super(HostRemove, self).get_parser(prog_name) parser.add_argument('hostname', metavar='<hostname | all>', help=u._('Host name or "all"')) return parser
[docs] def take_action(self, parsed_args): try: hostname = parsed_args.hostname.strip() hostnames = [hostname] if hostname == 'all': hostnames = _get_all_hostnames() CLIENT.host_remove(hostnames) except ClientException as e: raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc())
[docs]class HostList(Lister): """List hosts and their groups. If a hostname is provided, only list information about that host. """
[docs] def get_parser(self, prog_name): parser = super(HostList, self).get_parser(prog_name) parser.add_argument('hostname', nargs='?', metavar='[hostname]', help=u._('Host name')) return parser
[docs] def take_action(self, parsed_args): try: hostname = None if parsed_args.hostname: hostname = parsed_args.hostname.strip() hosts = [] if hostname: hosts = CLIENT.host_get([hostname]) else: hosts = CLIENT.host_get_all() data = [] if hosts: for host in hosts: data.append((host.name, host.get_groups())) else: data.append(('', '')) data = convert_lists_to_string(data, parsed_args) return ((u._('Host'), u._('Groups')), sorted(data)) except ClientException as e: raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc())
[docs]class HostCheck(Command): """Check configuration of host(s)."""
[docs] def get_parser(self, prog_name): parser = super(HostCheck, self).get_parser(prog_name) parser.add_argument('hostname', metavar='<hostname | all>', help=u._('Host name or "all"')) parser.add_argument('--predeploy', action='store_true', help=u._('Run pre-deploy host checks.')) return parser
[docs] def take_action(self, parsed_args): try: hostname = parsed_args.hostname.strip() hostnames = [hostname] if hostname == 'all': hostnames = _get_all_hostnames() if parsed_args.predeploy: # run pre-deploy checks verbose_level = self.app.options.verbose_level job = CLIENT.async_host_precheck(hostnames, verbose_level) status = job.wait() if verbose_level > 2: LOG.info('\n\n' + 80 * '=') LOG.info(u._('DEBUG command output:\n{out}') .format(out=job.get_console_output())) if status != 0: raise CommandError(u._('Job failed:\n{msg}') .format(msg=job.get_error_message())) elif verbose_level > 1: # log any ansible warnings msg = job.get_error_message() if msg: LOG.warn(msg) else: # just do an ssh check summary = CLIENT.host_ssh_check(hostnames) all_ok = True for hostname, info in summary.items(): status = u._('success') msg = '' if not info['success']: status = u._('failed- ') msg = info['msg'] all_ok = False LOG.info(u._('Host {host}: {sts} {msg}') .format(host=hostname, sts=status, msg=msg)) if not all_ok: raise CommandError(u._('Host check failed.')) except ClientException as e: raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc())
[docs]class HostSetup(Command): """Setup openstack-kollacli on host."""
[docs] def get_parser(self, prog_name): parser = super(HostSetup, self).get_parser(prog_name) parser.add_argument('hostname', nargs='?', metavar='<hostname>', help=u._('Host name')) parser.add_argument('--insecure', nargs='?', help=argparse.SUPPRESS) parser.add_argument('--file', '-f', nargs='?', metavar='<hosts_info_file>', help=u._('Absolute path to hosts info file ')) return parser
[docs] def take_action(self, parsed_args): try: if not parsed_args.hostname and not parsed_args.file: raise CommandError( u._('Host name or hosts info file path is required.')) if parsed_args.hostname and parsed_args.file: raise CommandError( u._('Host name and hosts info file path ' 'cannot both be present.')) if parsed_args.file: # multi-host setup via xml file hosts_data = self._get_yml_data(parsed_args.file.strip()) CLIENT.host_setup(hosts_data) else: # single host setup hostname = parsed_args.hostname.strip() summary = CLIENT.host_ssh_check([hostname]) if summary[hostname]['success']: LOG.info( u._LI('Skipping setup of host ({host}) as ' 'ssh check is ok.').format(host=hostname)) return True if parsed_args.insecure: password = parsed_args.insecure.strip() else: password = getpass.getpass( u._('{name} password for {host}: ') .format(name=get_setup_user(), host=hostname)) CLIENT.host_setup({hostname: {'password': password}}) except ClientException as e: raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc())
def _get_yml_data(self, yml_path): if not os.path.isfile(yml_path): raise CommandError( u._('No file exists at {path}. An absolute file path is ' 'required.').format(path=yml_path)) with open(yml_path, 'r') as hosts_file: file_data = hosts_file.read() hosts_info = yaml.safe_load(file_data) if not hosts_info: raise CommandError(u._('{path} is empty.').format(path=yml_path)) return hosts_info
[docs]class HostStop(Command): """Stop. Stops all kolla related docker containers on either the specified host or all hosts if the hostname all is used. """
[docs] def get_parser(self, prog_name): parser = super(HostStop, self).get_parser(prog_name) parser.add_argument('hostname', metavar='<hostname | all>', help=u._('Host name or ip address or "all"')) return parser
[docs] def take_action(self, parsed_args): try: hostname = parsed_args.hostname.strip() hostnames = [hostname] if hostname == 'all': hostnames = _get_all_hostnames() verbose_level = self.app.options.verbose_level job = CLIENT.async_host_stop(hostnames, verbose_level) status = job.wait() if verbose_level > 2: LOG.info('\n\n' + 80 * '=') LOG.info(u._('DEBUG command output:\n{out}') .format(out=job.get_console_output())) if status != 0: raise CommandError(u._('Job failed:\n{msg}') .format(msg=job.get_error_message())) elif verbose_level > 1: # log any ansible warnings msg = job.get_error_message() if msg: LOG.warn(msg) except ClientException as e: raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc())
def _get_all_hostnames(): hostnames = [] hosts = CLIENT.host_get_all() for host in hosts: hostnames.append(host.name) return hostnames