From 755cf0f96cfcff3cdb654e25fbc83ada7b4b70e6 Mon Sep 17 00:00:00 2001 From: Steve Noyes Date: Fri, 14 Aug 2015 17:33:22 -0400 Subject: [PATCH] Summary: add 4 new cli group commands Description: - new commands: - group add / remove - group listservices - group listhosts - make jsonpickle output read-able - add try/excepts in host.py to better debug errors - remove zones logic --- kollacli/ansible/inventory.py | 66 +++++++---- kollacli/group.py | 113 ++++++++++++++++++ kollacli/host.py | 217 +++++++++++++--------------------- setup.cfg | 6 +- tests/host.py | 27 ----- 5 files changed, 244 insertions(+), 185 deletions(-) create mode 100644 kollacli/group.py diff --git a/kollacli/ansible/inventory.py b/kollacli/ansible/inventory.py index 3370e73..0460562 100644 --- a/kollacli/ansible/inventory.py +++ b/kollacli/ansible/inventory.py @@ -247,9 +247,13 @@ class Inventory(object): # if this is called many times you will have an unpleasant # file handle leak tmp_filehandle, tmp_path = mkstemp() + + # 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) with open(tmp_path, 'w') as tmp_file: - tmp_file.write(data) + tmp_file.write(pretty_data) shutil.copyfile(tmp_path, inventory_path) os.remove(tmp_path) except Exception as e: @@ -284,6 +288,17 @@ class Inventory(object): def get_hostnames(self): return self._hosts.keys() + def get_host_groups(self): + """return { hostname : groupnames }""" + + host_groups = {} + for host in self._hosts.values(): + host_groups[host.name] = [] + groups = self.get_groups(host) + for group in groups: + host_groups[host.name].append(group.name) + return host_groups + def get_host(self, hostname): host = None if hostname in self._hosts: @@ -318,6 +333,14 @@ class Inventory(object): if group_count == 0: del self._hosts[hostname] + def add_group(self, groupname): + if groupname not in self._groups: + self._groups[groupname] = Group(groupname) + + def remove_group(self, groupname): + if groupname in self._groups: + del self._groups[groupname] + def get_groups(self, host=None): """return all groups containing host @@ -325,31 +348,30 @@ class Inventory(object): """ if not host: return self._groups.values() - host_groups = self.get_host_groups([host]) - groupnames = host_groups[host.name] + groups = [] - for groupname in groupnames: - groups.append(self._groups[groupname]) + for group in self._groups.values(): + if host.name in group.get_hostnames(): + groups.append(group) return groups - def get_host_groups(self, hosts): - """returns a dict: { hostname : [groupnames] }""" - host_groups = self._get_host_groups(hosts, self._groups.values()) - return host_groups + def get_group_services(self): + """return { groupname : [servicenames] }""" + group_services = {} + for group in self._groups.values(): + group_services[group.name] = [] + for child in group.children: + group_services[group.name].append(child.name) + return group_services - def _get_host_groups(self, hosts, groups): - host_groups = {} - for group in groups: - if group.children: - hosts_children = self._get_host_groups(hosts, group.children) - host_groups.update(hosts_children) - for host in hosts: - if host.name in group._hosts: - if host.name not in host_groups: - host_groups[host.name] = [] - host_groups[host.name].append(group.name) - - return host_groups + def get_group_hosts(self): + """return { groupname : [hostnames] }""" + group_hosts = {} + for group in self._groups.values(): + group_hosts[group.name] = [] + for host in group.get_hosts(): + group_hosts[group.name].append(host.name) + return group_hosts def get_ansible_json(self): """generate json inventory for ansible diff --git a/kollacli/group.py b/kollacli/group.py new file mode 100644 index 0000000..c7bd382 --- /dev/null +++ b/kollacli/group.py @@ -0,0 +1,113 @@ +# 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 + +from kollacli.ansible.inventory import Inventory +from kollacli.exceptions import CommandError + +from cliff.command import Command +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='', + help='group') + return parser + + def take_action(self, parsed_args): + try: + groupname = parsed_args.groupname.strip() + + inventory = Inventory.load() + inventory.add_group(groupname) + Inventory.save(inventory) + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) + + +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='', + help='group name') + return parser + + def take_action(self, parsed_args): + try: + groupname = parsed_args.groupname.strip() + inventory = Inventory.load() + inventory.remove_group(groupname) + Inventory.save(inventory) + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) + + +class GroupListservices(Lister): + """List all groups and their services""" + + log = logging.getLogger(__name__) + + def take_action(self, parsed_args): + try: + inventory = Inventory.load() + + data = [] + group_services = inventory.get_group_services() + if group_services: + for (groupname, servicenames) in group_services.items(): + data.append((groupname, servicenames)) + else: + data.append(('', '')) + return (('Group Name', 'Services'), sorted(data)) + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) + + +class GroupListhosts(Lister): + """List all groups and their hosts""" + + log = logging.getLogger(__name__) + + def take_action(self, parsed_args): + try: + inventory = Inventory.load() + + data = [] + group_hosts = inventory.get_group_hosts() + if group_hosts: + for (groupname, hostnames) in group_hosts.items(): + data.append((groupname, hostnames)) + else: + data.append(('', '')) + return (('Group Name', 'Hosts'), sorted(data)) + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) diff --git a/kollacli/host.py b/kollacli/host.py index 757aea7..9c77d3e 100644 --- a/kollacli/host.py +++ b/kollacli/host.py @@ -14,28 +14,21 @@ import argparse import getpass import logging +import traceback from kollacli.ansible.inventory import Inventory -from kollacli import exceptions -from kollacli.i18n import _ -from kollacli.objects.zones import Zones +from kollacli.exceptions import CommandError from cliff.command import Command from cliff.lister import Lister def _host_not_found(log, hostname): - raise exceptions.CommandError( + raise CommandError( 'ERROR: Host (%s) not found. ' % hostname + 'Please add it with "host add"') -def _zone_not_found(log, zonename): - raise exceptions.CommandError( - 'ERROR: Zone (%s) not found. ' % zonename + - 'Please add it with "zone add"') - - class HostAdd(Command): """Add host to open-stack-kolla""" log = logging.getLogger(__name__) @@ -49,12 +42,17 @@ class HostAdd(Command): return parser def take_action(self, parsed_args): - hostname = parsed_args.hostname.strip() - groupname = parsed_args.groupname.strip() + try: + hostname = parsed_args.hostname.strip() + groupname = parsed_args.groupname.strip() - inventory = Inventory.load() - inventory.add_host(hostname, groupname) - Inventory.save(inventory) + inventory = Inventory.load() + inventory.add_host(hostname, groupname) + Inventory.save(inventory) + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) class HostRemove(Command): @@ -74,13 +72,18 @@ class HostRemove(Command): return parser def take_action(self, parsed_args): - hostname = parsed_args.hostname.strip() - groupname = None - if parsed_args.groupname: - groupname = parsed_args.groupname.strip() - inventory = Inventory.load() - inventory.remove_host(hostname, groupname) - Inventory.save(inventory) + try: + hostname = parsed_args.hostname.strip() + groupname = None + if parsed_args.groupname: + groupname = parsed_args.groupname.strip() + inventory = Inventory.load() + inventory.remove_host(hostname, groupname) + Inventory.save(inventory) + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) class HostList(Lister): @@ -89,90 +92,21 @@ class HostList(Lister): log = logging.getLogger(__name__) def take_action(self, parsed_args): - inventory = Inventory.load() + try: + inventory = Inventory.load() - hosts = inventory.get_hosts() - - data = [] - host_groups = inventory.get_host_groups(hosts) - if host_groups: - for (hostname, groupnames) in host_groups.items(): - data.append((hostname, groupnames)) - else: - data.append(('', '')) - return (('Host Name', 'Groups'), sorted(data)) - - -class HostSetzone(Command): - """Add a host to a zone""" - - log = logging.getLogger(__name__) - - def get_parser(self, prog_name): - parser = super(HostSetzone, self).get_parser(prog_name) - parser.add_argument('hostname', metavar='', help='host name') - parser.add_argument('zone', metavar='[zone]', help='zone name') - return parser - - def take_action(self, parsed_args): - hostname = parsed_args.hostname.strip() - zonename = parsed_args.zone.strip() - - if zonename not in Zones().get_all(): - _zone_not_found(self.log, zonename) - return False - - hosts = Inventory() - host = hosts.get_host(hostname) - if not host: - _host_not_found(self.log, hostname) - return False - - host.zone = zonename - hosts.save() - - -class HostClearzone(Command): - """Clear the zone from a host""" - - log = logging.getLogger(__name__) - - def get_parser(self, prog_name): - parser = super(HostClearzone, self).get_parser(prog_name) - parser.add_argument('hostname', metavar='', help='host name') - return parser - - def take_action(self, parsed_args): - hostname = parsed_args.hostname.strip() - - hosts = Inventory() - host = hosts.get_host(hostname) - if not host: - _host_not_found(self.log, hostname) - return False - - host.zone = '' - hosts.save() - - -class HostAddservice(Command): - """add service to a host""" - - log = logging.getLogger(__name__) - - def take_action(self, parsed_args): - self.log.info(_('host addservice')) - self.app.stdout.write(parsed_args) - - -class HostRemoveservice(Command): - """Remove service from a host""" - - log = logging.getLogger(__name__) - - def take_action(self, parsed_args): - self.log.info(_('host removeservice')) - self.app.stdout.write(parsed_args) + data = [] + host_groups = inventory.get_host_groups() + if host_groups: + for (hostname, groupnames) in host_groups.items(): + data.append((hostname, groupnames)) + else: + data.append(('', '')) + return (('Host Name', 'Groups'), sorted(data)) + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) class HostCheck(Command): @@ -186,15 +120,20 @@ class HostCheck(Command): return parser def take_action(self, parsed_args): - hostname = parsed_args.hostname.strip() + try: + hostname = parsed_args.hostname.strip() - inventory = Inventory.load() - host = inventory.get_host(hostname) - if not host: - _host_not_found(self.log, hostname) - return False + inventory = Inventory.load() + host = inventory.get_host(hostname) + if not host: + _host_not_found(self.log, hostname) + return False - host.check() + host.check() + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) class HostInstall(Command): @@ -209,19 +148,24 @@ class HostInstall(Command): return parser def take_action(self, parsed_args): - hostname = parsed_args.hostname.strip() - inventory = Inventory.load() - host = inventory.get_host(hostname) - if not host: - _host_not_found(self.log, hostname) - return False + try: + hostname = parsed_args.hostname.strip() + inventory = Inventory.load() + host = inventory.get_host(hostname) + if not host: + _host_not_found(self.log, hostname) + return False - if parsed_args.insecure: - password = parsed_args.insecure.strip() - else: - password = getpass.getpass('Root password for %s: ' % hostname) + if parsed_args.insecure: + password = parsed_args.insecure.strip() + else: + password = getpass.getpass('Root password for %s: ' % hostname) - host.install(password) + host.install(password) + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) class HostUninstall(Command): @@ -236,15 +180,20 @@ class HostUninstall(Command): return parser def take_action(self, parsed_args): - hostname = parsed_args.hostname.strip() - inventory = Inventory.load() - host = inventory.get_host(hostname) - if not host: - _host_not_found(self.log, hostname) - return False + try: + hostname = parsed_args.hostname.strip() + inventory = Inventory.load() + host = inventory.get_host(hostname) + if not host: + _host_not_found(self.log, hostname) + return False - if parsed_args.insecure: - password = parsed_args.insecure.strip() - else: - password = getpass.getpass('Root password for %s: ' % hostname) - host.uninstall(password) + if parsed_args.insecure: + password = parsed_args.insecure.strip() + else: + password = getpass.getpass('Root password for %s: ' % hostname) + host.uninstall(password) + except CommandError as e: + raise e + except Exception as e: + raise Exception(traceback.format_exc()) diff --git a/setup.cfg b/setup.cfg index e82a5a7..1141cd8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,11 +31,13 @@ console_scripts = kollacli = kollacli.shell:main kolla.cli = + group_add = kollacli.group:GroupAdd + group_remove = kollacli.group:GroupRemove + group_listservices = kollacli.group:GroupListservices + group_listhosts = kollacli.group:GroupListhosts host_add = kollacli.host:HostAdd host_remove = kollacli.host:HostRemove host_list = kollacli.host:HostList - host_setzone = kollacli.host:HostSetzone - host_clearzone = kollacli.host:HostClearzone host_addservice = kollacli.host:HostAddservice host_removeservice = kollacli.host:HostRemoveservice host_check = kollacli.host:HostCheck diff --git a/tests/host.py b/tests/host.py index 5bbf59e..2878c41 100644 --- a/tests/host.py +++ b/tests/host.py @@ -66,33 +66,6 @@ class TestFunctional(KollaCliTest): 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') -# 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 = TestHosts() test_hosts.load()