Summary: update host command to use ansible-friendly inventory persistance

Description:
- use new jsonpickle storage for hosts/inventory data instead of yaml. this
  is done to make it easier to store and load the object model for the
  inventory.
- rename hosts class to inventory
- remove host-zone association, new association is with groups. This better
  matches the ansible model.
- populate blank inventory with structure required for openstack. This adds
  the various services and containers under the appropriate groups.
- created new group class and updated inventory and host classes. added attributes
  for host for hypervisor, and management attribute. Also added versions to the
  inventory class.
This commit is contained in:
Steve Noyes 2015-08-13 18:56:46 -04:00
parent a3ce5c852e
commit 1e33dc3d40
6 changed files with 538 additions and 376 deletions

View File

@ -0,0 +1,315 @@
# 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 jsonpickle
import logging
import os
import shutil
from tempfile import mkstemp
from kollacli import exceptions
from kollacli import utils
from kollacli.sshutils import ssh_check_host
from kollacli.sshutils import ssh_check_keys
from kollacli.sshutils import ssh_install_host
from kollacli.sshutils import ssh_keygen
from kollacli.sshutils import ssh_uninstall_host
from kollacli.exceptions import CommandError
INVENTORY_PATH = 'ansible/inventory/inventory.p'
COMPUTE_GRP_NAME = 'compute'
CONTROL_GRP_NAME = 'control'
NETWORK_GRP_NAME = 'network'
DEPLOY_GROUPS = [COMPUTE_GRP_NAME,
CONTROL_GRP_NAME,
NETWORK_GRP_NAME,
]
SERVICE_GROUPS = ['mariadb', 'rabbitmq', 'keystone', 'glance',
'nova', 'haproxy', 'neutron']
CONTAINER_GROUPS = ['glance-api', 'glance-registry',
'nova-api', 'nova-conductor', 'nova-consoleauth',
'nova-novncproxy', 'nova-scheduler',
'neutron-server', 'neutron-agents']
DEFAULT_HIERARCHY = {
CONTROL_GRP_NAME: {
'glance': ['glance-api', 'glance-registry'],
'keystone': [],
'mariadb': [],
'nova': ['nova-api', 'nova-conductor', 'nova-consoleauth',
'nova-novncproxy', 'nova-scheduler'],
'rabbitmq': [],
},
NETWORK_GRP_NAME: {
'haproxy': [],
'neutron': ['neutron-server', 'neutron-agents'],
},
COMPUTE_GRP_NAME: {
},
}
class Host(object):
class_version = 1
log = logging.getLogger(__name__)
def __init__(self, hostname):
self.name = hostname
self._groups = {} # kv = groupname:group
self.alias = ''
self.is_mgmt = False
self.hypervisor = ''
self.vars = {}
self.version = self.__class__.class_version
def upgrade(self):
pass
def _add_group(self, group):
if group.name not in self._groups:
self._groups[group.name] = group
def _remove_group(self, group):
if group.name in self._groups:
del self._groups[group.name]
def get_groups(self):
return self._groups.values()
def check(self):
sshKeysExist = ssh_check_keys()
if not sshKeysExist:
try:
ssh_keygen()
except Exception as e:
raise exceptions.CommandError(
'ERROR: ssh key generation failed on local host : %s'
% str(e))
try:
self.log.info('Starting check of host (%s)' % self.name)
ssh_check_host(self.name)
self.log.info('Host (%s), check succeeded' % self.name)
except Exception as e:
raise Exception(
'ERROR: Host (%s), check failed. Reason : %s'
% (self.name, str(e)))
return True
def install(self, password):
self._setup_keys()
# check if already installed
if self._is_installed():
self.log.info('Install skipped for host (%s), ' % self.name +
'kolla already installed')
return True
# not installed- we need to set up the user / remote ssh keys
# using root and the available password
try:
self.log.info('Starting install of host (%s)'
% self.name)
ssh_install_host(self.name, password)
self.log.info('Host (%s) install succeeded' % self.name)
except Exception as e:
raise exceptions.CommandError(
'ERROR: Host (%s) install failed : %s'
% (self.name, str(e)))
return True
def uninstall(self, password):
self._setup_keys()
try:
self.log.info('Starting uninstall of host (%s)' % self.name)
ssh_uninstall_host(self.name, password)
self.log.info('Host (%s) uninstall succeeded' % self.name)
except Exception as e:
raise exceptions.CommandError(
'ERROR: Host (%s) uninstall failed : %s'
% (self.name, str(e)))
return True
def _setup_keys(self):
sshKeysExist = ssh_check_keys()
if not sshKeysExist:
try:
ssh_keygen()
except Exception as e:
raise exceptions.CommandError(
'ERROR: Error generating ssh keys on local host : %s'
% str(e))
def _is_installed(self):
is_installed = False
try:
ssh_check_host(self.name)
is_installed = True
except Exception as e:
self.log.debug('%s' % str(e))
pass
return is_installed
class Group(object):
class_version = 1
def __init__(self, name):
self.name = name
self.parent = None
self.children = []
self._hosts = {} # kv = hostname:object
self._version = 1
self.vars = {}
self.version = self.__class__.class_version
def upgrade(self):
pass
def add_host(self, host):
if host.name not in self._hosts:
self._hosts[host.name] = host
else:
host = self._hosts[host.name]
host._add_group(self)
def remove_host(self, host):
if host.name in self._hosts:
host._remove_group(self)
del self._hosts[host.name]
def get_hosts(self):
return self._hosts.values()
class Inventory(object):
class_version = 1
def __init__(self):
self._groups = {} # kv = name:object
self._hosts = {} # kv = name:object
self.vars = {}
self.version = self.__class__.class_version
# initialize the inventory to its defaults
self._create_default_inventory()
def upgrade(self):
pass
@staticmethod
def load():
"""load the inventory from a pickle file"""
inventory_path = os.path.join(utils.get_client_etc(), INVENTORY_PATH)
try:
if os.path.exists(inventory_path):
with open(inventory_path, 'rb') as inv_file:
data = inv_file.read()
inventory = jsonpickle.decode(data)
# upgrade version handling
if inventory.version != inventory.class_version:
inventory.upgrade()
else:
inventory = Inventory()
except Exception as e:
raise Exception('ERROR: loading inventory : %s' % str(e))
return inventory
@staticmethod
def save(inventory):
"""Save the inventory in a pickle file"""
inventory_path = os.path.join(utils.get_client_etc(), INVENTORY_PATH)
try:
# the file handle returned from mkstemp must be closed or else
# if this is called many times you will have an unpleasant
# file handle leak
tmp_filehandle, tmp_path = mkstemp()
data = jsonpickle.encode(inventory)
with open(tmp_path, 'w') as tmp_file:
tmp_file.write(data)
shutil.copyfile(tmp_path, inventory_path)
os.remove(tmp_path)
except Exception as e:
raise Exception('ERROR: saving inventory : %s' % str(e))
finally:
try:
os.close(tmp_filehandle)
except Exception:
pass
if tmp_filehandle is not None:
try:
os.close(tmp_filehandle)
except Exception:
pass
def _create_default_inventory(self):
for (deploy_name, services) in DEFAULT_HIERARCHY.items():
deploy_group = Group(deploy_name)
# add service groups
for (service_name, container_names) in services.items():
service_group = Group(service_name)
deploy_group.children.append(service_group)
service_group.parent = deploy_group
for container_name in container_names:
container_group = Group(container_name)
service_group.children.append(container_group)
container_group.parent = service_group
self._groups[deploy_name] = deploy_group
def get_hosts(self):
return self._hosts.values()
def get_host(self, hostname):
host = None
if hostname in self._hosts:
host = self._hosts[hostname]
return host
def add_host(self, hostname, groupname):
if groupname not in self._groups:
raise CommandError('Group name not valid')
# create new host if it doesn't exist
if hostname not in self._hosts:
host = Host(hostname)
self._hosts[hostname] = host
else:
host = self._hosts[hostname]
group = self._groups[groupname]
group.add_host(host)
def remove_host(self, hostname, groupname=None):
if hostname in self._hosts:
host = self._hosts[hostname]
groups = host.get_groups()
for group in groups:
if not groupname or groupname == group.name:
host._remove_group(group)
# if host no longer exists in any group, remove it from inventory
if not host.get_groups():
del self._hosts[hostname]

View File

@ -17,8 +17,7 @@ import logging
from kollacli import exceptions
from kollacli.i18n import _
from kollacli.objects.hosts import Host
from kollacli.objects.hosts import Hosts
from kollacli.ansible.inventory import Inventory
from kollacli.objects.zones import Zones
from cliff.command import Command
@ -38,45 +37,50 @@ def _zone_not_found(log, zonename):
class HostAdd(Command):
"""Add host to open-stack-kolla
"""Add host to open-stack-kolla"""
log = logging.getLogger(__name__)
If host already exists, just update the network address.
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')
parser.add_argument('groupname', metavar='<groupname>',
help='group name')
return parser
def take_action(self, parsed_args):
hostname = parsed_args.hostname.strip()
groupname = parsed_args.groupname.strip()
inventory = Inventory.load()
inventory.add_host(hostname, groupname)
Inventory.save(inventory)
class HostRemove(Command):
"""Remove host from openstack-kolla
If a group is specified, the host will be removed from that group.
If no group is specified, the host will be removed from all groups.
"""
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='hostname')
parser.add_argument('networkaddress', metavar='networkaddress',
help='Network address')
return parser
def take_action(self, parsed_args):
hostname = parsed_args.hostname.strip()
net_addr = parsed_args.networkaddress.strip()
hosts = Hosts()
host = Host(hostname, net_addr)
hosts.add_host(host)
hosts.save()
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='hostname')
parser.add_argument('hostname', metavar='<hostname>', help='host name')
parser.add_argument('groupname', nargs='?',
metavar='<group>', help='group name')
return parser
def take_action(self, parsed_args):
hostname = parsed_args.hostname.strip()
hosts = Hosts()
hosts.remove_host(hostname)
hosts.save()
groupname = None
if parsed_args.groupname:
groupname = parsed_args.groupname.strip()
inventory = Inventory.load()
inventory.remove_host(hostname, groupname)
Inventory.save(inventory)
class HostList(Lister):
@ -85,14 +89,19 @@ class HostList(Lister):
log = logging.getLogger(__name__)
def take_action(self, parsed_args):
hosts = Hosts().get_all()
inventory = Inventory.load()
hosts = inventory.get_hosts()
data = []
if hosts:
for host in hosts:
data.append((host.hostname, host.net_addr, host.zone))
groupnames = []
for group in host.get_groups():
groupnames.append(group.name)
data.append((host.name, groupnames))
else:
data.append(('', '', ''))
return (('Host Name', 'Address', 'Zone'), data)
data.append(('', ''))
return (('Host Name', 'Groups'), data)
class HostSetzone(Command):
@ -114,7 +123,7 @@ class HostSetzone(Command):
_zone_not_found(self.log, zonename)
return False
hosts = Hosts()
hosts = Inventory()
host = hosts.get_host(hostname)
if not host:
_host_not_found(self.log, hostname)
@ -137,7 +146,7 @@ class HostClearzone(Command):
def take_action(self, parsed_args):
hostname = parsed_args.hostname.strip()
hosts = Hosts()
hosts = Inventory()
host = hosts.get_host(hostname)
if not host:
_host_not_found(self.log, hostname)
@ -180,7 +189,8 @@ class HostCheck(Command):
def take_action(self, parsed_args):
hostname = parsed_args.hostname.strip()
host = Hosts().get_host(hostname)
inventory = Inventory.load()
host = inventory.get_host(hostname)
if not host:
_host_not_found(self.log, hostname)
return False
@ -201,7 +211,8 @@ class HostInstall(Command):
def take_action(self, parsed_args):
hostname = parsed_args.hostname.strip()
host = Hosts().get_host(hostname)
inventory = Inventory.load()
host = inventory.get_host(hostname)
if not host:
_host_not_found(self.log, hostname)
return False
@ -227,7 +238,8 @@ class HostUninstall(Command):
def take_action(self, parsed_args):
hostname = parsed_args.hostname.strip()
host = Hosts().get_host(hostname)
inventory = Inventory.load()
host = inventory.get_host(hostname)
if not host:
_host_not_found(self.log, hostname)
return False

View File

@ -1,166 +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
from kollacli import exceptions
from kollacli import utils
from kollacli.sshutils import ssh_check_host
from kollacli.sshutils import ssh_check_keys
from kollacli.sshutils import ssh_install_host
from kollacli.sshutils import ssh_keygen
from kollacli.sshutils import ssh_uninstall_host
class Host(object):
hostname = ''
net_addr = ''
zone = ''
services = []
log = logging.getLogger(__name__)
def __init__(self, hostname, net_addr=''):
self.hostname = hostname
self.net_addr = net_addr
def check(self):
sshKeysExist = ssh_check_keys()
if not sshKeysExist:
try:
ssh_keygen()
except Exception as e:
raise exceptions.CommandError(
'ERROR: ssh key generation failed on local host : %s'
% str(e))
try:
self.log.info('Starting host (%s) check at address (%s)' %
(self.hostname, self.net_addr))
ssh_check_host(self.net_addr)
self.log.info('Host (%s), check succeeded' % self.hostname)
except Exception as e:
raise Exception(
'ERROR: Host (%s), check failed. Reason : %s'
% (self.hostname, str(e)))
return True
def install(self, password):
self._setup_keys()
# check if already installed
if self._is_installed():
self.log.info('Install skipped for host (%s), ' % self.hostname +
'kolla already installed')
return True
# not installed- we need to set up the user / remote ssh keys
# using root and the available password
try:
self.log.info('Starting install of host (%s) at address (%s)'
% (self.hostname, self.net_addr))
ssh_install_host(self.net_addr, password)
self.log.info('Host (%s) install succeeded' % self.hostname)
except Exception as e:
raise exceptions.CommandError(
'ERROR: Host (%s) install failed : %s'
% (self.hostname, str(e)))
return True
def uninstall(self, password):
self._setup_keys()
try:
self.log.info('Starting uninstall of host (%s) at address (%s)'
% (self.hostname, self.net_addr))
ssh_uninstall_host(self.net_addr, password)
self.log.info('Host (%s) uninstall succeeded' % self.hostname)
except Exception as e:
raise exceptions.CommandError(
'ERROR: Host (%s) uninstall failed : %s'
% (self.hostname, str(e)))
return True
def _setup_keys(self):
sshKeysExist = ssh_check_keys()
if not sshKeysExist:
try:
ssh_keygen()
except Exception as e:
raise exceptions.CommandError(
'ERROR: Error generating ssh keys on local host : %s'
% str(e))
def _is_installed(self):
is_installed = False
try:
ssh_check_host(self.net_addr)
is_installed = True
except Exception as e:
self.log.debug('%s' % str(e))
pass
return is_installed
class Hosts(object):
_hosts = {}
def __init__(self):
yml = utils.load_etc_yaml('hosts.yml')
for (hostname, info) in yml.items():
host = Host(hostname)
if 'NetworkAddress' in info:
host.net_addr = info['NetworkAddress']
if 'Zone' in info:
host.zone = info['Zone']
if 'Services' in info:
service_list = info['Services']
if service_list:
host.services = service_list.split(',')
self._hosts[hostname] = host
def get_all(self):
return self._hosts.values()
def get_host(self, hostname):
host = None
if hostname in self._hosts:
host = self._hosts[hostname]
return host
def add_host(self, host):
if host.hostname not in self._hosts:
self._hosts[host.hostname] = host
else:
# existing host, just update network address
cur_host = self._hosts[host.hostname]
cur_host.net_addr = host.net_addr
def remove_host(self, hostname):
if hostname in self._hosts:
del self._hosts[hostname]
def save(self):
"""save hosts info"""
info = {}
for host in self._hosts.values():
info[host.hostname] = {}
info[host.hostname]['NetworkAddress'] = host.net_addr
info[host.hostname]['Zone'] = host.zone
info[host.hostname]['Services'] = []
for service in host.services:
info[host.hostname]['Services'].append(service)
utils.save_etc_yaml('hosts.yml', info)

View File

@ -2,6 +2,7 @@ Babel>=1.3
cliff>=1.10.0 # Apache-2.0
cliff-tablib>=1.1
docker-py>=1.3
jsonpickle>=0.9
oslo.i18n>=1.5.0 # Apache-2.0
paramiko
pbr>=0.11,<2.0

View File

@ -22,12 +22,12 @@ import testtools
import kollacli.utils as utils
TEST_SUFFIX = '/test/'
TEST_SUFFIX = 'test/'
ENV_ETC = 'KOLLA_CLIENT_ETC'
VENV_PY_PATH = '/.venv/bin/python'
VENV_PY_PATH = '.venv/bin/python'
KOLLA_CMD = 'kollacli'
KOLLA_SHELL_DIR = 'kollacli'
HOSTS_FNAME = 'test_hosts'
@ -51,13 +51,13 @@ class KollaCliTest(testtools.TestCase):
self._set_cmd_prefix()
# make sure hosts and zones yaml files exist
# and clear them out
# make sure inventory dirs exists and remove inventory file
self._init_dir(etc_path)
hosts_path = etc_path + '/hosts.yml'
self._init_file(hosts_path)
zones_path = etc_path + '/zones.yml'
self._init_file(zones_path)
etc_ansible_path = os.path.join(etc_path, 'ansible/')
self._init_dir(etc_ansible_path)
etc_inventory_path = os.path.join(etc_ansible_path, 'inventory/')
self._init_dir(etc_inventory_path)
self._init_file(os.path.join(etc_inventory_path, 'inventory.p'))
def tearDown(self):
self._restore_env_var()
@ -73,73 +73,12 @@ class KollaCliTest(testtools.TestCase):
% (msg, full_cmd)))
return msg
def get_test_hosts(self):
"""get hosts from test_hosts file
host is {hostname: net_addr}
"""
hosts = None
if not os.path.exists(HOSTS_FNAME):
self.log.error('test_hosts file not found, are you running' +
'the tests in the tests directory?')
return hosts
with open(HOSTS_FNAME, 'r') as f:
hosts = self.TestHosts()
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
tokens = line.split()
self.assertIs(True, len(tokens) > 2,
'%s expected 3 params on line: %s'
% (HOSTS_FNAME, line))
hostname = tokens[0]
net_addr = tokens[1]
pwd = tokens[2]
hosts.add(hostname, net_addr)
hosts.set_password(hostname, pwd)
return hosts
class TestHosts(object):
"""test representation of host data"""
info = {}
def __init__(self):
self.info = {}
def remove(self, name):
del self.info[name]
def add(self, name, ip, zone='', services=[]):
if name not in self.info:
self.info[name] = {}
self.info[name]['net'] = ip
self.info[name]['zone'] = zone
self.info[name]['services'] = services
def get_ip(self, name):
return self.info[name]['net']
def get_zone(self, name):
return self.info[name]['zone']
def get_services(self, name):
return self.info[name]['services']
def get_hostnames(self):
return self.info.keys()
def set_password(self, name, password):
self.info[name]['pwd'] = password
def get_password(self, name):
return self.info[name]['pwd']
# PRIVATE FUNCTIONS ----------------------------------------------------
def _setup_env_var(self):
new_etc_path = utils.get_client_etc() + TEST_SUFFIX
os.environ[ENV_ETC] = new_etc_path
etc_path = utils.get_client_etc()
if not etc_path.endswith(TEST_SUFFIX):
etc_path = os.path.join(etc_path, TEST_SUFFIX)
os.environ[ENV_ETC] = etc_path
def _restore_env_var(self):
etc_path = utils.get_client_etc()
@ -170,12 +109,12 @@ class KollaCliTest(testtools.TestCase):
return (retval, msg)
def _init_file(self, filepath):
with open(filepath, 'w') as f:
f.close()
if os.path.exists(filepath):
os.remove(filepath)
def _init_dir(self, path):
if not os.path.isdir(path):
os.makedirs(path)
os.mkdir(path)
def _set_cmd_prefix(self):
"""Select the command to invoke the kollacli
@ -210,9 +149,9 @@ class KollaCliTest(testtools.TestCase):
os_kolla_dir = cwd.rsplit('/', 1)[0]
shell_dir = os_kolla_dir + '/%s/' % KOLLA_SHELL_DIR
shell_path = shell_dir + 'shell.py'
shell_path = os.path.join(shell_dir, 'shell.py')
python_path = os_kolla_dir + VENV_PY_PATH
python_path = os.path.join(os_kolla_dir, VENV_PY_PATH)
self.log.debug('shell_path: %s' % shell_path)
self.log.debug('python_path: %s' % python_path)
@ -225,3 +164,67 @@ class KollaCliTest(testtools.TestCase):
self.assertEqual(0, 1,
'no kollacli shell command found. Aborting tests')
class TestHosts(object):
"""host systems for testing
This class can either be used for metadata to hold info about test hosts,
or can be loaded from a test file for info on actual test host machines.
"""
def __init__(self):
self.info = {}
def remove(self, name):
del self.info[name]
def add(self, name):
if name not in self.info:
self.info[name] = {'groups': [],
'pwd': '',
}
def get_groups(self, name):
return self.info[name]['groups']
def add_group(self, name, group):
if group not in self.info[name]['groups']:
self.info[name]['groups'].append(group)
def remove_group(self, name, group):
if group in self.info[name]['groups']:
self.info[name]['groups'].remove(group)
def get_hostnames(self):
return self.info.keys()
def set_password(self, name, password):
self.info[name]['pwd'] = password
def get_password(self, name):
return self.info[name]['pwd']
def load(self):
"""load hosts from test_hosts file
"""
hosts = None
if not os.path.exists(HOSTS_FNAME):
self.log.error('test_hosts file not found, are you running ' +
'the tests in the tests directory?')
return hosts
with open(HOSTS_FNAME, 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
tokens = line.split()
if len(tokens) != 3:
raise Exception('%s expected 3 params on line: %s'
% (HOSTS_FNAME, line))
hostname = tokens[0]
pwd = tokens[2]
self.add(hostname)
self.set_password(hostname, pwd)

View File

@ -13,85 +13,97 @@
# under the License.
#
from common import KollaCliTest
import unittest
from common import TestHosts
import json
import unittest
class TestFunctional(KollaCliTest):
def test_host_add_remove(self):
hosts = self.TestHosts()
hosts = TestHosts()
msg = self.run_client_cmd('host list')
msg = self.run_client_cmd('host list -f json')
self._check_cli_output(hosts, msg)
hostname = 'host_test1'
ip_addr = '1.1.1.1'
hosts.add(hostname, ip_addr)
self.run_client_cmd('host add %s %s' % (hostname, ip_addr))
host1 = 'host_test1'
host2 = 'host_test2'
msg = self.run_client_cmd('host list')
group1 = 'control'
group2 = 'network'
group3 = 'compute'
hosts.add(host1)
hosts.add_group(host1, group1)
self.run_client_cmd('host add %s %s' % (host1, group1))
msg = self.run_client_cmd('host list -f json')
self._check_cli_output(hosts, msg)
hostname = 'host_test2'
ip_addr = '2.2.2.2'
hosts.add(hostname, ip_addr)
self.run_client_cmd('host add %s %s' % (hostname, ip_addr))
msg = self.run_client_cmd('host list')
hosts.add_group(host1, group2)
self.run_client_cmd('host add %s %s' % (host1, group2))
msg = self.run_client_cmd('host list -f json')
self._check_cli_output(hosts, msg)
hostname = 'host_test2'
hosts.remove(hostname)
self.run_client_cmd('host remove %s' % hostname)
msg = self.run_client_cmd('host list')
hosts.remove_group(host1, group1)
self.run_client_cmd('host remove %s %s' % (host1, group1))
msg = self.run_client_cmd('host list -f json')
self._check_cli_output(hosts, msg)
hostname = 'host_test1'
hosts.remove(hostname)
self.run_client_cmd('host remove %s' % hostname)
msg = self.run_client_cmd('host list')
hosts.add(host2)
hosts.add_group(host2, group3)
self.run_client_cmd('host add %s %s' % (host2, group3))
msg = self.run_client_cmd('host list -f json')
self._check_cli_output(hosts, msg)
def test_host_setzone(self):
hosts = self.TestHosts()
hostname = 'host_test1'
ip_addr = '1.1.1.1'
zonename = 'test_zone1'
hosts.add(hostname, ip_addr, zonename)
self.run_client_cmd('zone add %s' % zonename)
self.run_client_cmd('host add %s %s' % (hostname, ip_addr))
self.run_client_cmd('host setzone %s %s' % (hostname, zonename))
msg = self.run_client_cmd('host list')
hosts.remove(host2)
self.run_client_cmd('host remove %s %s' % (host2, group3))
msg = self.run_client_cmd('host list -f json')
self._check_cli_output(hosts, msg)
zonename = 'test_zone2'
hosts.add(hostname, ip_addr, zonename)
self.run_client_cmd('zone add %s' % zonename)
self.run_client_cmd('host setzone %s %s' % (hostname, zonename))
msg = self.run_client_cmd('host list')
hosts.remove(host1)
self.run_client_cmd('host remove %s' % host1)
msg = self.run_client_cmd('host list -f json')
self._check_cli_output(hosts, msg)
zonename = ''
hosts.add(hostname, ip_addr, zonename)
self.run_client_cmd('host clearzone %s' % hostname)
msg = self.run_client_cmd('host list')
self._check_cli_output(hosts, msg)
# def test_host_setzone(self):
# hosts = self.TestHosts()
# hostname = 'host_test1'
# ip_addr = '1.1.1.1'
# zonename = 'test_zone1'
# hosts.add(hostname, ip_addr, zonename)
# self.run_client_cmd('zone add %s' % zonename)
#
# self.run_client_cmd('host add %s %s' % (hostname, ip_addr))
# self.run_client_cmd('host setzone %s %s' % (hostname, zonename))
# msg = self.run_client_cmd('host list')
# self._check_cli_output(hosts, msg)
#
# zonename = 'test_zone2'
# hosts.add(hostname, ip_addr, zonename)
# self.run_client_cmd('zone add %s' % zonename)
#
# self.run_client_cmd('host setzone %s %s' % (hostname, zonename))
# msg = self.run_client_cmd('host list')
# self._check_cli_output(hosts, msg)
#
# zonename = ''
# hosts.add(hostname, ip_addr, zonename)
# self.run_client_cmd('host clearzone %s' % hostname)
# msg = self.run_client_cmd('host list')
# self._check_cli_output(hosts, msg)
def test_host_install(self):
test_hosts = self.get_test_hosts()
test_hosts = TestHosts()
test_hosts.load()
if not test_hosts:
self.log.info('no test_hosts file found, skipping test')
return
hostname = test_hosts.get_hostnames()[0]
net_addr = test_hosts.get_ip(hostname)
pwd = test_hosts.get_password(hostname)
self.run_client_cmd('host add %s %s' % (hostname, net_addr))
self.run_client_cmd('host add %s control' % (hostname))
# check if host is installed
msg = self.run_client_cmd('host check %s' % hostname, True)
@ -117,60 +129,45 @@ class TestFunctional(KollaCliTest):
self.assertIn('ERROR:', msg, 'Uninstall failed on host: (%s)'
% hostname)
def _check_cli_output(self, hosts, cli_output):
def _check_cli_output(self, exp_hosts, cli_output):
"""Verify cli data against model data
The host list cli output looks like this:
+-----------+---------+------+
| Host Name | Address | Zone |
+-----------+---------+------+
| foobar | 2.2.2.2 | |
| foo | 1.1.1.1 | |
+-----------+---------+------+
$ host list -f json
[{"Host Name": "foo", "Groups": ["control", "network"]}]
"""
# check for any host in cli output that shouldn't be there
cli_lines = cli_output.split('\n')
exp_hosts = hosts.get_hostnames()
for cli_line in cli_lines:
if ('|' not in cli_line or
cli_line.startswith('+') or
cli_line.startswith('| Host Name ')):
continue
cli_host = cli_line.split('|')[1].strip()
if cli_host:
self.assertIn(cli_host, exp_hosts,
'unexpected host: %s, found in cli output: %s'
% (cli_host, cli_lines))
cli_hosts = json.loads(cli_output)
for hostname in exp_hosts:
exp_ip = hosts.get_ip(hostname)
exp_zone = hosts.get_zone(hostname)
exp_hostnames = exp_hosts.get_hostnames()
if not exp_hostnames:
if len(cli_hosts) == 1:
cli_hostname = cli_hosts[0]['Host Name']
if not cli_hostname:
# both cli and expected hosts are None
return
hostname_found = False
for cli_line in cli_lines:
if ('|' not in cli_line or
cli_line.startswith('+') or
cli_line.startswith('| Host Name ')):
continue
for cli_host in cli_hosts:
cli_hostname = cli_host['Host Name']
self.assertIn(cli_hostname, exp_hostnames,
'unexpected host: %s, found in cli output: %s'
% (cli_hostname, cli_output))
tokens = cli_line.split('|')
if tokens[1].strip() == hostname:
hostname_found = True
# check that all expected hosts are in the output
for exp_hostname in exp_hosts.get_hostnames():
exp_host_found = False
for cli_host in cli_hosts:
if exp_hostname == cli_host['Host Name']:
exp_host_found = True
cli_groups = cli_host['Groups']
exp_groups = exp_hosts.get_groups(exp_hostname)
self.assertEqual(exp_groups, cli_groups)
# check network address
yaml_ip = tokens[2].strip()
self.assertEqual(exp_ip, yaml_ip,
'incorrect ip address in cli output')
# check zone
yaml_zone = tokens[3].strip()
self.assertEqual(exp_zone, yaml_zone,
'incorrect zone in cli output')
self.assertTrue(hostname_found,
self.assertTrue(exp_host_found,
'hostname: %s not in cli output: \n%s'
% (hostname, cli_output))
% (exp_hostname, cli_output))
if __name__ == '__main__':
unittest.main()