create apis for deploy, upgrade, host_destroy, host_precheck

- create apis for deploy, upgrade, host_destroy, host_precheck
- change CLI to use new APIs
- create new async api class for job-based apis
- add skeletal upgrade utest
- add some -v and -vv's to utests
- fix newly introduced bug in log_collector
- fix some pep8 warnings

Jira-Issue: OSTACKDEV-20
This commit is contained in:
Steve Noyes 2016-03-17 14:26:43 -04:00
parent 67ef2345d7
commit 1e58e92d5e
12 changed files with 190 additions and 41 deletions

66
kollacli/api/async.py Normal file
View File

@ -0,0 +1,66 @@
# 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 logging
from kollacli.api.job import Job
from kollacli.common.ansible import actions
LOG = logging.getLogger(__name__)
class AsyncApi(object):
# TODO(bmace) -- update this to only take host names
# and we will probably only support compute host individual deploys
def async_deploy(self, hostnames=[], groupnames=[], servicenames=[],
serial_flag=False, verbose_level=1):
"""Deploy.
Deploy containers to hosts.
"""
ansible_job = actions.deploy(hostnames, groupnames, servicenames,
serial_flag, verbose_level)
return Job(ansible_job)
def async_upgrade(self, verbose_level=1):
"""Upgrade.
Upgrade containers to new version specified by the property
"openstack_release."
"""
ansible_job = actions.upgrade(verbose_level)
return Job(ansible_job)
def async_host_destroy(self, hostname, destroy_type, verbose_level=1,
include_data=False):
"""Destroy Hosts.
Stops and removes all kolla related docker containers on either the
specified host or all hosts if hostname is "all".
"""
ansible_job = actions.destroy_hosts(hostname, destroy_type,
verbose_level, include_data)
return Job(ansible_job)
def async_host_precheck(self, hostname, verbose_level=1):
"""Check pre-deployment configuration of host(s).
Check if host is ready for a new deployment. This will fail if
the host is not configured correctly or if it has already been
deployed to.
If hostname is "all", then check all hosts.
"""
ansible_job = actions.precheck(hostname, verbose_level)
return Job(ansible_job)

View File

@ -13,6 +13,7 @@
# under the License. # under the License.
import logging import logging
from kollacli.api.async import AsyncApi
from kollacli.api.deploy import DeployApi from kollacli.api.deploy import DeployApi
from kollacli.api.host import HostApi from kollacli.api.host import HostApi
@ -20,6 +21,7 @@ LOG = logging.getLogger(__name__)
class ClientApi( class ClientApi(
AsyncApi,
DeployApi, DeployApi,
HostApi HostApi
): ):

View File

@ -13,21 +13,13 @@
# under the License. # under the License.
import logging import logging
LOG = logging.getLogger(__name__)
from kollacli.common.ansible import actions
from kollacli.common.inventory import Inventory from kollacli.common.inventory import Inventory
LOG = logging.getLogger(__name__)
class DeployApi(object): class DeployApi(object):
# TODO(bmace) -- update this to only take host names
# and we will probably only support compute host individual deploys
def deploy(self, hostnames=[], groupnames=[], servicenames=[],
serial_flag=False, verbose_level=1):
actions.deploy(hostnames, groupnames, servicenames,
serial_flag, verbose_level)
def deploy_set_mode(self, remote_mode): def deploy_set_mode(self, remote_mode):
inventory = Inventory.load() inventory = Inventory.load()
inventory.set_deploy_mode(remote_mode) inventory.set_deploy_mode(remote_mode)

50
kollacli/api/job.py Normal file
View File

@ -0,0 +1,50 @@
# 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.
class Job(object):
def __init__(self, ansible_job):
self._ansible_job = ansible_job
def wait(self):
"""wait for job to complete
return status of job (see get_status() for status values)
"""
return self._ansible_job.wait()
def get_status(self):
"""get status of job
Status:
- None: still running
- 0: complete/success
- 1: complete/fail
"""
return self._ansible_job.get_status()
def get_error_message(self):
"""get error message
if job failed, this will return a string with the error message.
"""
return self._ansible_job.get_error_message()
def get_console_output(self):
"""get command output
get the console output from the job. Returns a string
containing the console output of the job.
"""
return self._ansible_job.get_command_output()

View File

@ -62,7 +62,19 @@ class Deploy(Command):
if parsed_args.serial: if parsed_args.serial:
serial_flag = True serial_flag = True
CLIENT.deploy(hosts, groups, services, serial_flag, verbose_level) job = CLIENT.async_deploy(hosts, groups, services, serial_flag,
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:
LOG.info(u._('Success'))
else:
raise CommandError(u._('Job failed:\n{msg}')
.format(msg=job.get_error_message()))
except Exception: except Exception:
raise Exception(traceback.format_exc()) raise Exception(traceback.format_exc())

View File

@ -21,8 +21,6 @@ import yaml
import kollacli.i18n as u import kollacli.i18n as u
from kollacli.api.client import ClientApi from kollacli.api.client import ClientApi
from kollacli.common.ansible.actions import destroy_hosts
from kollacli.common.ansible.actions import precheck
from kollacli.common.inventory import Inventory from kollacli.common.inventory import Inventory
from kollacli.common.utils import convert_to_unicode from kollacli.common.utils import convert_to_unicode
from kollacli.common.utils import get_setup_user from kollacli.common.utils import get_setup_user
@ -99,7 +97,16 @@ class HostDestroy(Command):
verbose_level = self.app.options.verbose_level verbose_level = self.app.options.verbose_level
destroy_hosts(hostname, destroy_type, verbose_level, include_data) job = CLIENT.async_host_destroy(hostname, destroy_type,
verbose_level, include_data)
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()))
except CommandError as e: except CommandError as e:
raise e raise e
@ -193,7 +200,17 @@ class HostCheck(Command):
if parsed_args.predeploy: if parsed_args.predeploy:
# run pre-deploy checks # run pre-deploy checks
precheck(hostname) verbose_level = self.app.options.verbose_level
job = CLIENT.async_host_precheck(hostname, 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()))
else: else:
# run ssh checks # run ssh checks
all_ok = True all_ok = True

View File

@ -14,10 +14,16 @@
import logging import logging
import traceback import traceback
from kollacli.common.ansible.actions import upgrade
from cliff.command import Command from cliff.command import Command
import kollacli.i18n as u
from kollacli.api.client import ClientApi
from kollacli.exceptions import CommandError
CLIENT = ClientApi()
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -28,9 +34,19 @@ class Upgrade(Command):
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
verbose_level = self.app.options.verbose_level
try: try:
upgrade(verbose_level) verbose_level = self.app.options.verbose_level
job = CLIENT.async_upgrade(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:
LOG.info(u._('Success'))
else:
raise CommandError(u._('Job failed:\n{msg}')
.format(msg=job.get_error_message()))
except Exception: except Exception:
raise Exception(traceback.format_exc()) raise Exception(traceback.format_exc())

View File

@ -65,7 +65,7 @@ def destroy_hosts(hostname, destroy_type, verbose_level=1, include_data=False):
playbook.print_output = False playbook.print_output = False
playbook.verbose_level = verbose_level playbook.verbose_level = verbose_level
job = playbook.run() job = playbook.run()
_process_job(job, verbose_level) return job
def deploy(hostnames=[], groupnames=[], servicenames=[], def deploy(hostnames=[], groupnames=[], servicenames=[],
@ -84,7 +84,7 @@ def deploy(hostnames=[], groupnames=[], servicenames=[],
_run_deploy_rules(playbook) _run_deploy_rules(playbook)
job = playbook.run() job = playbook.run()
_process_job(job, verbose_level) return job
def precheck(hostname, verbose_level=1): def precheck(hostname, verbose_level=1):
@ -102,7 +102,7 @@ def precheck(hostname, verbose_level=1):
playbook.print_output = True playbook.print_output = True
playbook.verbose_level = verbose_level playbook.verbose_level = verbose_level
job = playbook.run() job = playbook.run()
_process_job(job, verbose_level) return job
def upgrade(verbose_level=1): def upgrade(verbose_level=1):
@ -114,19 +114,7 @@ def upgrade(verbose_level=1):
playbook.print_output = True playbook.print_output = True
playbook.verbose_level = verbose_level playbook.verbose_level = verbose_level
job = playbook.run() job = playbook.run()
_process_job(job, verbose_level) return job
def _process_job(job, verbose_level):
job.wait()
status = job.get_status()
if status != 0:
if verbose_level > 2:
LOG.info('\n\n' + 80 * '=')
LOG.info('DEBUG command output:\n%s'
% job.get_command_output())
raise CommandError(u._('Ansible command failed:\n{msg}')
.format(msg=job.get_error_message()))
def _run_deploy_rules(playbook): def _run_deploy_rules(playbook):

View File

@ -20,8 +20,8 @@ import subprocess # nosec
import tempfile import tempfile
import time import time
from kollacli.common.utils import get_admin_uids
from kollacli.common.inventory import remove_temp_inventory from kollacli.common.inventory import remove_temp_inventory
from kollacli.common.utils import get_admin_uids
from kollacli.common.utils import safe_decode from kollacli.common.utils import safe_decode
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)

View File

@ -137,14 +137,19 @@ class TestFunctional(KollaCliTest):
self.run_cli_cmd('property set enable_%s no' % service) self.run_cli_cmd('property set enable_%s no' % service)
self.run_cli_cmd('deploy') self.run_cli_cmd('deploy')
self.run_cli_cmd('deploy --serial') self.run_cli_cmd('deploy --serial -v')
self.run_cli_cmd('deploy --groups=control') self.run_cli_cmd('deploy --groups=control -vv')
finally: finally:
# re-enable services after the test # re-enable services after the test
for service in ALL_SERVICES: for service in ALL_SERVICES:
self.run_cli_cmd('property set enable_%s yes' % service) self.run_cli_cmd('property set enable_%s yes' % service)
def test_upgrade(self):
# test will upgrade an environment with no hosts, mostly a NOP,
# but it will go through the client code paths.
self.run_cli_cmd('upgrade -v')
def check_json(self, msg, groups, hosts, included_groups, included_hosts): def check_json(self, msg, groups, hosts, included_groups, included_hosts):
err_msg = ('included groups: %s\n' % included_groups + err_msg = ('included groups: %s\n' % included_groups +
'included hosts: %s\n' % included_hosts) 'included hosts: %s\n' % included_hosts)

View File

@ -112,7 +112,7 @@ class TestFunctional(KollaCliTest):
# destroy non-data services (via --stop flag) # destroy non-data services (via --stop flag)
# this should leave only data containers running # this should leave only data containers running
try: try:
self.run_cli_cmd('host destroy %s --stop' % hostname) self.run_cli_cmd('host destroy %s --stop -v' % hostname)
except Exception as e: except Exception as e:
self.assertFalse(is_physical_host, '2nd destroy exception: %s' % e) self.assertFalse(is_physical_host, '2nd destroy exception: %s' % e)
self.assertIn(UNKNOWN_HOST, '%s' % e, self.assertIn(UNKNOWN_HOST, '%s' % e,
@ -133,7 +133,8 @@ class TestFunctional(KollaCliTest):
'after no-data destroy.') 'after no-data destroy.')
try: try:
self.run_cli_cmd('host destroy %s --includedata --stop' % hostname) self.run_cli_cmd('host destroy %s --includedata --stop -vv'
% hostname)
except Exception as e: except Exception as e:
self.assertFalse(is_physical_host, '3rd destroy exception: %s' % e) self.assertFalse(is_physical_host, '3rd destroy exception: %s' % e)
self.assertIn(UNKNOWN_HOST, '%s' % e, self.assertIn(UNKNOWN_HOST, '%s' % e,

View File

@ -20,10 +20,10 @@ import tarfile
import tempfile import tempfile
from kollacli.common.inventory import Inventory from kollacli.common.inventory import Inventory
from kollacli.common.inventory import remove_temp_inventory
from kollacli.common import properties from kollacli.common import properties
from kollacli.common.utils import get_admin_user from kollacli.common.utils import get_admin_user
from kollacli.common.utils import get_ansible_command from kollacli.common.utils import get_ansible_command
from kollacli.common.utils import remove_temp_inventory
from kollacli.common.utils import safe_decode from kollacli.common.utils import safe_decode
tar_file_descr = None tar_file_descr = None