Merge branch 'o3l_2.next' of git://ca-git.us.oracle.com/openstack-kollacli
Jira-Issue: OPENSTACK-592 Conflicts: kollacli/commands/host.py kollacli/common.py tests/deploy.py tests/password.py tests/property.py tools/log_collector.py
This commit is contained in:
commit
654fd18433
@ -43,6 +43,7 @@ Requires: python-cliff >= 1.13.0
|
||||
Requires: python-cliff-tablib >= 1.1
|
||||
Requires: python-jsonpickle >= 0.9.2
|
||||
Requires: python-oslo-i18n >= 2.5.0
|
||||
Requires: python-oslo-utils >= 2.4.0
|
||||
Requires: python-paramiko >= 1.15.1
|
||||
Requires: python-pbr >= 1.6.0
|
||||
Requires: python-six >= 1.9.0
|
||||
@ -50,6 +51,7 @@ Requires: PyYAML >= 3.10
|
||||
|
||||
Requires: /usr/bin/ssh-keygen
|
||||
|
||||
Conflicts: python-oslo-utils = 2.6.0
|
||||
|
||||
%description
|
||||
The KollaCLI simplifies OpenStack Kolla deployments.
|
||||
@ -154,6 +156,9 @@ esac
|
||||
|
||||
|
||||
%changelog
|
||||
* Fri Nov 06 2015 - Steve Noyes <steve.noyes@oracle.com>
|
||||
- add python-oslo-utils requirement
|
||||
|
||||
* Mon Oct 26 2015 - Steve Noyes <steve.noyes@oracle.com>
|
||||
- Remove obsolete json_generator
|
||||
|
||||
|
307
doc/source/api/reference/hosts.rst
Normal file
307
doc/source/api/reference/hosts.rst
Normal file
@ -0,0 +1,307 @@
|
||||
***********************
|
||||
HOSTS API - Reference
|
||||
***********************
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
This hosts documentation is work in progress and may change in near future.
|
||||
|
||||
|
||||
Hosts API
|
||||
===============
|
||||
|
||||
.. _get_hosts_all:
|
||||
|
||||
GET /v1/hosts
|
||||
##########################
|
||||
Retrieve all hosts in the inventory.
|
||||
|
||||
|
||||
Request/Response:
|
||||
************************************
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Request:
|
||||
|
||||
GET /v1/hosts
|
||||
Headers:
|
||||
X-Auth-Token: {token_id}
|
||||
|
||||
Response:
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
{
|
||||
"hosts":[
|
||||
{
|
||||
"hostname":{host_name1},
|
||||
"groups":[
|
||||
{group_name1},
|
||||
{group_name2},
|
||||
.....
|
||||
],
|
||||
},
|
||||
.....
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
HTTP Status Codes
|
||||
*****************
|
||||
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| Code | Description |
|
||||
+======+=============================================================================+
|
||||
| 200 | Successful request. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 401 | Missing or Invalid X-Auth-Token. Authentication required. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
|
||||
.. _get_host:
|
||||
|
||||
GET /v1/hosts/{host_name}
|
||||
##########################
|
||||
Retrieve the host for the given host name.
|
||||
|
||||
|
||||
Request/Response:
|
||||
************************************
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Request:
|
||||
|
||||
GET /v1/hosts/{host_name}
|
||||
Headers:
|
||||
X-Auth-Token: {token_id}
|
||||
|
||||
Response:
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
{
|
||||
"hostname":{host_name},
|
||||
"groups":[
|
||||
{group_name1},
|
||||
{group_name2},
|
||||
],
|
||||
.....
|
||||
}
|
||||
|
||||
|
||||
HTTP Status Codes
|
||||
*****************
|
||||
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| Code | Description |
|
||||
+======+=============================================================================+
|
||||
| 200 | Successful request. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 401 | Missing or Invalid X-Auth-Token. Authentication required. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 404 | Host does not exist in inventory |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
|
||||
|
||||
.. _post_host:
|
||||
|
||||
POST /v1/hosts
|
||||
##########################
|
||||
Create new host.
|
||||
|
||||
This call is used to create a new host and add it to the inventory.
|
||||
|
||||
Request/Response (create or replace host):
|
||||
**************************************
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Request:
|
||||
|
||||
POST /v1/hosts/{host_name}
|
||||
Headers:
|
||||
Content-Type: application/json
|
||||
X-Auth-Token: {token_id}
|
||||
|
||||
{
|
||||
"hostname":{host_name},
|
||||
"groups": [
|
||||
{group_name1},
|
||||
{group_name2},
|
||||
.....
|
||||
],
|
||||
}
|
||||
|
||||
Response:
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
|
||||
HTTP Status Codes
|
||||
*****************
|
||||
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| Code | Description |
|
||||
+======+=============================================================================+
|
||||
| 200 | Successfully created host. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 400 | Bad Request. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 401 | Missing or Invalid X-Auth-Token. Authentication required. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 404 | Group does not exist in inventory |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 409 | Host already exists in inventory |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
|
||||
|
||||
.. _delete_host:
|
||||
|
||||
DELETE /v1/hosts/{host_name}
|
||||
##############################
|
||||
|
||||
Delete host from the inventory.
|
||||
|
||||
Request/Response:
|
||||
*****************
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
DELETE /v1/host/{host_name}
|
||||
Headers:
|
||||
X-Auth-Token: {token_id}
|
||||
|
||||
Response:
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
|
||||
HTTP Status Codes
|
||||
*****************
|
||||
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| Code | Description |
|
||||
+======+=============================================================================+
|
||||
| 200 | Successfully deleted host. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 401 | Missing or Invalid X-Auth-Token. Authentication required. |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
|
||||
.. _check_host:
|
||||
|
||||
POST /v1/hosts/{host_name}/actions
|
||||
##############################
|
||||
|
||||
Check verifies that the host has its ssh keys set up correctly (can be accessed without a
|
||||
password from the deploy host). If the host check failed, the reason will be provided in
|
||||
the response message.
|
||||
|
||||
Request/Response:
|
||||
*****************
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
POST /v1/hosts/{host_name}/actions
|
||||
Headers:
|
||||
Content-Type: application/json
|
||||
X-Auth-Token: {token_id}
|
||||
{
|
||||
"check": {
|
||||
"host-name": {host_name},
|
||||
}
|
||||
}
|
||||
|
||||
Response:
|
||||
*********
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
200 OK
|
||||
|
||||
{
|
||||
"message":{message_string}
|
||||
}
|
||||
|
||||
|
||||
HTTP Status Codes
|
||||
*****************
|
||||
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| Code | Description |
|
||||
+======+=============================================================================+
|
||||
| 200 | Host check was successful |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 400 | Bad Request |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 401 | Invalid X-Auth-Token or the token doesn't have permissions to this resource |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 404 | Host does not exist in inventory |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 405 | Host check failed |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
|
||||
.. _setup_host:
|
||||
|
||||
POST /v1/hosts/actions
|
||||
##############################
|
||||
|
||||
Host setup distributes the ssh keys into the appropriate directory/file on the host.
|
||||
This assumes docker has been installed and is running on the host. Setup can be done
|
||||
for a single host or multiple hosts.
|
||||
|
||||
If a single host is to be setup, the host-name and host-password attributes must be
|
||||
supplied. If multiple hosts are to be setup, the hosts-file-path must be
|
||||
supplied.
|
||||
|
||||
Either the host-name/password or hosts-file-path must be supplied. If both are supplied,
|
||||
then all the hosts specified will be setup.
|
||||
|
||||
If the host setup failed, the reason will be provided in
|
||||
the response message.
|
||||
|
||||
Request/Response:
|
||||
*****************
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
POST /v1/hosts/actions
|
||||
Headers:
|
||||
Content-Type: application/json
|
||||
X-Auth-Token: {token_id}
|
||||
|
||||
{
|
||||
"setup": {
|
||||
"host-name": {host_name},
|
||||
"host-password": {password},
|
||||
"hosts-file-path": {hosts_file_path}
|
||||
}
|
||||
}
|
||||
|
||||
Response:
|
||||
*********
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
200 OK
|
||||
|
||||
{
|
||||
"message":{message_string}
|
||||
}
|
||||
|
||||
|
||||
HTTP Status Codes
|
||||
*****************
|
||||
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| Code | Description |
|
||||
+======+=============================================================================+
|
||||
| 200 | Host setup was successful |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 400 | Bad Request |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 401 | Invalid X-Auth-Token or the token doesn't have permissions to this resource |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 404 | Host does not exist in inventory |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
| 405 | Host setup failed |
|
||||
+------+-----------------------------------------------------------------------------+
|
||||
|
101
kollacli/commands/deploy.py
Normal file
101
kollacli/commands/deploy.py
Normal file
@ -0,0 +1,101 @@
|
||||
# Copyright(c) 2015, 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
|
||||
import traceback
|
||||
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common.ansible.actions import deploy
|
||||
from kollacli.common.inventory import Inventory
|
||||
from kollacli.common.utils import convert_to_unicode
|
||||
from kollacli.exceptions import CommandError
|
||||
|
||||
from cliff.command import Command
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Deploy(Command):
|
||||
"""Deploy"""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Deploy, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help=u._('Deployment host list'))
|
||||
parser.add_argument('--groups', nargs='?',
|
||||
metavar='<group_list>',
|
||||
help=u._('Deployment group list'))
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help=u._('Deployment service list'))
|
||||
parser.add_argument('--serial', action='store_true',
|
||||
help=u._('Deploy serially'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
hosts = None
|
||||
groups = None
|
||||
services = None
|
||||
serial_flag = False
|
||||
verbose_level = self.app.options.verbose_level
|
||||
try:
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
hosts = convert_to_unicode(host_list).split(',')
|
||||
if parsed_args.groups:
|
||||
group_list = parsed_args.groups.strip()
|
||||
groups = convert_to_unicode(group_list).split(',')
|
||||
if parsed_args.services:
|
||||
service_list = parsed_args.services.strip()
|
||||
services = convert_to_unicode(service_list).split(',')
|
||||
if parsed_args.serial:
|
||||
serial_flag = True
|
||||
|
||||
deploy(hosts, groups, services, serial_flag,
|
||||
verbose_level)
|
||||
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class Setdeploy(Command):
|
||||
"""Set deploy mode
|
||||
|
||||
Set deploy mode to either local or remote. Local indicates
|
||||
that the openstack deployment will be to the local host.
|
||||
Remote means that the deployment is on remote hosts.
|
||||
"""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Setdeploy, self).get_parser(prog_name)
|
||||
parser.add_argument('mode', metavar='<mode>',
|
||||
help=u._('mode=<local, remote>'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
mode = parsed_args.mode.strip()
|
||||
remote_flag = False
|
||||
if mode == 'remote':
|
||||
remote_flag = True
|
||||
elif mode != 'local':
|
||||
raise CommandError(
|
||||
u._('Invalid deploy mode. Mode must be '
|
||||
'either "local" or "remote".'))
|
||||
inventory = Inventory.load()
|
||||
inventory.set_deploy_mode(remote_flag)
|
||||
Inventory.save(inventory)
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
@ -11,12 +11,13 @@
|
||||
# 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 traceback
|
||||
|
||||
from kollacli.ansible.inventory import Inventory
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common.inventory import Inventory
|
||||
from kollacli.common.utils import convert_to_unicode
|
||||
from kollacli.exceptions import CommandError
|
||||
from kollacli import utils
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
@ -24,18 +25,16 @@ from cliff.lister import Lister
|
||||
|
||||
class GroupAdd(Command):
|
||||
"""Add group to open-stack-kolla"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupAdd, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help='group')
|
||||
help=u._('Group name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
groupname = utils.convert_to_unicode(groupname)
|
||||
groupname = convert_to_unicode(groupname)
|
||||
|
||||
inventory = Inventory.load()
|
||||
inventory.add_group(groupname)
|
||||
@ -49,18 +48,16 @@ class GroupAdd(Command):
|
||||
class GroupRemove(Command):
|
||||
"""Remove group from openstack-kolla"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupRemove, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help='group name')
|
||||
help=u._('Group name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
groupname = utils.convert_to_unicode(groupname)
|
||||
groupname = convert_to_unicode(groupname)
|
||||
inventory = Inventory.load()
|
||||
inventory.remove_group(groupname)
|
||||
Inventory.save(inventory)
|
||||
@ -72,22 +69,20 @@ class GroupRemove(Command):
|
||||
|
||||
class GroupAddhost(Command):
|
||||
"""Add host to group"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupAddhost, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help='group')
|
||||
help=u._('Group name'))
|
||||
parser.add_argument('hostname', metavar='<hostname>',
|
||||
help='host')
|
||||
help=u._('Host name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
groupname = utils.convert_to_unicode(groupname)
|
||||
groupname = convert_to_unicode(groupname)
|
||||
hostname = parsed_args.hostname.strip()
|
||||
hostname = utils.convert_to_unicode(hostname)
|
||||
hostname = convert_to_unicode(hostname)
|
||||
inventory = Inventory.load()
|
||||
inventory.add_host(hostname, groupname)
|
||||
Inventory.save(inventory)
|
||||
@ -100,22 +95,20 @@ class GroupAddhost(Command):
|
||||
class GroupRemovehost(Command):
|
||||
"""Remove host group from group"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupRemovehost, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help='group')
|
||||
help=u._('Group name'))
|
||||
parser.add_argument('hostname', metavar='<hostname>',
|
||||
help='host')
|
||||
help=u._('Host name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
groupname = utils.convert_to_unicode(groupname)
|
||||
groupname = convert_to_unicode(groupname)
|
||||
hostname = parsed_args.hostname.strip()
|
||||
hostname = utils.convert_to_unicode(hostname)
|
||||
hostname = convert_to_unicode(hostname)
|
||||
|
||||
inventory = Inventory.load()
|
||||
inventory.remove_host(hostname, groupname)
|
||||
@ -129,8 +122,6 @@ class GroupRemovehost(Command):
|
||||
class GroupListhosts(Lister):
|
||||
"""List all groups and their hosts"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
inventory = Inventory.load()
|
||||
@ -142,7 +133,7 @@ class GroupListhosts(Lister):
|
||||
data.append((groupname, hostnames))
|
||||
else:
|
||||
data.append(('', ''))
|
||||
return (('Group', 'Hosts'), sorted(data))
|
||||
return ((u._('Group'), u._('Hosts')), sorted(data))
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
@ -151,22 +142,20 @@ class GroupListhosts(Lister):
|
||||
|
||||
class GroupAddservice(Command):
|
||||
"""Add service to group"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupAddservice, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help='group')
|
||||
help=u._('Group name'))
|
||||
parser.add_argument('servicename', metavar='<servicename>',
|
||||
help='service')
|
||||
help=u._('Service name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
groupname = utils.convert_to_unicode(groupname)
|
||||
groupname = convert_to_unicode(groupname)
|
||||
servicename = parsed_args.servicename.strip()
|
||||
servicename = utils.convert_to_unicode(servicename)
|
||||
servicename = convert_to_unicode(servicename)
|
||||
|
||||
inventory = Inventory.load()
|
||||
inventory.add_group_to_service(groupname, servicename)
|
||||
@ -180,22 +169,20 @@ class GroupAddservice(Command):
|
||||
class GroupRemoveservice(Command):
|
||||
"""Remove service group from group"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GroupRemoveservice, self).get_parser(prog_name)
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help='group')
|
||||
help=u._('Group name'))
|
||||
parser.add_argument('servicename', metavar='<servicename>',
|
||||
help='service')
|
||||
help=u._('Service name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
groupname = utils.convert_to_unicode(groupname)
|
||||
groupname = convert_to_unicode(groupname)
|
||||
servicename = parsed_args.servicename.strip()
|
||||
servicename = utils.convert_to_unicode(servicename)
|
||||
servicename = convert_to_unicode(servicename)
|
||||
|
||||
inventory = Inventory.load()
|
||||
inventory.remove_group_from_service(groupname, servicename)
|
||||
@ -209,8 +196,6 @@ class GroupRemoveservice(Command):
|
||||
class GroupListservices(Lister):
|
||||
"""List all groups and their services"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
inventory = Inventory.load()
|
||||
@ -222,7 +207,7 @@ class GroupListservices(Lister):
|
||||
data.append((groupname, sorted(servicenames)))
|
||||
else:
|
||||
data.append(('', ''))
|
||||
return (('Group', 'Services'), sorted(data))
|
||||
return ((u._('Group'), u._('Services')), sorted(data))
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception as e:
|
@ -16,41 +16,41 @@ import getpass
|
||||
import logging
|
||||
import os
|
||||
import traceback
|
||||
import utils
|
||||
import yaml
|
||||
|
||||
from kollacli.ansible.inventory import Inventory
|
||||
from kollacli.ansible.playbook import AnsiblePlaybook
|
||||
from kollacli.ansible import properties
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common.ansible.actions import destroy_hosts
|
||||
from kollacli.common.inventory import Inventory
|
||||
from kollacli.common.utils import convert_to_unicode
|
||||
from kollacli.common.utils import get_setup_user
|
||||
from kollacli.exceptions import CommandError
|
||||
from kollacli.utils import convert_to_unicode
|
||||
from kollacli.utils import get_kollacli_home
|
||||
from kollacli.utils import get_setup_user
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
def _host_not_found(log, hostname):
|
||||
|
||||
def _host_not_found(hostname):
|
||||
raise CommandError(
|
||||
'Host (%s) not found. ' % hostname +
|
||||
'Please add it with "host add"')
|
||||
u._('Host ({host}) not found. Please add it with "host add".')
|
||||
.format(host=hostname))
|
||||
|
||||
|
||||
class HostAdd(Command):
|
||||
"""Add host to open-stack-kolla"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostAdd, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', metavar='<hostname>',
|
||||
help='host name or ip address')
|
||||
help=u._('Host name or ip address'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
hostname = parsed_args.hostname.strip()
|
||||
hostname = utils.convert_to_unicode(hostname)
|
||||
hostname = convert_to_unicode(hostname)
|
||||
|
||||
inventory = Inventory.load()
|
||||
inventory.add_host(hostname)
|
||||
@ -67,16 +67,15 @@ class HostDestroy(Command):
|
||||
Stops and removes all kolla related docker containers on either the
|
||||
specified host or if no host is specified, on all hosts.
|
||||
"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostDestroy, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', metavar='<hostname | all>',
|
||||
help='host name or ip address or "all"')
|
||||
help=u._('Host name or ip address or "all"'))
|
||||
parser.add_argument('--stop', action='store_true',
|
||||
help='stop rather than kill')
|
||||
help=u._('Stop rather than kill'))
|
||||
parser.add_argument('--includedata', action='store_true',
|
||||
help='destroy data containers')
|
||||
help=u._('Destroy data containers'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -85,36 +84,17 @@ class HostDestroy(Command):
|
||||
hostname = parsed_args.hostname.strip()
|
||||
hostname = convert_to_unicode(hostname)
|
||||
|
||||
if hostname != 'all':
|
||||
inventory = Inventory.load()
|
||||
host = inventory.get_host(hostname)
|
||||
if not host:
|
||||
_host_not_found(self.log, hostname)
|
||||
|
||||
destroy_type = 'kill'
|
||||
if parsed_args.stop:
|
||||
destroy_type = 'stop'
|
||||
playbook_name = 'host_destroy_no_data.yml'
|
||||
include_data = False
|
||||
if parsed_args.includedata:
|
||||
playbook_name = 'host_destroy.yml'
|
||||
include_data = True
|
||||
|
||||
verbose_level = self.app.options.verbose_level
|
||||
|
||||
destroy_hosts(hostname, destroy_type, verbose_level, include_data)
|
||||
|
||||
self.log.info('please be patient as this may take a while.')
|
||||
ansible_properties = properties.AnsibleProperties()
|
||||
base_distro = \
|
||||
ansible_properties.get_property('kolla_base_distro')
|
||||
install_type = \
|
||||
ansible_properties.get_property('kolla_install_type')
|
||||
container_prefix = base_distro + '-' + install_type
|
||||
kollacli_home = get_kollacli_home()
|
||||
playbook = AnsiblePlaybook()
|
||||
playbook.playbook_path = os.path.join(kollacli_home,
|
||||
'ansible/' + playbook_name)
|
||||
playbook.extra_vars = 'hosts=' + hostname + \
|
||||
' prefix=' + container_prefix + \
|
||||
' destroy_type=' + destroy_type
|
||||
playbook.print_output = False
|
||||
playbook.verbose_level = self.app.options.verbose_level
|
||||
playbook.run()
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
@ -124,17 +104,16 @@ class HostDestroy(Command):
|
||||
class HostRemove(Command):
|
||||
"""Remove host from openstack-kolla"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostRemove, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', metavar='<hostname>', help='host name')
|
||||
parser.add_argument('hostname', metavar='<hostname>',
|
||||
help=u._('Host name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
hostname = parsed_args.hostname.strip()
|
||||
hostname = utils.convert_to_unicode(hostname)
|
||||
hostname = convert_to_unicode(hostname)
|
||||
inventory = Inventory.load()
|
||||
inventory.remove_host(hostname)
|
||||
Inventory.save(inventory)
|
||||
@ -150,12 +129,10 @@ class HostList(Lister):
|
||||
If a hostname is provided, only list information about that host.
|
||||
"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostList, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', nargs='?', metavar='[hostname]',
|
||||
help='hostname')
|
||||
help=u._('Host name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -163,14 +140,14 @@ class HostList(Lister):
|
||||
hostname = None
|
||||
if parsed_args.hostname:
|
||||
hostname = parsed_args.hostname.strip()
|
||||
hostname = utils.convert_to_unicode(hostname)
|
||||
hostname = convert_to_unicode(hostname)
|
||||
|
||||
inventory = Inventory.load()
|
||||
|
||||
if hostname:
|
||||
host = inventory.get_host(hostname)
|
||||
if not host:
|
||||
_host_not_found(self.log, hostname)
|
||||
_host_not_found(hostname)
|
||||
|
||||
data = []
|
||||
host_groups = inventory.get_host_groups()
|
||||
@ -182,7 +159,7 @@ class HostList(Lister):
|
||||
data.append((hostname, groupnames))
|
||||
else:
|
||||
data.append(('', ''))
|
||||
return (('Host', 'Groups'), sorted(data))
|
||||
return ((u._('Host'), u._('Groups')), sorted(data))
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
@ -192,20 +169,19 @@ class HostList(Lister):
|
||||
class HostCheck(Command):
|
||||
"""Check if openstack-kollacli is setup"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostCheck, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', metavar='<hostname>', help='hostname')
|
||||
parser.add_argument('hostname', metavar='<hostname>',
|
||||
help=u._('Host name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
hostname = parsed_args.hostname.strip()
|
||||
hostname = utils.convert_to_unicode(hostname)
|
||||
hostname = convert_to_unicode(hostname)
|
||||
inventory = Inventory.load()
|
||||
if not inventory.get_host(hostname):
|
||||
_host_not_found(self.log, hostname)
|
||||
_host_not_found(hostname)
|
||||
|
||||
inventory.check_host(hostname)
|
||||
except CommandError as e:
|
||||
@ -217,26 +193,25 @@ class HostCheck(Command):
|
||||
class HostSetup(Command):
|
||||
"""Setup openstack-kollacli on host"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(HostSetup, self).get_parser(prog_name)
|
||||
parser.add_argument('hostname', nargs='?',
|
||||
metavar='<hostname>', help='hostname')
|
||||
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='hosts info file absolute path')
|
||||
help=u._('Absolute path to hosts info file '))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
if not parsed_args.hostname and not parsed_args.file:
|
||||
raise CommandError('Hostname or hosts info file path ' +
|
||||
'is required')
|
||||
raise CommandError(
|
||||
u._('Host name or hosts info file path is required.'))
|
||||
if parsed_args.hostname and parsed_args.file:
|
||||
raise CommandError('Hostname and hosts info file path ' +
|
||||
'cannot both be present')
|
||||
raise CommandError(
|
||||
u._('Host name and hosts info file path '
|
||||
'cannot both be present.'))
|
||||
inventory = Inventory.load()
|
||||
|
||||
if parsed_args.file:
|
||||
@ -246,23 +221,25 @@ class HostSetup(Command):
|
||||
else:
|
||||
# single host setup
|
||||
hostname = parsed_args.hostname.strip()
|
||||
hostname = utils.convert_to_unicode(hostname)
|
||||
hostname = convert_to_unicode(hostname)
|
||||
if not inventory.get_host(hostname):
|
||||
_host_not_found(self.log, hostname)
|
||||
_host_not_found(hostname)
|
||||
|
||||
check_ok = inventory.check_host(hostname, True)
|
||||
if check_ok:
|
||||
self.log.info('Skipping setup of host (%s) as check is ok'
|
||||
% hostname)
|
||||
LOG.info(
|
||||
u._LI('Skipping setup of host ({host}) as '
|
||||
'check is ok.').format(host=hostname))
|
||||
return True
|
||||
|
||||
if parsed_args.insecure:
|
||||
password = parsed_args.insecure.strip()
|
||||
else:
|
||||
setup_user = get_setup_user()
|
||||
password = getpass.getpass('%s password for %s: ' %
|
||||
(setup_user, hostname))
|
||||
password = utils.convert_to_unicode(password)
|
||||
password = getpass.getpass(
|
||||
u._('{user} password for {host}: ')
|
||||
.format(user=setup_user, host=hostname))
|
||||
password = convert_to_unicode(password)
|
||||
inventory.setup_host(hostname, password)
|
||||
|
||||
except CommandError as e:
|
||||
@ -272,13 +249,14 @@ class HostSetup(Command):
|
||||
|
||||
def get_yml_data(self, yml_path):
|
||||
if not os.path.isfile(yml_path):
|
||||
raise CommandError('No file exists at %s. ' % yml_path +
|
||||
'An absolute file path is required.')
|
||||
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.load(file_data)
|
||||
hosts_info = yaml.safe_load(file_data)
|
||||
if not hosts_info:
|
||||
raise CommandError('%s is empty' % yml_path)
|
||||
raise CommandError(u._('{path} is empty.').format(path=yml_path))
|
||||
return hosts_info
|
@ -13,26 +13,25 @@
|
||||
# under the License.
|
||||
import argparse
|
||||
import getpass
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
import kollacli.i18n as u
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
|
||||
from kollacli.ansible.passwords import clear_password
|
||||
from kollacli.ansible.passwords import get_password_names
|
||||
from kollacli.ansible.passwords import set_password
|
||||
from kollacli.common.passwords import clear_password
|
||||
from kollacli.common.passwords import get_password_names
|
||||
from kollacli.common.passwords import set_password
|
||||
|
||||
|
||||
class PasswordSet(Command):
|
||||
"Password Set"
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PasswordSet, self).get_parser(prog_name)
|
||||
parser.add_argument('passwordname', metavar='<passwordname>',
|
||||
help='passwordname')
|
||||
help=u._('Password name'))
|
||||
parser.add_argument('--insecure', nargs='?', help=argparse.SUPPRESS)
|
||||
return parser
|
||||
|
||||
@ -42,7 +41,7 @@ class PasswordSet(Command):
|
||||
if parsed_args.insecure:
|
||||
password = parsed_args.insecure.strip()
|
||||
else:
|
||||
password = getpass.getpass('Password: ').strip()
|
||||
password = getpass.getpass(u._('Password: ')).strip()
|
||||
|
||||
set_password(password_name, password)
|
||||
|
||||
@ -53,12 +52,10 @@ class PasswordSet(Command):
|
||||
class PasswordClear(Command):
|
||||
"Password Clear"
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PasswordClear, self).get_parser(prog_name)
|
||||
parser.add_argument('passwordname', metavar='<passwordname>',
|
||||
help='passwordname')
|
||||
help=u._('Password name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -72,8 +69,6 @@ class PasswordClear(Command):
|
||||
class PasswordList(Lister):
|
||||
"""List all password names"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
password_names = get_password_names()
|
||||
password_names = sorted(password_names)
|
||||
@ -82,4 +77,4 @@ class PasswordList(Lister):
|
||||
for password_name in password_names:
|
||||
data.append((password_name, '-'))
|
||||
|
||||
return (('Password Name', 'Password'), data)
|
||||
return ((u._('Password Name'), u._('Password')), data)
|
@ -14,23 +14,26 @@
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from kollacli.ansible import properties
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common import properties
|
||||
from kollacli.common import utils
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PropertySet(Command):
|
||||
"Property Set"
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PropertySet, self).get_parser(prog_name)
|
||||
parser.add_argument('propertyname', metavar='<propertyname>',
|
||||
help='propertyname')
|
||||
help=u._('Property name'))
|
||||
parser.add_argument('propertyvalue', metavar='<propertyvalue',
|
||||
help='propertyvalue')
|
||||
help=u._('Property value'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -47,12 +50,10 @@ class PropertySet(Command):
|
||||
class PropertyClear(Command):
|
||||
"Property Clear"
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PropertyClear, self).get_parser(prog_name)
|
||||
parser.add_argument('propertyname', metavar='<propertyname>',
|
||||
help='propertyname')
|
||||
help=u._('Property name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -68,16 +69,52 @@ class PropertyClear(Command):
|
||||
class PropertyList(Lister):
|
||||
"""List all properties"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PropertyList, self).get_parser(prog_name)
|
||||
parser.add_argument('--all', action='store_true',
|
||||
help=u._('List all properties'))
|
||||
parser.add_argument('--long', action='store_true',
|
||||
help=u._('Show all property attributes'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ansible_properties = properties.AnsibleProperties()
|
||||
property_list = ansible_properties.get_all_unique()
|
||||
|
||||
list_all = False
|
||||
if parsed_args.all:
|
||||
list_all = True
|
||||
|
||||
list_long = False
|
||||
if parsed_args.long:
|
||||
list_long = True
|
||||
|
||||
property_length = utils.get_property_list_length()
|
||||
data = []
|
||||
if property_list:
|
||||
for value in property_list:
|
||||
data.append((value.name, value.value))
|
||||
else:
|
||||
data.append(('', ''))
|
||||
for prop in property_list:
|
||||
include_prop = False
|
||||
if (prop.value is not None and
|
||||
len(str(prop.value)) > property_length):
|
||||
if list_all:
|
||||
include_prop = True
|
||||
else:
|
||||
include_prop = True
|
||||
|
||||
return (('Property Name', 'Property Value'), data)
|
||||
if include_prop:
|
||||
if list_long:
|
||||
data.append((prop.name, prop.value, prop.overrides,
|
||||
prop.orig_value))
|
||||
else:
|
||||
data.append((prop.name, prop.value))
|
||||
else:
|
||||
if list_long:
|
||||
data.append(('', '', '', ''))
|
||||
else:
|
||||
data.append(('', ''))
|
||||
|
||||
if list_long:
|
||||
return ((u._('Property Name'), u._('Property Value'),
|
||||
u._('Overrides'), u._('Original Value')), data)
|
||||
else:
|
||||
return ((u._('Property Name'), u._('Property Value')), data)
|
@ -11,12 +11,13 @@
|
||||
# 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 traceback
|
||||
|
||||
from kollacli.ansible.inventory import Inventory
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common.inventory import Inventory
|
||||
from kollacli.common.utils import convert_to_unicode
|
||||
from kollacli.exceptions import CommandError
|
||||
from kollacli import utils
|
||||
|
||||
from cliff.command import Command
|
||||
from cliff.lister import Lister
|
||||
@ -28,22 +29,21 @@ class ServiceAddGroup(Command):
|
||||
Associated the service to a group. If this is a sub-service,
|
||||
the inherit flag will be cleared.
|
||||
"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ServiceAddGroup, self).get_parser(prog_name)
|
||||
parser.add_argument('servicename', metavar='<servicename>',
|
||||
help='service')
|
||||
help=u._('Service name'))
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help='group')
|
||||
help=u._('Group name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
groupname = utils.convert_to_unicode(groupname)
|
||||
groupname = convert_to_unicode(groupname)
|
||||
servicename = parsed_args.servicename.strip()
|
||||
servicename = utils.convert_to_unicode(servicename)
|
||||
servicename = convert_to_unicode(servicename)
|
||||
|
||||
inventory = Inventory.load()
|
||||
|
||||
@ -59,22 +59,20 @@ class ServiceAddGroup(Command):
|
||||
class ServiceRemoveGroup(Command):
|
||||
"""Remove group from service"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ServiceRemoveGroup, self).get_parser(prog_name)
|
||||
parser.add_argument('servicename', metavar='<servicename>',
|
||||
help='service')
|
||||
help=u._('Service name'))
|
||||
parser.add_argument('groupname', metavar='<groupname>',
|
||||
help='group')
|
||||
help=u._('Group name'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
groupname = parsed_args.groupname.strip()
|
||||
groupname = utils.convert_to_unicode(groupname)
|
||||
groupname = convert_to_unicode(groupname)
|
||||
servicename = parsed_args.servicename.strip()
|
||||
servicename = utils.convert_to_unicode(servicename)
|
||||
servicename = convert_to_unicode(servicename)
|
||||
|
||||
inventory = Inventory.load()
|
||||
|
||||
@ -90,8 +88,6 @@ class ServiceRemoveGroup(Command):
|
||||
class ServiceListGroups(Lister):
|
||||
"""List services and their groups"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
inventory = Inventory.load()
|
||||
@ -109,7 +105,8 @@ class ServiceListGroups(Lister):
|
||||
data.append((servicename, groupnames, inh_str))
|
||||
else:
|
||||
data.append(('', ''))
|
||||
return (('Service', 'Groups', 'Inherited'), sorted(data))
|
||||
return ((u._('Service'), u._('Groups'), u._('Inherited')),
|
||||
sorted(data))
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
@ -119,8 +116,6 @@ class ServiceListGroups(Lister):
|
||||
class ServiceList(Lister):
|
||||
"""List services and their sub-services"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
inventory = Inventory.load()
|
||||
@ -132,7 +127,7 @@ class ServiceList(Lister):
|
||||
data.append((servicename, sub_svcname))
|
||||
else:
|
||||
data.append(('', ''))
|
||||
return (('Service', 'Sub-Services'), sorted(data))
|
||||
return ((u._('Service'), u._('Sub-Services')), sorted(data))
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception as e:
|
27
kollacli/commands/support.py
Normal file
27
kollacli/commands/support.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright(c) 2015, 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.
|
||||
from kollacli.common.support import dump
|
||||
|
||||
from cliff.command import Command
|
||||
|
||||
|
||||
class Dump(Command):
|
||||
"""Dumps configuration data for debugging
|
||||
|
||||
Dumps most files in /etc/kolla and /usr/share/kolla into a
|
||||
tar file so be given to support / development to help with
|
||||
debugging problems.
|
||||
"""
|
||||
def take_action(self, parsed_args):
|
||||
dump()
|
@ -1,228 +0,0 @@
|
||||
# Copyright(c) 2015, 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
|
||||
import os
|
||||
import tarfile
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
from kollacli.ansible.inventory import Inventory
|
||||
from kollacli.ansible.playbook import AnsiblePlaybook
|
||||
from kollacli.ansible.properties import AnsibleProperties
|
||||
from kollacli.exceptions import CommandError
|
||||
from kollacli.utils import convert_to_unicode
|
||||
from kollacli.utils import get_kolla_etc
|
||||
from kollacli.utils import get_kolla_home
|
||||
from kollacli.utils import get_kolla_log_dir
|
||||
from kollacli.utils import get_kollacli_etc
|
||||
from kollacli.utils import run_cmd
|
||||
|
||||
from cliff.command import Command
|
||||
|
||||
|
||||
class Deploy(Command):
|
||||
"""Deploy"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Deploy, self).get_parser(prog_name)
|
||||
parser.add_argument('--hosts', nargs='?',
|
||||
metavar='<host_list>',
|
||||
help='deployment host list')
|
||||
parser.add_argument('--groups', nargs='?',
|
||||
metavar='<group_list>',
|
||||
help='deployment group list')
|
||||
parser.add_argument('--services', nargs='?',
|
||||
metavar='<service_list>',
|
||||
help='deployment service list')
|
||||
parser.add_argument('--serial', action='store_true',
|
||||
help='deploy serially')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
if parsed_args.hosts and parsed_args.groups:
|
||||
raise CommandError('Hosts and Groups arguments cannot both ' +
|
||||
'be present at the same time.')
|
||||
|
||||
self._run_rules()
|
||||
|
||||
playbook = AnsiblePlaybook()
|
||||
kolla_home = get_kolla_home()
|
||||
playbook.playbook_path = os.path.join(kolla_home,
|
||||
'ansible/site.yml')
|
||||
if parsed_args.hosts:
|
||||
host_list = parsed_args.hosts.strip()
|
||||
host_list = convert_to_unicode(host_list)
|
||||
playbook.hosts = host_list.split(',')
|
||||
if parsed_args.groups:
|
||||
group_list = parsed_args.groups.strip()
|
||||
group_list = convert_to_unicode(group_list)
|
||||
playbook.groups = group_list.split(',')
|
||||
if parsed_args.services:
|
||||
tag_list = parsed_args.services.strip()
|
||||
tag_list = convert_to_unicode(tag_list)
|
||||
playbook.services = tag_list.split(',')
|
||||
if parsed_args.serial:
|
||||
playbook.serial = True
|
||||
|
||||
playbook.verbose_level = self.app.options.verbose_level
|
||||
playbook.run()
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
def _run_rules(self):
|
||||
# check that ring files are in /etc/kolla/config/swift if
|
||||
# swift is enabled
|
||||
expected_files = ['account.ring.gz',
|
||||
'container.ring.gz',
|
||||
'object.ring.gz']
|
||||
properties = AnsibleProperties()
|
||||
is_enabled = properties.get_property('enable_swift')
|
||||
if is_enabled == 'yes':
|
||||
path_pre = os.path.join(get_kolla_etc(), 'config', 'swift')
|
||||
for expected_file in expected_files:
|
||||
path = os.path.join(path_pre, expected_file)
|
||||
if not os.path.isfile(path):
|
||||
msg = ('Deploy failed. ' +
|
||||
'Swift is enabled but ring buffers have ' +
|
||||
'not yet been set up. Please see the ' +
|
||||
'documentation for swift configuration ' +
|
||||
'instructions.')
|
||||
raise CommandError(msg)
|
||||
|
||||
|
||||
class Dump(Command):
|
||||
"""Dumps configuration data for debugging
|
||||
|
||||
Dumps most files in /etc/kolla and /usr/share/kolla into a
|
||||
tar file so be given to support / development to help with
|
||||
debugging problems.
|
||||
"""
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
kolla_home = get_kolla_home()
|
||||
kolla_logs = get_kolla_log_dir()
|
||||
kolla_ansible = os.path.join(kolla_home, 'ansible')
|
||||
kolla_docs = os.path.join(kolla_home, 'docs')
|
||||
kolla_etc = get_kolla_etc()
|
||||
kolla_config = os.path.join(kolla_etc, 'config')
|
||||
kolla_globals = os.path.join(kolla_etc, 'globals.yml')
|
||||
kollacli_etc = get_kollacli_etc().rstrip('/')
|
||||
ketc = 'kolla/etc/'
|
||||
kshare = 'kolla/share/'
|
||||
fd, dump_path = tempfile.mkstemp(prefix='kollacli_dump_',
|
||||
suffix='.tgz')
|
||||
os.close(fd) # avoid fd leak
|
||||
with tarfile.open(dump_path, 'w:gz') as tar:
|
||||
# Can't blanket add kolla_home because the .ssh dir is
|
||||
# accessible by the kolla user only (not kolla group)
|
||||
tar.add(kolla_ansible,
|
||||
arcname=kshare + os.path.basename(kolla_ansible))
|
||||
tar.add(kolla_docs,
|
||||
arcname=kshare + os.path.basename(kolla_docs))
|
||||
|
||||
# Can't blanket add kolla_etc because the passwords.yml
|
||||
# file is accessible by the kolla user only (not kolla group)
|
||||
tar.add(kolla_config,
|
||||
arcname=ketc + os.path.basename(kolla_config))
|
||||
tar.add(kolla_globals,
|
||||
arcname=ketc + os.path.basename(kolla_globals))
|
||||
tar.add(kollacli_etc,
|
||||
arcname=ketc + os.path.basename(kollacli_etc))
|
||||
|
||||
# add kolla log files
|
||||
if os.path.isdir(kolla_logs):
|
||||
tar.add(kolla_logs)
|
||||
|
||||
# add output of various commands
|
||||
self._add_cmd_info(tar)
|
||||
|
||||
self.log.info('dump successful to %s' % dump_path)
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
def _add_cmd_info(self, tar):
|
||||
# run all the kollacli list commands
|
||||
cmds = ['kollacli service listgroups',
|
||||
'kollacli service list',
|
||||
'kollacli group listservices',
|
||||
'kollacli group listhosts',
|
||||
'kollacli host list',
|
||||
'kollacli property list',
|
||||
'kollacli password list']
|
||||
|
||||
# collect the json inventory output
|
||||
inventory = Inventory.load()
|
||||
inv_path = inventory.create_json_gen_file()
|
||||
cmds.append(inv_path)
|
||||
|
||||
try:
|
||||
fd, path = tempfile.mkstemp(suffix='.tmp')
|
||||
os.close(fd)
|
||||
with open(path, 'w') as tmp_file:
|
||||
for cmd in cmds:
|
||||
err_msg, output = run_cmd(cmd, False)
|
||||
tmp_file.write('\n\n$ %s\n' % cmd)
|
||||
if err_msg:
|
||||
tmp_file.write('Error message: %s\n' % err_msg)
|
||||
lines = output.split('\n')
|
||||
for line in lines:
|
||||
tmp_file.write(line + '\n')
|
||||
tar.add(path, arcname=os.path.join('kolla', 'cmds_output'))
|
||||
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
if path:
|
||||
os.remove(path)
|
||||
if inv_path:
|
||||
os.remove(inv_path)
|
||||
return
|
||||
|
||||
|
||||
class Setdeploy(Command):
|
||||
"""Set deploy mode
|
||||
|
||||
Set deploy mode to either local or remote. Local indicates
|
||||
that the openstack deployment will be to the local host.
|
||||
Remote means that the deployment is on remote hosts.
|
||||
"""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Setdeploy, self).get_parser(prog_name)
|
||||
parser.add_argument('mode', metavar='<mode>',
|
||||
help='mode=<local, remote>')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
mode = parsed_args.mode.strip()
|
||||
remote_flag = False
|
||||
if mode == 'remote':
|
||||
remote_flag = True
|
||||
elif mode != 'local':
|
||||
raise CommandError('Invalid deploy mode. Mode must be ' +
|
||||
'either "local" or "remote"')
|
||||
inventory = Inventory.load()
|
||||
inventory.set_deploy_mode(remote_flag)
|
||||
Inventory.save(inventory)
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
0
kollacli/common/__init__.py
Normal file
0
kollacli/common/__init__.py
Normal file
0
kollacli/common/ansible/__init__.py
Normal file
0
kollacli/common/ansible/__init__.py
Normal file
108
kollacli/common/ansible/actions.py
Normal file
108
kollacli/common/ansible/actions.py
Normal file
@ -0,0 +1,108 @@
|
||||
# Copyright(c) 2015, 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
|
||||
import os
|
||||
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common.ansible.playbook import AnsiblePlaybook
|
||||
from kollacli.common import properties
|
||||
from kollacli.common.properties import AnsibleProperties
|
||||
from kollacli.common.utils import get_kolla_etc
|
||||
from kollacli.common.utils import get_kolla_home
|
||||
from kollacli.common.utils import get_kollacli_home
|
||||
from kollacli.exceptions import CommandError
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def destroy_hosts(hostname, destroy_type, verbose_level=1, include_data=False):
|
||||
'''destroy containers on a host (or all hosts).
|
||||
|
||||
If hostname == 'all', then containers on all hosts will be
|
||||
stopped. Otherwise, the containers on the specified host
|
||||
will be stopped.
|
||||
|
||||
The destroy type can either be 'stop' or 'kill'.
|
||||
'''
|
||||
if destroy_type not in ['stop', 'kill']:
|
||||
raise CommandError(
|
||||
u._('Invalid destroy type ({type}). Must be either '
|
||||
'"stop" or "kill".').format(type=destroy_type))
|
||||
|
||||
playbook_name = 'host_destroy_no_data.yml'
|
||||
if include_data:
|
||||
playbook_name = 'host_destroy.yml'
|
||||
|
||||
LOG.info(u._LI('Please be patient as this may take a while.'))
|
||||
ansible_properties = properties.AnsibleProperties()
|
||||
base_distro = \
|
||||
ansible_properties.get_property('kolla_base_distro')
|
||||
install_type = \
|
||||
ansible_properties.get_property('kolla_install_type')
|
||||
container_prefix = base_distro + '-' + install_type
|
||||
kollacli_home = get_kollacli_home()
|
||||
playbook = AnsiblePlaybook()
|
||||
playbook.playbook_path = os.path.join(kollacli_home,
|
||||
'ansible/' + playbook_name)
|
||||
playbook.extra_vars = 'hosts=' + hostname + \
|
||||
' prefix=' + container_prefix + \
|
||||
' destroy_type=' + destroy_type
|
||||
playbook.print_output = False
|
||||
playbook.verbose_level = verbose_level
|
||||
playbook.run()
|
||||
|
||||
|
||||
def deploy(hostnames=[], groupnames=[], servicenames=[],
|
||||
serial_flag=False, verbose_level=1):
|
||||
if hostnames and groupnames:
|
||||
raise CommandError(
|
||||
u._('Hosts and Groups arguments cannot '
|
||||
'both be present at the same time.'))
|
||||
|
||||
_run_deploy_rules()
|
||||
|
||||
playbook = AnsiblePlaybook()
|
||||
kolla_home = get_kolla_home()
|
||||
playbook.playbook_path = os.path.join(kolla_home,
|
||||
'ansible/site.yml')
|
||||
playbook.hosts = hostnames
|
||||
playbook.groups = groupnames
|
||||
playbook.services = servicenames
|
||||
playbook.serial = serial_flag
|
||||
|
||||
playbook.verbose_level = verbose_level
|
||||
playbook.run()
|
||||
|
||||
|
||||
def _run_deploy_rules():
|
||||
# check that ring files are in /etc/kolla/config/swift if
|
||||
# swift is enabled
|
||||
expected_files = ['account.ring.gz',
|
||||
'container.ring.gz',
|
||||
'object.ring.gz']
|
||||
properties = AnsibleProperties()
|
||||
is_enabled = properties.get_property('enable_swift')
|
||||
if is_enabled == 'yes':
|
||||
path_pre = os.path.join(get_kolla_etc(), 'config', 'swift')
|
||||
for expected_file in expected_files:
|
||||
path = os.path.join(path_pre, expected_file)
|
||||
if not os.path.isfile(path):
|
||||
msg = u._(
|
||||
'Deploy failed. '
|
||||
'Swift is enabled but ring buffers have '
|
||||
'not yet been set up. Please see the '
|
||||
'documentation for swift configuration '
|
||||
'instructions.')
|
||||
raise CommandError(msg)
|
@ -13,14 +13,18 @@
|
||||
# under the License.
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import subprocess # nosec
|
||||
import traceback
|
||||
|
||||
from kollacli.ansible.inventory import Inventory
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common.utils import get_admin_user
|
||||
from kollacli.common.utils import get_ansible_command
|
||||
from kollacli.common.utils import get_kolla_etc
|
||||
from kollacli.common.utils import run_cmd
|
||||
from kollacli.exceptions import CommandError
|
||||
from kollacli.utils import get_admin_user
|
||||
from kollacli.utils import get_kolla_etc
|
||||
from kollacli.utils import run_cmd
|
||||
|
||||
from kollacli.common.inventory import Inventory
|
||||
|
||||
|
||||
class AnsiblePlaybook(object):
|
||||
@ -49,27 +53,29 @@ class AnsiblePlaybook(object):
|
||||
if self.verbose_level > 1:
|
||||
flag = '-vvv'
|
||||
|
||||
ansible_cmd = get_ansible_command(playbook=True)
|
||||
admin_user = get_admin_user()
|
||||
command_string = ('/usr/bin/sudo -u %s ansible-playbook %s'
|
||||
% (admin_user, flag))
|
||||
inventory = Inventory.load()
|
||||
command_string = ('/usr/bin/sudo -u %s %s %s'
|
||||
% (admin_user, ansible_cmd, flag))
|
||||
inventory_filter = {}
|
||||
inventory = Inventory.load()
|
||||
if self.hosts:
|
||||
for hostname in self.hosts:
|
||||
host = inventory.get_host(hostname)
|
||||
if not host:
|
||||
raise CommandError(
|
||||
'Host (%s) not found. ' % hostname)
|
||||
raise CommandError(u._('Host ({host}) not found.')
|
||||
.format(host=hostname))
|
||||
inventory_filter['deploy_hosts'] = self.hosts
|
||||
elif self.groups:
|
||||
for groupname in self.groups:
|
||||
group = inventory.get_group(groupname)
|
||||
if not group:
|
||||
raise CommandError(
|
||||
'Group (%s) not found. ' % groupname)
|
||||
raise CommandError(u._('Group ({group}) not found.')
|
||||
.format(group=groupname))
|
||||
inventory_filter['deploy_groups'] = self.groups
|
||||
|
||||
inventory_path = inventory.create_json_gen_file(inventory_filter)
|
||||
inventory_path = \
|
||||
inventory.create_json_gen_file(inventory_filter)
|
||||
inventory_string = '-i ' + inventory_path
|
||||
cmd = (command_string + ' ' + inventory_string)
|
||||
|
||||
@ -101,8 +107,8 @@ class AnsiblePlaybook(object):
|
||||
for service in self.services:
|
||||
valid_service = inventory.get_service(service)
|
||||
if not valid_service:
|
||||
raise CommandError(
|
||||
'Service (%s) not found. ' % service)
|
||||
raise CommandError(u._('Service ({srvc}) not found.')
|
||||
.format(srvc=service))
|
||||
if not first:
|
||||
service_string = service_string + ','
|
||||
else:
|
||||
@ -121,7 +127,7 @@ class AnsiblePlaybook(object):
|
||||
# log the inventory
|
||||
dbg_gen = inventory_path
|
||||
(inv, _) = \
|
||||
subprocess.Popen(dbg_gen.split(' '),
|
||||
subprocess.Popen(dbg_gen.split(' '), # nosec
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE).communicate()
|
||||
self.log.debug(inv)
|
||||
@ -134,7 +140,7 @@ class AnsiblePlaybook(object):
|
||||
err_msg = '%s %s' % (err_msg, output)
|
||||
raise CommandError(err_msg)
|
||||
|
||||
self.log.info('Success')
|
||||
self.log.info(u._('Success'))
|
||||
except CommandError as e:
|
||||
raise e
|
||||
except Exception:
|
@ -18,12 +18,17 @@ import os
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
from kollacli import exceptions
|
||||
from kollacli import utils
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common.sshutils import ssh_setup_host
|
||||
from kollacli.common.utils import get_admin_user
|
||||
from kollacli.common.utils import get_ansible_command
|
||||
from kollacli.common.utils import get_kollacli_etc
|
||||
from kollacli.common.utils import run_cmd
|
||||
from kollacli.common.utils import sync_read_file
|
||||
from kollacli.common.utils import sync_write_file
|
||||
|
||||
from kollacli.exceptions import CommandError
|
||||
from kollacli.sshutils import ssh_setup_host
|
||||
from kollacli.utils import get_admin_user
|
||||
|
||||
ANSIBLE_SSH_USER = 'ansible_ssh_user'
|
||||
ANSIBLE_CONNECTION = 'ansible_connection'
|
||||
@ -155,7 +160,7 @@ class HostGroup(object):
|
||||
self.set_var(ANSIBLE_BECOME, 'yes')
|
||||
if remote_flag:
|
||||
# set the ssh info for all the servers in the group
|
||||
self.set_var(ANSIBLE_SSH_USER, utils.get_admin_user())
|
||||
self.set_var(ANSIBLE_SSH_USER, get_admin_user())
|
||||
self.clear_var(ANSIBLE_CONNECTION)
|
||||
else:
|
||||
# remove ssh info, add local connection type
|
||||
@ -276,11 +281,11 @@ class Inventory(object):
|
||||
@staticmethod
|
||||
def load():
|
||||
"""load the inventory from a pickle file"""
|
||||
inventory_path = os.path.join(utils.get_kollacli_etc(), INVENTORY_PATH)
|
||||
inventory_path = os.path.join(get_kollacli_etc(), INVENTORY_PATH)
|
||||
data = ''
|
||||
try:
|
||||
if os.path.exists(inventory_path):
|
||||
data = utils.sync_read_file(inventory_path)
|
||||
data = sync_read_file(inventory_path)
|
||||
|
||||
if data.strip():
|
||||
inventory = jsonpickle.decode(data)
|
||||
@ -291,23 +296,26 @@ class Inventory(object):
|
||||
else:
|
||||
inventory = Inventory()
|
||||
except Exception:
|
||||
raise CommandError('loading inventory failed: %s'
|
||||
% traceback.format_exc())
|
||||
raise CommandError(
|
||||
u._('Loading inventory failed. : {error}')
|
||||
.format(error=traceback.format_exc()))
|
||||
return inventory
|
||||
|
||||
@staticmethod
|
||||
def save(inventory):
|
||||
"""Save the inventory in a pickle file"""
|
||||
inventory_path = os.path.join(utils.get_kollacli_etc(), INVENTORY_PATH)
|
||||
inventory_path = os.path.join(get_kollacli_etc(), INVENTORY_PATH)
|
||||
try:
|
||||
# multiple trips thru json to render a readable inventory file
|
||||
data = jsonpickle.encode(inventory)
|
||||
data_str = json.loads(data)
|
||||
pretty_data = json.dumps(data_str, indent=4)
|
||||
utils.sync_write_file(inventory_path, pretty_data)
|
||||
sync_write_file(inventory_path, pretty_data)
|
||||
|
||||
except Exception as e:
|
||||
raise CommandError('saving inventory failed: %s' % e)
|
||||
raise CommandError(
|
||||
u._('Saving inventory failed. : {error}')
|
||||
.format(error=str(e)))
|
||||
|
||||
def _create_default_inventory(self):
|
||||
|
||||
@ -334,7 +342,7 @@ class Inventory(object):
|
||||
return self._hosts.values()
|
||||
|
||||
def get_hostnames(self):
|
||||
return self._hosts.keys()
|
||||
return list(self._hosts.keys())
|
||||
|
||||
def get_host(self, hostname):
|
||||
host = None
|
||||
@ -349,16 +357,19 @@ class Inventory(object):
|
||||
if group name is not none, add host to group
|
||||
"""
|
||||
if groupname and groupname not in self._groups:
|
||||
raise CommandError('Group name (%s) does not exist'
|
||||
% groupname)
|
||||
raise CommandError(
|
||||
u._('Group name ({group}) does not exist.')
|
||||
.format(group=groupname))
|
||||
|
||||
if groupname and hostname not in self._hosts:
|
||||
raise CommandError('Host name (%s) does not exist'
|
||||
% hostname)
|
||||
raise CommandError(
|
||||
u._('Host name ({host}) does not exist.')
|
||||
.format(host=hostname))
|
||||
|
||||
if not groupname and not self.remote_mode and len(self._hosts) >= 1:
|
||||
raise CommandError('Cannot have more than one host when in ' +
|
||||
'local deploy mode')
|
||||
raise CommandError(
|
||||
u._('Cannot have more than one host when in local deploy '
|
||||
'mode.'))
|
||||
|
||||
# create new host if it doesn't exist
|
||||
host = Host(hostname)
|
||||
@ -379,8 +390,9 @@ class Inventory(object):
|
||||
if group name is not none, remove host from group
|
||||
"""
|
||||
if groupname and groupname not in self._groups:
|
||||
raise CommandError('Group name (%s) does not exist'
|
||||
% groupname)
|
||||
raise CommandError(
|
||||
u._('Group name ({group}) does not exist.')
|
||||
.format(group=groupname))
|
||||
|
||||
if hostname not in self._hosts:
|
||||
return
|
||||
@ -409,10 +421,10 @@ class Inventory(object):
|
||||
for hostname, host_info in hosts_info.items():
|
||||
host = self.get_host(hostname)
|
||||
if not host:
|
||||
failed_hosts[hostname] = "Host doesn't exist"
|
||||
failed_hosts[hostname] = u._("Host doesn't exist.")
|
||||
continue
|
||||
if not host_info or 'password' not in host_info:
|
||||
failed_hosts[hostname] = 'No password in yml file'
|
||||
failed_hosts[hostname] = u._('No password in yml file.')
|
||||
continue
|
||||
passwd = host_info['password']
|
||||
uname = None
|
||||
@ -426,27 +438,32 @@ class Inventory(object):
|
||||
summary = '\n'
|
||||
for hostname, err in failed_hosts.items():
|
||||
summary = summary + '- %s: %s\n' % (hostname, err)
|
||||
raise CommandError('Not all hosts were set up: %s' % summary)
|
||||
raise CommandError(
|
||||
u._('Not all hosts were set up. : {reasons}')
|
||||
.format(reasons=summary))
|
||||
else:
|
||||
self.log.info('All hosts were successfully set up')
|
||||
self.log.info(u._LI('All hosts were successfully set up.'))
|
||||
|
||||
def setup_host(self, hostname, password, uname=None):
|
||||
try:
|
||||
self.log.info('Starting setup of host (%s)'
|
||||
% hostname)
|
||||
self.log.info(
|
||||
u._LI('Starting setup of host ({host}).')
|
||||
.format(host=hostname))
|
||||
ssh_setup_host(hostname, password, uname)
|
||||
check_ok = self.check_host(hostname, True)
|
||||
if not check_ok:
|
||||
raise Exception('Post setup check failed')
|
||||
self.log.info('Host (%s) setup succeeded' % hostname)
|
||||
raise Exception(u._('Post setup check failed.'))
|
||||
self.log.info(u._LI('Host ({host}) setup succeeded.')
|
||||
.format(host=hostname))
|
||||
except Exception as e:
|
||||
raise exceptions.CommandError(
|
||||
'Host (%s) setup failed : %s'
|
||||
% (hostname, e))
|
||||
raise CommandError(
|
||||
u._('Host ({host}) setup failed : {error}')
|
||||
.format(host=hostname, error=str(e)))
|
||||
return True
|
||||
|
||||
def check_host(self, hostname, result_only=False):
|
||||
command_string = '/usr/bin/sudo -u %s ansible ' % get_admin_user()
|
||||
command_string = '/usr/bin/sudo -u %s %s ' % \
|
||||
(get_admin_user(), get_ansible_command())
|
||||
gen_file_path = self.create_json_gen_file()
|
||||
err_msg = None
|
||||
output = None
|
||||
@ -454,7 +471,7 @@ class Inventory(object):
|
||||
inventory_string = '-i ' + gen_file_path
|
||||
ping_string = ' %s %s' % (hostname, '-m ping')
|
||||
cmd = (command_string + inventory_string + ping_string)
|
||||
err_msg, output = utils.run_cmd(cmd, False)
|
||||
err_msg, output = run_cmd(cmd, False)
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
@ -464,20 +481,23 @@ class Inventory(object):
|
||||
if result_only:
|
||||
return False
|
||||
else:
|
||||
raise exceptions.CommandError(
|
||||
'Host (%s) check failed : %s %s'
|
||||
% (hostname, err_msg, output))
|
||||
raise CommandError(
|
||||
u._('Host ({host}) check failed. : {error} {message}')
|
||||
.format(host=hostname, error=err_msg, message=output))
|
||||
else:
|
||||
if not result_only:
|
||||
self.log.info('Host (%s) check succeeded' % hostname)
|
||||
self.log.info(
|
||||
u._LI('Host ({host}) check succeeded.')
|
||||
.format(host=hostname))
|
||||
return True
|
||||
|
||||
def add_group(self, groupname):
|
||||
|
||||
# Group names cannot overlap with service names:
|
||||
if groupname in self._services or groupname in self._sub_services:
|
||||
raise CommandError('Invalid group name. A service name '
|
||||
'cannot be used for a group name.')
|
||||
raise CommandError(
|
||||
u._('Invalid group name. A service name '
|
||||
'cannot be used for a group name.'))
|
||||
|
||||
if groupname not in self._groups:
|
||||
self._groups[groupname] = HostGroup(groupname)
|
||||
@ -490,8 +510,9 @@ class Inventory(object):
|
||||
|
||||
def remove_group(self, groupname):
|
||||
if groupname in PROTECTED_GROUPS:
|
||||
raise CommandError('Cannot remove %s group. ' % groupname +
|
||||
'It is required by kolla.')
|
||||
raise CommandError(
|
||||
u._('Cannot remove {group} group. It is required by kolla.')
|
||||
.format(group=groupname))
|
||||
|
||||
# remove group from services & subservices
|
||||
for service in self._services.values():
|
||||
@ -510,7 +531,7 @@ class Inventory(object):
|
||||
return group
|
||||
|
||||
def get_groupnames(self):
|
||||
return self._groups.keys()
|
||||
return list(self._groups.keys())
|
||||
|
||||
def get_groups(self, host=None):
|
||||
"""return all groups containing host
|
||||
@ -587,7 +608,8 @@ class Inventory(object):
|
||||
|
||||
def add_group_to_service(self, groupname, servicename):
|
||||
if groupname not in self._groups:
|
||||
raise CommandError('Group (%s) not found.' % groupname)
|
||||
raise CommandError(u._('Group ({group}) not found.')
|
||||
.format(group=groupname))
|
||||
if servicename in self._services:
|
||||
service = self.get_service(servicename)
|
||||
service.add_groupname(groupname)
|
||||
@ -595,11 +617,13 @@ class Inventory(object):
|
||||
sub_service = self.get_sub_service(servicename)
|
||||
sub_service.add_groupname(groupname)
|
||||
else:
|
||||
raise CommandError('Service (%s) not found.' % servicename)
|
||||
raise CommandError(u._('Service ({service})) not found.')
|
||||
.format(service=servicename))
|
||||
|
||||
def remove_group_from_service(self, groupname, servicename):
|
||||
if groupname not in self._groups:
|
||||
raise CommandError('Group (%s) not found.' % groupname)
|
||||
raise CommandError(u._('Group ({group}) not found.')
|
||||
.format(group=groupname))
|
||||
if servicename in self._services:
|
||||
service = self.get_service(servicename)
|
||||
service.remove_groupname(groupname)
|
||||
@ -607,7 +631,8 @@ class Inventory(object):
|
||||
sub_service = self.get_sub_service(servicename)
|
||||
sub_service.remove_groupname(groupname)
|
||||
else:
|
||||
raise CommandError('Service (%s) not found.' % servicename)
|
||||
raise CommandError(u._('Service ({service})) not found.')
|
||||
.format(service=servicename))
|
||||
|
||||
def create_sub_service(self, sub_servicename):
|
||||
if sub_servicename not in self._sub_services:
|
||||
@ -657,8 +682,8 @@ class Inventory(object):
|
||||
|
||||
def set_deploy_mode(self, remote_flag):
|
||||
if not remote_flag and len(self._hosts) > 1:
|
||||
raise CommandError('Cannot set local deploy mode when multiple ' +
|
||||
'hosts exist')
|
||||
raise CommandError(
|
||||
u._('Cannot set local deploy mode when multiple hosts exist.'))
|
||||
self.remote_mode = remote_flag
|
||||
|
||||
for group in self.get_groups():
|
||||
@ -755,7 +780,10 @@ class Inventory(object):
|
||||
return json.dumps(jdict)
|
||||
|
||||
def _filter_hosts(self, initial_hostnames, deploy_hostnames):
|
||||
"""filter out hosts not in deploy hosts"""
|
||||
"""filter out hosts not in deploy hosts
|
||||
|
||||
Must preserve the ordering of hosts in the group.
|
||||
"""
|
||||
filtered_hostnames = []
|
||||
for hostname in initial_hostnames:
|
||||
if hostname in deploy_hostnames:
|
||||
@ -780,5 +808,5 @@ class Inventory(object):
|
||||
json_gen_file.write("print('%s')" % json_out)
|
||||
|
||||
# set executable by group
|
||||
os.chmod(json_gen_path, 0o555)
|
||||
os.chmod(json_gen_path, 0o555) # nosec
|
||||
return json_gen_path
|
@ -13,8 +13,10 @@
|
||||
# under the License.
|
||||
import os
|
||||
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common import utils
|
||||
from kollacli.exceptions import CommandError
|
||||
from kollacli import utils
|
||||
|
||||
PWDS_FILENAME = 'passwords.yml'
|
||||
PWD_EDITOR_FILENAME = 'passwd_editor.py'
|
||||
@ -29,7 +31,9 @@ def set_password(pwd_key, pwd_value):
|
||||
cmd = '%s -k %s -v %s' % (_get_cmd_prefix(), pwd_key, pwd_value)
|
||||
err_msg, output = utils.run_cmd(cmd, print_output=False)
|
||||
if err_msg:
|
||||
raise CommandError('%s %s' % (err_msg, output))
|
||||
raise CommandError(
|
||||
u._('Password set failed. {error} {message}')
|
||||
.format(error=err_msg, message=output))
|
||||
|
||||
|
||||
def clear_password(pwd_key):
|
@ -13,12 +13,13 @@
|
||||
# under the License.
|
||||
import logging
|
||||
import os
|
||||
import six
|
||||
import yaml
|
||||
|
||||
from kollacli.utils import change_property
|
||||
from kollacli.utils import get_kolla_etc
|
||||
from kollacli.utils import get_kolla_home
|
||||
from kollacli.utils import sync_read_file
|
||||
from kollacli.common.utils import change_property
|
||||
from kollacli.common.utils import get_kolla_etc
|
||||
from kollacli.common.utils import get_kolla_home
|
||||
from kollacli.common.utils import sync_read_file
|
||||
|
||||
ALLVARS_PATH = 'ansible/group_vars/all.yml'
|
||||
GLOBALS_FILENAME = 'globals.yml'
|
||||
@ -58,9 +59,8 @@ class AnsibleProperties(object):
|
||||
ANSIBLE_DEFAULTS_PATH)
|
||||
if os.path.isfile(file_name):
|
||||
with open(file_name) as service_file:
|
||||
service_contents = yaml.load(service_file)
|
||||
service_contents = yaml.safe_load(service_file)
|
||||
self.file_contents[file_name] = service_contents
|
||||
service_contents = self.filter_jinja2(service_contents)
|
||||
prop_file_name = service_name + ':main.yml'
|
||||
for key, value in service_contents.items():
|
||||
ansible_property = AnsibleProperty(key, value,
|
||||
@ -73,12 +73,17 @@ class AnsibleProperties(object):
|
||||
try:
|
||||
self.allvars_path = os.path.join(kolla_home, ALLVARS_PATH)
|
||||
with open(self.allvars_path) as allvars_file:
|
||||
allvars_contents = yaml.load(allvars_file)
|
||||
allvars_contents = yaml.safe_load(allvars_file)
|
||||
self.file_contents[self.allvars_path] = allvars_contents
|
||||
allvars_contents = self.filter_jinja2(allvars_contents)
|
||||
for key, value in allvars_contents.items():
|
||||
overrides = False
|
||||
orig_value = None
|
||||
if key in self.unique_properties:
|
||||
overrides = True
|
||||
orig_value = self.unique_properties[key].value
|
||||
ansible_property = AnsibleProperty(key, value,
|
||||
'group_vars/all.yml')
|
||||
'group_vars/all.yml',
|
||||
overrides, orig_value)
|
||||
self.properties.append(ansible_property)
|
||||
self.unique_properties[key] = ansible_property
|
||||
except Exception as e:
|
||||
@ -87,12 +92,17 @@ class AnsibleProperties(object):
|
||||
try:
|
||||
self.globals_path = os.path.join(kolla_etc, GLOBALS_FILENAME)
|
||||
globals_data = sync_read_file(self.globals_path)
|
||||
globals_contents = yaml.load(globals_data)
|
||||
globals_contents = yaml.safe_load(globals_data)
|
||||
self.file_contents[self.globals_path] = globals_contents
|
||||
globals_contents = self.filter_jinja2(globals_contents)
|
||||
for key, value in globals_contents.items():
|
||||
overrides = False
|
||||
orig_value = None
|
||||
if key in self.unique_properties:
|
||||
overrides = True
|
||||
orig_value = self.unique_properties[key].value
|
||||
ansible_property = AnsibleProperty(key, value,
|
||||
GLOBALS_FILENAME)
|
||||
GLOBALS_FILENAME,
|
||||
overrides, orig_value)
|
||||
self.properties.append(ansible_property)
|
||||
self.unique_properties[key] = ansible_property
|
||||
except Exception as e:
|
||||
@ -114,16 +124,20 @@ class AnsibleProperties(object):
|
||||
unique_list.append(value)
|
||||
return sorted(unique_list, key=lambda x: x.name)
|
||||
|
||||
# TODO(bmace) -- if this isn't used for 2.1.x it should be removed
|
||||
# property listing is still being tweaked so leaving for
|
||||
# the time being in case we want to use it
|
||||
def filter_jinja2(self, contents):
|
||||
new_contents = {}
|
||||
for key, value in contents.items():
|
||||
if isinstance(value, basestring) is False:
|
||||
self.log.debug('removing non-string: %s' % str(value))
|
||||
del contents[key]
|
||||
if not isinstance(value, six.string_types):
|
||||
self.log.debug('removing non-string: %s' % value)
|
||||
continue
|
||||
if '{{' in value and '}}' in value:
|
||||
if value and '{{' in value and '}}' in value:
|
||||
self.log.debug('removing jinja2 value: %s' % value)
|
||||
del contents[key]
|
||||
return contents
|
||||
continue
|
||||
new_contents[key] = value
|
||||
return new_contents
|
||||
|
||||
def set_property(self, property_key, property_value):
|
||||
# We only manipulate values in the globals.yml file so look up the key
|
||||
@ -148,7 +162,10 @@ class AnsibleProperties(object):
|
||||
|
||||
class AnsibleProperty(object):
|
||||
|
||||
def __init__(self, name, value, file_name):
|
||||
def __init__(self, name, value, file_name, overrides=False,
|
||||
orig_value=None):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.file_name = file_name
|
||||
self.overrides = overrides
|
||||
self.orig_value = orig_value
|
@ -16,19 +16,20 @@ import os.path
|
||||
import paramiko
|
||||
import traceback
|
||||
|
||||
from distutils.version import StrictVersion
|
||||
from kollacli.common.utils import get_admin_user
|
||||
from kollacli.common.utils import get_kollacli_etc
|
||||
from kollacli.common.utils import get_setup_user
|
||||
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.exceptions import CommandError
|
||||
from kollacli.utils import get_admin_user
|
||||
from kollacli.utils import get_kollacli_etc
|
||||
from kollacli.utils import get_setup_user
|
||||
|
||||
MIN_DOCKER_VERSION = '1.8.1'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def ssh_connect(net_addr, username, password):
|
||||
try:
|
||||
logging.getLogger('paramiko').setLevel(logging.WARNING)
|
||||
ssh_client = paramiko.SSHClient()
|
||||
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh_client.connect(hostname=net_addr, username=username,
|
||||
@ -40,7 +41,6 @@ def ssh_connect(net_addr, username, password):
|
||||
|
||||
|
||||
def ssh_setup_host(net_addr, password, setup_user=None):
|
||||
log = logging.getLogger(__name__)
|
||||
admin_user = get_admin_user()
|
||||
if setup_user is None:
|
||||
setup_user = get_setup_user()
|
||||
@ -58,7 +58,7 @@ def ssh_setup_host(net_addr, password, setup_user=None):
|
||||
'.ssh', 'authorized_keys')
|
||||
cmd = ('/usr/bin/sudo su - %s -c "echo \'%s\' >> %s"'
|
||||
% (admin_user, public_key, key_dir))
|
||||
_exec_ssh_cmd(cmd, ssh_client, log)
|
||||
_exec_ssh_cmd(cmd, ssh_client)
|
||||
|
||||
# TODO(bmace) verify ssh connection to the new account
|
||||
except Exception as e:
|
||||
@ -67,71 +67,24 @@ def ssh_setup_host(net_addr, password, setup_user=None):
|
||||
_close_ssh_client(ssh_client)
|
||||
|
||||
|
||||
def _pre_setup_checks(ssh_client, log):
|
||||
cmd = 'docker --version'
|
||||
msg, errmsg = _exec_ssh_cmd(cmd, ssh_client, log)
|
||||
if errmsg:
|
||||
raise CommandError("'%s' failed. Is docker installed? : %s"
|
||||
% (cmd, errmsg))
|
||||
if 'Docker version' not in msg:
|
||||
raise CommandError("'%s' failed. Is docker installed? : %s"
|
||||
% (cmd, msg))
|
||||
|
||||
version = msg.split('version ')[1].split(',')[0]
|
||||
if StrictVersion(version) < StrictVersion(MIN_DOCKER_VERSION):
|
||||
raise CommandError('docker version (%s) below minimum (%s)'
|
||||
% (version, msg))
|
||||
|
||||
# docker is installed, now check if it is running
|
||||
cmd = 'docker info'
|
||||
_, errmsg = _exec_ssh_cmd(cmd, ssh_client, log)
|
||||
# docker info can return warning messages in stderr, ignore them
|
||||
if errmsg and 'WARNING' not in errmsg:
|
||||
raise CommandError("'%s' failed. Is docker running? : %s"
|
||||
% (cmd, errmsg))
|
||||
|
||||
# check for docker-py
|
||||
cmd = 'python -c "import docker"'
|
||||
msg, errmsg = _exec_ssh_cmd(cmd, ssh_client, log)
|
||||
if errmsg:
|
||||
raise CommandError('host check failed. ' +
|
||||
'Is docker-py installed?')
|
||||
|
||||
|
||||
def _post_setup_checks(net_addr, log):
|
||||
try:
|
||||
ssh_client = ssh_connect(net_addr, get_admin_user(), '')
|
||||
|
||||
except Exception as e:
|
||||
raise CommandError("remote login failed : %s" % e)
|
||||
|
||||
try:
|
||||
# a basic test
|
||||
ssh_client.exec_command('ls')
|
||||
|
||||
except Exception as e:
|
||||
raise CommandError("remote command 'ls' failed : %s" % e)
|
||||
|
||||
finally:
|
||||
_close_ssh_client(ssh_client)
|
||||
|
||||
|
||||
def _close_ssh_client(ssh_client):
|
||||
if ssh_client:
|
||||
try:
|
||||
ssh_client.close()
|
||||
except Exception:
|
||||
except Exception: # nosec
|
||||
pass
|
||||
|
||||
|
||||
def _exec_ssh_cmd(cmd, ssh_client, log):
|
||||
log.debug(cmd)
|
||||
_, stdout, stderr = ssh_client.exec_command(cmd, get_pty=True)
|
||||
def _exec_ssh_cmd(cmd, ssh_client):
|
||||
LOG.debug(cmd)
|
||||
_, stdout, stderr = ssh_client.exec_command(cmd, get_pty=True) # nosec
|
||||
msg = stdout.read()
|
||||
errmsg = stderr.read()
|
||||
log.debug('%s : %s' % (msg, errmsg))
|
||||
LOG.debug('%s : %s' % (msg, errmsg))
|
||||
if errmsg:
|
||||
log.warn('WARNING: command (%s) message : %s' % (cmd, errmsg.strip()))
|
||||
LOG.warn(
|
||||
u._LW('WARNING: command : {command})\nmessage : {message}')
|
||||
.format(command=cmd, message=errmsg.strip()))
|
||||
return msg, errmsg
|
||||
|
||||
|
129
kollacli/common/support.py
Normal file
129
kollacli/common/support.py
Normal file
@ -0,0 +1,129 @@
|
||||
# Copyright(c) 2015, 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
|
||||
import os
|
||||
import tarfile
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common.inventory import Inventory
|
||||
from kollacli.common.utils import get_kolla_etc
|
||||
from kollacli.common.utils import get_kolla_home
|
||||
from kollacli.common.utils import get_kolla_log_dir
|
||||
from kollacli.common.utils import get_kollacli_etc
|
||||
from kollacli.common.utils import run_cmd
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def dump():
|
||||
"""Dumps configuration data for debugging
|
||||
|
||||
Dumps most files in /etc/kolla and /usr/share/kolla into a
|
||||
tar file so be given to support / development to help with
|
||||
debugging problems.
|
||||
"""
|
||||
try:
|
||||
msg = None
|
||||
return_code = 0
|
||||
kolla_home = get_kolla_home()
|
||||
kolla_logs = get_kolla_log_dir()
|
||||
kolla_ansible = os.path.join(kolla_home, 'ansible')
|
||||
kolla_docs = os.path.join(kolla_home, 'docs')
|
||||
kolla_etc = get_kolla_etc()
|
||||
kolla_config = os.path.join(kolla_etc, 'config')
|
||||
kolla_globals = os.path.join(kolla_etc, 'globals.yml')
|
||||
kollacli_etc = get_kollacli_etc().rstrip('/')
|
||||
ketc = 'kolla/etc/'
|
||||
kshare = 'kolla/share/'
|
||||
fd, dump_path = tempfile.mkstemp(prefix='kollacli_dump_',
|
||||
suffix='.tgz')
|
||||
os.close(fd) # avoid fd leak
|
||||
with tarfile.open(dump_path, 'w:gz') as tar:
|
||||
# Can't blanket add kolla_home because the .ssh dir is
|
||||
# accessible by the kolla user only (not kolla group)
|
||||
tar.add(kolla_ansible,
|
||||
arcname=kshare + os.path.basename(kolla_ansible))
|
||||
tar.add(kolla_docs,
|
||||
arcname=kshare + os.path.basename(kolla_docs))
|
||||
|
||||
# Can't blanket add kolla_etc because the passwords.yml
|
||||
# file is accessible by the kolla user only (not kolla group)
|
||||
tar.add(kolla_config,
|
||||
arcname=ketc + os.path.basename(kolla_config))
|
||||
tar.add(kolla_globals,
|
||||
arcname=ketc + os.path.basename(kolla_globals))
|
||||
tar.add(kollacli_etc,
|
||||
arcname=ketc + os.path.basename(kollacli_etc))
|
||||
|
||||
# add kolla log files
|
||||
if os.path.isdir(kolla_logs):
|
||||
tar.add(kolla_logs)
|
||||
|
||||
# add output of various commands
|
||||
_add_cmd_info(tar)
|
||||
|
||||
msg = u._LI('dump successful to {path}').format(path=dump_path)
|
||||
LOG.info(msg)
|
||||
|
||||
except Exception:
|
||||
msg = (u._LI('dump failed: {reason}')
|
||||
.format(reason=traceback.format_exc()))
|
||||
LOG.error(msg)
|
||||
return_code = -1
|
||||
|
||||
return return_code, msg
|
||||
|
||||
|
||||
def _add_cmd_info(tar):
|
||||
# run all the kollacli list commands
|
||||
cmds = ['kollacli --version',
|
||||
'kollacli service listgroups',
|
||||
'kollacli service list',
|
||||
'kollacli group listservices',
|
||||
'kollacli group listhosts',
|
||||
'kollacli host list',
|
||||
'kollacli property list',
|
||||
'kollacli password list']
|
||||
|
||||
# collect the json inventory output
|
||||
inventory = Inventory.load()
|
||||
inv_path = inventory.create_json_gen_file()
|
||||
cmds.append(inv_path)
|
||||
|
||||
try:
|
||||
fd, path = tempfile.mkstemp(suffix='.tmp')
|
||||
os.close(fd)
|
||||
with open(path, 'w') as tmp_file:
|
||||
for cmd in cmds:
|
||||
err_msg, output = run_cmd(cmd, False)
|
||||
tmp_file.write('\n\n$ %s\n' % cmd)
|
||||
if err_msg:
|
||||
tmp_file.write('Error message: %s\n' % err_msg)
|
||||
for line in output:
|
||||
tmp_file.write(line + '\n')
|
||||
|
||||
tar.add(path, arcname=os.path.join('kolla', 'cmds_output'))
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
if path:
|
||||
os.remove(path)
|
||||
if inv_path:
|
||||
os.remove(inv_path)
|
||||
return
|
@ -16,7 +16,13 @@ import logging
|
||||
import os
|
||||
import pexpect
|
||||
import pwd
|
||||
import yaml
|
||||
import six
|
||||
import sys
|
||||
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.exceptions import CommandError
|
||||
from oslo_utils.encodeutils import safe_decode
|
||||
|
||||
|
||||
def get_kolla_home():
|
||||
@ -48,7 +54,29 @@ def get_admin_uids():
|
||||
|
||||
|
||||
def get_kolla_log_file_size():
|
||||
return os.environ.get('KOLLA_LOG_FILE_SIZE', 500000)
|
||||
envvar = 'KOLLA_LOG_FILE_SIZE'
|
||||
size_str = os.environ.get(envvar, '500000')
|
||||
try:
|
||||
size = int(size_str)
|
||||
except Exception:
|
||||
raise CommandError(
|
||||
u._('Environmental variable ({env_var}) is not an '
|
||||
'integer ({log_size}).')
|
||||
.format(env_var=envvar, log_size=size_str))
|
||||
return size
|
||||
|
||||
|
||||
def get_property_list_length():
|
||||
envvar = 'KOLLA_PROP_LIST_LENGTH'
|
||||
length_str = os.environ.get(envvar, '50')
|
||||
try:
|
||||
length = int(length_str)
|
||||
except Exception:
|
||||
raise CommandError(
|
||||
u._('Environmental variable ({env_var}) is not an '
|
||||
'integer ({prop_length}).')
|
||||
.format(env_var=envvar, prop_length=length_str))
|
||||
return length
|
||||
|
||||
|
||||
def get_admin_user():
|
||||
@ -68,21 +96,34 @@ def get_pk_bits():
|
||||
return 1024
|
||||
|
||||
|
||||
def load_etc_yaml(fileName):
|
||||
contents = {}
|
||||
try:
|
||||
with open(get_kollacli_etc() + fileName, 'r') as f:
|
||||
contents = yaml.load(f)
|
||||
except Exception:
|
||||
# TODO(bmace) if file doesn't exist on a load we don't
|
||||
# want to blow up, some better behavior here?
|
||||
pass
|
||||
return contents or {}
|
||||
def get_ansible_command(playbook=False):
|
||||
"""get a python2 ansible command
|
||||
|
||||
|
||||
def save_etc_yaml(fileName, contents):
|
||||
with open(get_kollacli_etc() + fileName, 'w') as f:
|
||||
f.write(yaml.dump(contents))
|
||||
Ansible cannot run yet with python3. If the current default
|
||||
python is py3, prefix the ansible command with a py2
|
||||
interpreter.
|
||||
"""
|
||||
cmd = 'ansible'
|
||||
if playbook:
|
||||
cmd = 'ansible-playbook'
|
||||
if sys.version_info[0] >= 3:
|
||||
# running with py3, find a py2 interpreter for ansible
|
||||
py2_path = None
|
||||
usr_bin = os.path.join('/', 'usr', 'bin')
|
||||
for fname in os.listdir(usr_bin):
|
||||
if (fname.startswith('python2.') and
|
||||
os.path.isfile(os.path.join(usr_bin, fname))):
|
||||
suffix = fname.split('.')[1]
|
||||
if suffix.isdigit():
|
||||
py2_path = os.path.join(usr_bin, fname)
|
||||
break
|
||||
if py2_path is None:
|
||||
raise Exception(
|
||||
u._('ansible-playbook requires python2 and no '
|
||||
'python2 interpreter found in {path}.')
|
||||
.format(path=usr_bin))
|
||||
cmd = '%s %s' % (py2_path, os.path.join(usr_bin, cmd))
|
||||
return cmd
|
||||
|
||||
|
||||
def convert_to_unicode(the_string):
|
||||
@ -91,12 +132,7 @@ def convert_to_unicode(the_string):
|
||||
This is used to fixup extended ascii chars in strings. these chars cause
|
||||
errors in json pickle/unpickle.
|
||||
"""
|
||||
uni_string = ''
|
||||
try:
|
||||
uni_string = unicode(the_string)
|
||||
except UnicodeDecodeError:
|
||||
uni_string = the_string.decode('utf-8')
|
||||
return uni_string
|
||||
return six.u(the_string)
|
||||
|
||||
|
||||
def run_cmd(cmd, print_output=True):
|
||||
@ -118,13 +154,16 @@ def run_cmd(cmd, print_output=True):
|
||||
try:
|
||||
child = pexpect.spawn(cmd)
|
||||
sniff = child.read(len(pwd_prompt))
|
||||
sniff = safe_decode(sniff)
|
||||
if sniff == pwd_prompt:
|
||||
output = sniff + '\n'
|
||||
raise Exception(
|
||||
'Insufficient permissions to run command "%s"' % cmd)
|
||||
u._('Insufficient permissions to run command "{command}".')
|
||||
.format(command=cmd))
|
||||
child.maxsize = 1
|
||||
child.timeout = 86400
|
||||
for line in child:
|
||||
line = safe_decode(line)
|
||||
outline = sniff + line.rstrip()
|
||||
sniff = ''
|
||||
output = ''.join([output, outline, '\n'])
|
||||
@ -137,7 +176,8 @@ def run_cmd(cmd, print_output=True):
|
||||
if child:
|
||||
child.close()
|
||||
if child.exitstatus != 0:
|
||||
err_msg = 'Command Failed %s' % err_msg
|
||||
err_msg = (u._('Command failed. : {error}')
|
||||
.format(error=err_msg))
|
||||
return err_msg, output
|
||||
|
||||
|
@ -12,9 +12,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Exception definitions."""
|
||||
import kollacli.i18n as u
|
||||
|
||||
|
||||
class CommandError(Exception):
|
||||
def __init__(self, message, *args):
|
||||
message = 'ERROR: %s' % message
|
||||
message = u._('ERROR: {message}').format(message=message)
|
||||
super(CommandError, self).__init__(message, *args)
|
||||
|
@ -19,42 +19,51 @@ import sys
|
||||
from cliff.app import App
|
||||
from cliff.commandmanager import CommandManager
|
||||
|
||||
from kollacli.ansible.inventory import INVENTORY_PATH
|
||||
import kollacli.i18n as u
|
||||
|
||||
from kollacli.common.inventory import INVENTORY_PATH
|
||||
from kollacli.common.utils import get_kolla_log_dir
|
||||
from kollacli.common.utils import get_kolla_log_file_size
|
||||
from kollacli.common.utils import get_kollacli_etc
|
||||
from kollacli.exceptions import CommandError
|
||||
from kollacli.utils import get_kolla_log_dir
|
||||
from kollacli.utils import get_kolla_log_file_size
|
||||
from kollacli.utils import get_kollacli_etc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KollaCli(App):
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def __init__(self):
|
||||
super(KollaCli, self).__init__(
|
||||
description='Command-Line Client for OpenStack Kolla',
|
||||
description=u._('Command-Line Client for OpenStack Kolla'),
|
||||
version='0.1',
|
||||
command_manager=CommandManager('kolla.cli'),
|
||||
)
|
||||
|
||||
# check that current user is in the kolla group
|
||||
inventory_path = os.path.join(get_kollacli_etc(),
|
||||
INVENTORY_PATH)
|
||||
errString = 'Required file %s does not exist.\n' + \
|
||||
'Please re-install the kollacli to recreate the file.'
|
||||
if os.path.isfile(inventory_path) is False:
|
||||
raise CommandError(errString % inventory_path)
|
||||
err_string = u._(
|
||||
'Required file ({inventory}) does not exist.\n'
|
||||
'Please re-install the kollacli to '
|
||||
'recreate the file.').format(inventory=inventory_path)
|
||||
raise CommandError(err_string)
|
||||
|
||||
# check that current user can access the inventory file
|
||||
inventory_file = None
|
||||
try:
|
||||
inventory_file = open(inventory_path, 'r+')
|
||||
except Exception:
|
||||
raise CommandError('Permission denied to run the kollacli.' +
|
||||
'\nPlease add user to the kolla group and ' +
|
||||
'then log out and back in.')
|
||||
raise CommandError(
|
||||
u._('Permission denied to run the kollacli.\n'
|
||||
'Please add user to the kolla group and '
|
||||
'then log out and back in.'))
|
||||
finally:
|
||||
if inventory_file and inventory_file.close is False:
|
||||
inventory_file.close()
|
||||
|
||||
# paramiko log is very chatty, tune it down
|
||||
logging.getLogger('paramiko').setLevel(logging.WARNING)
|
||||
|
||||
# set up logging
|
||||
self.rotating_log_dir = get_kolla_log_dir()
|
||||
self.max_bytes = get_kolla_log_file_size()
|
||||
self.backup_count = 4
|
||||
@ -64,9 +73,9 @@ class KollaCli(App):
|
||||
self.add_rotational_log()
|
||||
|
||||
def clean_up(self, cmd, result, err):
|
||||
self.log.debug('clean_up %s', cmd.__class__.__name__)
|
||||
LOG.debug('clean_up %s', cmd.__class__.__name__)
|
||||
if err:
|
||||
self.log.debug('error: %s', err)
|
||||
LOG.debug('ERROR: %s', err)
|
||||
|
||||
def add_rotational_log(self):
|
||||
root_logger = logging.getLogger('')
|
||||
|
@ -2,9 +2,10 @@ ansible>=1.9.2
|
||||
Babel>=0.9.6
|
||||
cliff>=1.13.0 # Apache-2.0
|
||||
cliff-tablib>=1.1
|
||||
docker-py>=1.3.1
|
||||
docker-py==1.3.1
|
||||
jsonpickle>=0.9
|
||||
oslo.i18n>=1.3.0 # Apache-2.0
|
||||
oslo.utils!=2.6.0,>=2.4.0 # Apache-2.0
|
||||
paramiko>=1.15
|
||||
pbr>=0.10
|
||||
pexpect>=2.3,<3.3
|
||||
|
50
setup.cfg
50
setup.cfg
@ -31,31 +31,31 @@ console_scripts =
|
||||
kollacli = kollacli.shell:main
|
||||
|
||||
kolla.cli =
|
||||
deploy = kollacli.common:Deploy
|
||||
dump = kollacli.common:Dump
|
||||
group_add = kollacli.group:GroupAdd
|
||||
group_addhost = kollacli.group:GroupAddhost
|
||||
group_listhosts = kollacli.group:GroupListhosts
|
||||
group_listservices = kollacli.group:GroupListservices
|
||||
group_remove = kollacli.group:GroupRemove
|
||||
group_removehost = kollacli.group:GroupRemovehost
|
||||
host_add = kollacli.host:HostAdd
|
||||
host_check = kollacli.host:HostCheck
|
||||
host_destroy = kollacli.host:HostDestroy
|
||||
host_list = kollacli.host:HostList
|
||||
host_remove = kollacli.host:HostRemove
|
||||
host_setup = kollacli.host:HostSetup
|
||||
password_clear = kollacli.password:PasswordClear
|
||||
password_list = kollacli.password:PasswordList
|
||||
password_set = kollacli.password:PasswordSet
|
||||
property_clear = kollacli.property:PropertyClear
|
||||
property_list = kollacli.property:PropertyList
|
||||
property_set = kollacli.property:PropertySet
|
||||
service_addgroup = kollacli.service:ServiceAddGroup
|
||||
service_list = kollacli.service:ServiceList
|
||||
service_listgroups = kollacli.service:ServiceListGroups
|
||||
service_removegroup = kollacli.service:ServiceRemoveGroup
|
||||
setdeploy = kollacli.common:Setdeploy
|
||||
deploy = kollacli.commands.deploy:Deploy
|
||||
dump = kollacli.commands.support:Dump
|
||||
group_add = kollacli.commands.group:GroupAdd
|
||||
group_addhost = kollacli.commands.group:GroupAddhost
|
||||
group_listhosts = kollacli.commands.group:GroupListhosts
|
||||
group_listservices = kollacli.commands.group:GroupListservices
|
||||
group_remove = kollacli.commands.group:GroupRemove
|
||||
group_removehost = kollacli.commands.group:GroupRemovehost
|
||||
host_add = kollacli.commands.host:HostAdd
|
||||
host_check = kollacli.commands.host:HostCheck
|
||||
host_destroy = kollacli.commands.host:HostDestroy
|
||||
host_list = kollacli.commands.host:HostList
|
||||
host_remove = kollacli.commands.host:HostRemove
|
||||
host_setup = kollacli.commands.host:HostSetup
|
||||
password_clear = kollacli.commands.password:PasswordClear
|
||||
password_list = kollacli.commands.password:PasswordList
|
||||
password_set = kollacli.commands.password:PasswordSet
|
||||
property_clear = kollacli.commands.property:PropertyClear
|
||||
property_list = kollacli.commands.property:PropertyList
|
||||
property_set = kollacli.commands.property:PropertySet
|
||||
service_addgroup = kollacli.commands.service:ServiceAddGroup
|
||||
service_list = kollacli.commands.service:ServiceList
|
||||
service_listgroups = kollacli.commands.service:ServiceListGroups
|
||||
service_removegroup = kollacli.commands.service:ServiceRemoveGroup
|
||||
setdeploy = kollacli.commands.deploy:Setdeploy
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
|
@ -4,6 +4,7 @@
|
||||
# Hacking already pins down pep8, pyflakes and flake8
|
||||
hacking>=0.10.2,<0.11
|
||||
|
||||
bandit>=0.13.2
|
||||
coverage>=3.6
|
||||
discover
|
||||
fixtures>=0.3.14
|
||||
|
@ -17,12 +17,13 @@ import os
|
||||
import pxssh
|
||||
import subprocess
|
||||
import sys
|
||||
import testtools
|
||||
import traceback
|
||||
import yaml
|
||||
|
||||
import testtools
|
||||
from oslo_utils.encodeutils import safe_decode
|
||||
|
||||
import kollacli.utils as utils
|
||||
import kollacli.common.utils as utils
|
||||
|
||||
TEST_SUFFIX = 'test/'
|
||||
VENV_PY_PATH = '.venv/bin/python'
|
||||
@ -47,6 +48,7 @@ class KollaCliTest(testtools.TestCase):
|
||||
% self._testMethodName)
|
||||
|
||||
# switch to test path
|
||||
self.log.info('running python: %s/%s' % (sys.executable, sys.version))
|
||||
etc_path = utils.get_kollacli_etc()
|
||||
self.log.debug('etc for tests: %s' % etc_path)
|
||||
|
||||
@ -86,11 +88,12 @@ class KollaCliTest(testtools.TestCase):
|
||||
msg = e.output
|
||||
|
||||
except Exception as e:
|
||||
retval = e.errno
|
||||
retval = -1
|
||||
msg = ('Unexpected exception: %s, cmd: %s'
|
||||
% (traceback.format_exc(), cmd))
|
||||
|
||||
# the py dev debugger adds a string at the line start, remove it
|
||||
msg = safe_decode(msg)
|
||||
if msg.startswith('pydev debugger'):
|
||||
msg = msg.split('\n', 1)[1]
|
||||
return (retval, msg)
|
||||
@ -233,7 +236,7 @@ class TestConfig(object):
|
||||
self.hosts_info[name]['groups'].remove(group)
|
||||
|
||||
def get_hostnames(self):
|
||||
return self.hosts_info.keys()
|
||||
return list(self.hosts_info.keys())
|
||||
|
||||
def set_username(self, name, username):
|
||||
self.hosts_info[name]['username'] = username
|
||||
@ -258,7 +261,7 @@ class TestConfig(object):
|
||||
with open(path, 'r+') as cfg_file:
|
||||
yml_data = cfg_file.read()
|
||||
|
||||
test_cfg = yaml.load(yml_data)
|
||||
test_cfg = yaml.safe_load(yml_data)
|
||||
|
||||
hosts_info = test_cfg['hosts']
|
||||
if hosts_info:
|
||||
@ -298,6 +301,7 @@ class TestConfig(object):
|
||||
session.sendline(cmd)
|
||||
session.prompt()
|
||||
out = session.before
|
||||
out = safe_decode(out)
|
||||
self.log.info(out)
|
||||
session.logout()
|
||||
return out
|
||||
|
@ -14,8 +14,8 @@
|
||||
#
|
||||
from common import KollaCliTest
|
||||
|
||||
from kollacli.ansible.inventory import Inventory
|
||||
from kollacli.ansible.inventory import SERVICES
|
||||
from kollacli.common.inventory import Inventory
|
||||
from kollacli.common.inventory import SERVICES
|
||||
|
||||
import json
|
||||
import os
|
||||
@ -156,7 +156,6 @@ class TestFunctional(KollaCliTest):
|
||||
is_file = os.path.isfile(dump_path)
|
||||
self.assertTrue(is_file,
|
||||
'dump file not found at %s' % dump_path)
|
||||
|
||||
file_paths = []
|
||||
with tarfile.open(dump_path, 'r') as tar:
|
||||
file_paths = tar.getnames()
|
||||
|
@ -15,16 +15,17 @@
|
||||
from common import KollaCliTest
|
||||
from common import TestConfig
|
||||
|
||||
from kollacli.ansible import inventory
|
||||
|
||||
import unittest
|
||||
|
||||
DISABLED_SERVICES = [
|
||||
'cinder', 'glance', 'haproxy', 'heat', 'rabbitmq'
|
||||
'cinder', 'glance', 'haproxy', 'heat', 'msqlcluster',
|
||||
'horizon', 'keystone', 'murano', 'neutron', 'nova',
|
||||
]
|
||||
ENABLED_SERVICES = [
|
||||
'mysqlcluster'
|
||||
'rabbitmq'
|
||||
]
|
||||
ENABLED_DATA_SERVICES = [
|
||||
'rabbitmq_data'
|
||||
]
|
||||
|
||||
UNKNOWN_HOST = 'Name or service not known'
|
||||
@ -60,9 +61,12 @@ class TestFunctional(KollaCliTest):
|
||||
self.assertIn(UNKNOWN_HOST, '%s' % e,
|
||||
'Unexpected exception in host setup: %s' % e)
|
||||
|
||||
# add host to all deploy groups
|
||||
for group in inventory.DEPLOY_GROUPS:
|
||||
self.run_cli_cmd('group addhost %s %s' % (group, hostname))
|
||||
# add host to a new deploy group
|
||||
group_name = 'test_group'
|
||||
self.run_cli_cmd('group add %s' % group_name)
|
||||
self.run_cli_cmd('group addhost %s %s' % (group_name, hostname))
|
||||
for service in ENABLED_SERVICES:
|
||||
self.run_cli_cmd('service addgroup %s %s' % (service, group_name))
|
||||
|
||||
# destroy services, initialize server
|
||||
try:
|
||||
@ -107,9 +111,10 @@ class TestFunctional(KollaCliTest):
|
||||
'is not running on host: %s ' % hostname +
|
||||
'after deploy.')
|
||||
|
||||
# destroy services (via --stop flag)
|
||||
# destroy non-data services (via --stop flag)
|
||||
# this should leave only data containers running
|
||||
try:
|
||||
self.run_cli_cmd('host destroy %s --stop --includedata' % hostname)
|
||||
self.run_cli_cmd('host destroy %s --stop' % hostname)
|
||||
except Exception as e:
|
||||
self.assertFalse(is_physical_host, '2nd destroy exception: %s' % e)
|
||||
self.assertIn(UNKNOWN_HOST, '%s' % e,
|
||||
@ -123,10 +128,37 @@ class TestFunctional(KollaCliTest):
|
||||
'is running on host: %s ' % hostname +
|
||||
'after destroy.')
|
||||
|
||||
for enabled_service in ENABLED_DATA_SERVICES:
|
||||
self.assertIn(enabled_service, docker_ps,
|
||||
'enabled service: %s ' % enabled_service +
|
||||
'is not running on host: %s ' % hostname +
|
||||
'after no-data destroy.')
|
||||
|
||||
try:
|
||||
self.run_cli_cmd('host destroy %s --includedata --stop' % hostname)
|
||||
except Exception as e:
|
||||
self.assertFalse(is_physical_host, '3rd destroy exception: %s' % e)
|
||||
self.assertIn(UNKNOWN_HOST, '%s' % e,
|
||||
'Unexpected exception in 3rd destroy: %s' % e)
|
||||
|
||||
if is_physical_host:
|
||||
docker_ps = test_config.run_remote_cmd('docker ps', hostname)
|
||||
for disabled_service in DISABLED_SERVICES:
|
||||
self.assertNotIn(disabled_service, docker_ps,
|
||||
'disabled service: %s ' % disabled_service +
|
||||
'is running on host: %s ' % hostname +
|
||||
'after destroy.')
|
||||
|
||||
for enabled_service in ENABLED_DATA_SERVICES:
|
||||
self.assertNotIn(enabled_service, docker_ps,
|
||||
'enabled service: %s ' % enabled_service +
|
||||
'is running on host: %s ' % hostname +
|
||||
'after destroy.')
|
||||
|
||||
for enabled_service in ENABLED_SERVICES:
|
||||
self.assertNotIn(enabled_service, docker_ps,
|
||||
'enabled service: %s ' % enabled_service +
|
||||
'is still running on host: %s ' % hostname +
|
||||
'is running on host: %s ' % hostname +
|
||||
'after destroy.')
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -91,7 +91,7 @@ class TestFunctional(KollaCliTest):
|
||||
|
||||
# check if host is not set-up
|
||||
timeout = time.time() + 75
|
||||
while time.time <= timeout:
|
||||
while time.time() <= timeout:
|
||||
msg = self.run_cli_cmd('host check %s' % hostname, True)
|
||||
if 'ERROR:' not in msg:
|
||||
self.log.info('waiting for ansible ssh session to timeout')
|
||||
|
@ -16,7 +16,7 @@ from common import KollaCliTest
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from kollacli.utils import get_kolla_etc
|
||||
from kollacli.common.utils import get_kolla_etc
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
@ -17,7 +17,7 @@ from common import KollaCliTest
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from kollacli.utils import get_kolla_etc
|
||||
from kollacli.common.utils import get_kolla_etc
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
@ -17,9 +17,9 @@ from common import KollaCliTest
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from kollacli.ansible.inventory import DEFAULT_GROUPS
|
||||
from kollacli.ansible.inventory import DEFAULT_OVERRIDES
|
||||
from kollacli.ansible.inventory import SERVICES
|
||||
from kollacli.common.inventory import DEFAULT_GROUPS
|
||||
from kollacli.common.inventory import DEFAULT_OVERRIDES
|
||||
from kollacli.common.inventory import SERVICES
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
@ -19,7 +19,7 @@ predeploy_cmds:
|
||||
- setdeploy remote
|
||||
- property set kolla_external_address 192.168.9.89
|
||||
- property set kolla_internal_address 192.168.9.89
|
||||
- property set docker_registry ca-build44.us.oracle.com:5000
|
||||
- property set docker_registry ca-qa-docker-reg.us.oracle.com:5000
|
||||
- property set docker_insecure_registry True
|
||||
- property set network_interface enp0s3
|
||||
- property set openstack_release 2.0.1.89
|
||||
|
@ -22,6 +22,9 @@ import tempfile
|
||||
from kollacli.ansible.inventory import Inventory
|
||||
from kollacli.ansible import properties
|
||||
from kollacli.utils import get_admin_user
|
||||
from kollacli.utils import get_ansible_command
|
||||
|
||||
from oslo_utils.encodeutils import safe_decode
|
||||
|
||||
tar_file_descr = None
|
||||
|
||||
@ -30,14 +33,15 @@ def run_ansible_cmd(cmd, host):
|
||||
# sudo -u kolla ansible ol7-c4 -i inv_path -a "cmd"
|
||||
out = None
|
||||
user = get_admin_user()
|
||||
inventory = Inventory.load()
|
||||
inv_path = inventory.create_json_gen_file()
|
||||
inv = Inventory.load()
|
||||
inv_path = inv.create_json_gen_file()
|
||||
|
||||
acmd = ('/usr/bin/sudo -u %s ansible %s -i %s -a "%s"'
|
||||
% (user, host, inv_path, cmd))
|
||||
ansible_verb = get_ansible_command()
|
||||
ansible_cmd = ('/usr/bin/sudo -u %s %s %s -i %s -a "%s"'
|
||||
% (user, ansible_verb, host, inv_path, cmd))
|
||||
|
||||
try:
|
||||
(out, err) = subprocess.Popen(acmd, shell=True,
|
||||
(out, err) = subprocess.Popen(ansible_cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE).communicate()
|
||||
except Exception as e:
|
||||
@ -47,11 +51,13 @@ def run_ansible_cmd(cmd, host):
|
||||
|
||||
if not out:
|
||||
print('Host %s is not accessible: %s, skipping' % (host, err))
|
||||
elif '>>' not in out:
|
||||
print('Ansible command: %s' % acmd)
|
||||
print('Host: %s. \nInvalid ansible return data: [%s]. skipping'
|
||||
% (host, out))
|
||||
out = None
|
||||
else:
|
||||
out = safe_decode(out)
|
||||
if '>>' not in out:
|
||||
print('Ansible command: %s' % ansible_cmd)
|
||||
print('Host: %s. \nInvalid ansible return data: [%s]. skipping'
|
||||
% (host, out))
|
||||
out = None
|
||||
return out
|
||||
|
||||
|
||||
@ -165,6 +171,7 @@ def main():
|
||||
(_, err) = subprocess.Popen('kollacli dump'.split(),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE).communicate()
|
||||
err = safe_decode(err)
|
||||
if '/' in err:
|
||||
dump_path = '/' + err.strip().split('/', 1)[1]
|
||||
if os.path.isfile(dump_path):
|
||||
|
@ -15,13 +15,14 @@
|
||||
import getopt
|
||||
import sys
|
||||
|
||||
from kollacli import utils
|
||||
from kollacli.common.utils import change_property
|
||||
from kollacli.common.utils import sync_read_file
|
||||
|
||||
|
||||
def _print_pwd_keys(path):
|
||||
pwd_keys = ''
|
||||
prefix = ''
|
||||
pwd_data = utils.sync_read_file(path)
|
||||
pwd_data = sync_read_file(path)
|
||||
for line in pwd_data.split('\n'):
|
||||
if line.startswith('#'):
|
||||
# skip commented lines
|
||||
@ -67,7 +68,7 @@ def main():
|
||||
_print_pwd_keys(path)
|
||||
else:
|
||||
# edit a password
|
||||
utils.change_property(path, pwd_key, pwd_value, clear_flag)
|
||||
change_property(path, pwd_key, pwd_value, clear_flag)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
13
tox.ini
13
tox.ini
@ -1,7 +1,7 @@
|
||||
[tox]
|
||||
minversion = 1.6
|
||||
skipsdist = True
|
||||
envlist = py27,pep8
|
||||
envlist = py27,py34,pep8,bandit
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
@ -14,7 +14,12 @@ deps = -r{toxinidir}/requirements.txt
|
||||
[testenv:py27]
|
||||
commands =
|
||||
/usr/bin/find . -type f -name "*.pyc" -delete
|
||||
{envpython} setup.py test -s tests
|
||||
{envpython} -m unittest discover -s tests -p "*.*"
|
||||
|
||||
[testenv:py34]
|
||||
commands =
|
||||
/usr/bin/find . -type f -name "*.pyc" -delete
|
||||
{envpython} -m unittest discover -s tests -p "*.*"
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
@ -25,3 +30,7 @@ commands = {posargs}
|
||||
[flake8]
|
||||
show-source = True
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
||||
|
||||
[testenv:bandit]
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = bandit -r kollacli
|
||||
|
Loading…
Reference in New Issue
Block a user