diff --git a/kollacli/api/exceptions.py b/kollacli/api/exceptions.py index 78bdbe4..c9f05b1 100644 --- a/kollacli/api/exceptions.py +++ b/kollacli/api/exceptions.py @@ -53,7 +53,7 @@ class HostsSshCheckError(ClientException): for hostname in hostnames: failed_hosts = ''.join([failed_hosts, comma, hostname]) comma = ',' - message = (u._('host(s) ssh check failed: {hosts}') + message = (u._('Host(s) ssh check failed: {hosts}') .format(hosts=failed_hosts)) super(HostsSshCheckError, self).__init__(message, *args) @@ -75,6 +75,6 @@ class FailedOperation(ClientException): class MissingArgument(ClientException): """Missing argument""" def __init__(self, argname, *args): - message = (u._('argument is missing: {name}') + message = (u._('Argument is missing: {name}') .format(name=argname)) super(MissingArgument, self).__init__(message, *args) diff --git a/kollacli/api/group.py b/kollacli/api/group.py index aa71690..0fe7843 100644 --- a/kollacli/api/group.py +++ b/kollacli/api/group.py @@ -11,6 +11,7 @@ # 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 copy import copy import kollacli.i18n as u from kollacli.api.exceptions import MissingArgument @@ -23,8 +24,8 @@ class GroupApi(object): class Group(object): def __init__(self, groupname, servicenames, hostnames): self.name = groupname - self.servicenames = servicenames - self.hostnames = hostnames + self._servicenames = servicenames + self._hostnames = hostnames def get_name(self): """Get name @@ -34,21 +35,93 @@ class GroupApi(object): """ return self.name - def get_servicenames(self): - """Get service names associated with this group. + def get_services(self): + """Get names of services associated with this group. :return: service names :rtype: list of strings """ - return self.servicenames + return copy(self._servicenames) - def get_hostnames(self): - """Get host names associated with this group. + def add_service(self, servicename): + """Add service to group + + :param servicename: name of the service to add to the group + :type servicename: string + + """ + servicename = safe_decode(servicename) + inventory = Inventory.load() + inventory.validate_servicenames([servicename]) + + group_services = inventory.get_group_services() + self._servicenames = group_services[self.name] + if servicename not in self._servicenames: + # service not associated with group, add it + inventory.add_group_to_service(self.name, servicename) + Inventory.save(inventory) + + def remove_service(self, servicename): + """Remove service from group + + :param servicename: name of the service to remove from the group + :type servicename: string + + """ + servicename = safe_decode(servicename) + inventory = Inventory.load() + inventory.validate_servicenames([servicename]) + + group_services = inventory.get_group_services() + self._servicenames = group_services[self.name] + if servicename in self._servicenames: + # service is associated with group, remove it + inventory.remove_group_from_service(self.name, servicename) + Inventory.save(inventory) + + def get_hosts(self): + """Get names of hosts associated with this group. :return: host names :rtype: list of strings """ - return self.hostnames + return copy(self._hostnames) + + def add_host(self, hostname): + """Add host to group + + :param hostname: name of the host to add to the group + :type hostname: string + + """ + hostname = safe_decode(hostname) + inventory = Inventory.load() + inventory.validate_hostnames([hostname]) + + group = inventory.get_group(self.name) + self._hostnames = group.get_hostnames() + if hostname not in self._hostnames: + # host not associated with group, add it + inventory.add_host(hostname, self.name) + Inventory.save(inventory) + + def remove_host(self, hostname): + """Remove host from group + + :param hostname: name of the host to remove from the group + :type hostname: string + + """ + hostname = safe_decode(hostname) + inventory = Inventory.load() + inventory.validate_hostnames([hostname]) + + group = inventory.get_group(self.name) + self._hostnames = group.get_hostnames() + if hostname in self._hostnames: + # host is associated with group, remove it + inventory.remove_host(hostname, self.name) + Inventory.save(inventory) def group_add(self, groupname): """Add a group to the inventory @@ -58,7 +131,7 @@ class GroupApi(object): """ if not groupname: - raise MissingArgument('Group name') + raise MissingArgument(u._('Group name')) groupname = safe_decode(groupname) inventory = Inventory.load() @@ -73,7 +146,7 @@ class GroupApi(object): """ if not groupname: - raise MissingArgument('Group name') + raise MissingArgument(u._('Group name')) inventory = Inventory.load() groupname = safe_decode(groupname) @@ -97,13 +170,16 @@ class GroupApi(object): :rtype: list of Group objects """ if groupnames is None: - raise(MissingArgument(u._('Group names'))) + raise MissingArgument(u._('Group names')) groupnames = safe_decode(groupnames) return self._get_groups(groupnames) def _get_groups(self, groupnames, get_all=False): groups = [] inventory = Inventory.load() + if groupnames: + inventory.validate_groupnames(groupnames) + group_services = inventory.get_group_services() inv_groups = inventory.get_groups() for inv_group in inv_groups: diff --git a/kollacli/api/host.py b/kollacli/api/host.py index 822bc78..a4550a1 100644 --- a/kollacli/api/host.py +++ b/kollacli/api/host.py @@ -11,6 +11,8 @@ # 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 kollacli.i18n as u + from kollacli.api.exceptions import MissingArgument from kollacli.common.inventory import Inventory from kollacli.common.utils import safe_decode @@ -46,7 +48,7 @@ class HostApi(object): :param hostnames: list of strings """ if not hostnames: - raise MissingArgument('host names') + raise MissingArgument(u._('Host names')) hostnames = safe_decode(hostnames) inventory = Inventory.load() @@ -66,7 +68,7 @@ class HostApi(object): inventory = Inventory.load() if not hostnames: - raise MissingArgument('host name') + raise MissingArgument(u._('Host names')) hostnames = safe_decode(hostnames) any_changed = False @@ -97,7 +99,12 @@ class HostApi(object): :return: hosts :rtype: Host """ + if hostnames is None: + raise MissingArgument(u._('Host names')) + hostnames = safe_decode(hostnames) inventory = Inventory.load() + inventory.validate_hostnames(hostnames) + hosts = [] host_groups = inventory.get_host_groups() for hostname in hostnames: diff --git a/kollacli/api/service.py b/kollacli/api/service.py index 6464fcd..21d03b6 100644 --- a/kollacli/api/service.py +++ b/kollacli/api/service.py @@ -11,6 +11,7 @@ # 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 copy import copy import kollacli.i18n as u from kollacli.api.exceptions import MissingArgument @@ -42,8 +43,8 @@ class ServiceApi(object): childnames=[], groupnames=[]): self.name = servicename self.parentname = parentname - self.childnames = childnames - self.groupnames = groupnames + self._childnames = childnames + self._groupnames = groupnames def get_name(self): """Get name @@ -67,7 +68,7 @@ class ServiceApi(object): :return: child names :rtype: list of strings """ - return self.childnames + return copy(self._childnames) def get_groupnames(self): """Get names of the groups associated with this service @@ -75,7 +76,7 @@ class ServiceApi(object): :return: group names :rtype: list of strings """ - return self.groupnames + return copy(self._groupnames) def service_get_all(self): """Get all services in the inventory @@ -94,13 +95,16 @@ class ServiceApi(object): :rtype: list of Service objects """ if servicenames is None: - raise(MissingArgument(u._('Service names'))) + raise MissingArgument(u._('Service names')) servicenames = safe_decode(servicenames) return self._get_services(servicenames) def _get_services(self, servicenames, get_all=False): services = [] inventory = Inventory.load() + if servicenames: + inventory.validate_servicenames(servicenames) + inv_services = inventory.get_services() inv_subservices = inventory.get_sub_services() diff --git a/kollacli/commands/group.py b/kollacli/commands/group.py index 3be1a0b..bd937ad 100644 --- a/kollacli/commands/group.py +++ b/kollacli/commands/group.py @@ -18,8 +18,6 @@ import kollacli.i18n as u from kollacli.api.client import ClientApi from kollacli.api.exceptions import ClientException from kollacli.commands.exceptions import CommandError -from kollacli.common.inventory import Inventory -from kollacli.common.utils import convert_to_unicode from cliff.command import Command from cliff.lister import Lister @@ -79,14 +77,13 @@ class GroupAddhost(Command): def take_action(self, parsed_args): try: groupname = parsed_args.groupname.strip() - groupname = convert_to_unicode(groupname) hostname = parsed_args.hostname.strip() - hostname = convert_to_unicode(hostname) - inventory = Inventory.load() - inventory.add_host(hostname, groupname) - Inventory.save(inventory) - except CommandError as e: - raise e + + group = CLIENT.group_get([groupname])[0] + group.add_host(hostname) + + except ClientException as e: + raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc()) @@ -105,15 +102,13 @@ class GroupRemovehost(Command): def take_action(self, parsed_args): try: groupname = parsed_args.groupname.strip() - groupname = convert_to_unicode(groupname) hostname = parsed_args.hostname.strip() - hostname = convert_to_unicode(hostname) - inventory = Inventory.load() - inventory.remove_host(hostname, groupname) - Inventory.save(inventory) - except CommandError as e: - raise e + group = CLIENT.group_get([groupname])[0] + group.remove_host(hostname) + + except ClientException as e: + raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc()) @@ -129,7 +124,7 @@ class GroupListhosts(Lister): data = [] for group in groups: data.append((group.get_name(), - sorted(group.get_hostnames()))) + sorted(group.get_hosts()))) return ((u._('Group'), u._('Hosts')), sorted(data)) except ClientException as e: raise CommandError(str(e)) @@ -150,15 +145,13 @@ class GroupAddservice(Command): def take_action(self, parsed_args): try: groupname = parsed_args.groupname.strip() - groupname = convert_to_unicode(groupname) servicename = parsed_args.servicename.strip() - servicename = convert_to_unicode(servicename) - inventory = Inventory.load() - inventory.add_group_to_service(groupname, servicename) - Inventory.save(inventory) - except CommandError as e: - raise e + group = CLIENT.group_get([groupname])[0] + group.add_service(servicename) + + except ClientException as e: + raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc()) @@ -177,15 +170,13 @@ class GroupRemoveservice(Command): def take_action(self, parsed_args): try: groupname = parsed_args.groupname.strip() - groupname = convert_to_unicode(groupname) servicename = parsed_args.servicename.strip() - servicename = convert_to_unicode(servicename) - inventory = Inventory.load() - inventory.remove_group_from_service(groupname, servicename) - Inventory.save(inventory) - except CommandError as e: - raise e + group = CLIENT.group_get([groupname])[0] + group.remove_service(servicename) + + except ClientException as e: + raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc()) @@ -201,7 +192,7 @@ class GroupListservices(Lister): data = [] for group in groups: data.append((group.get_name(), - sorted(group.get_servicenames()))) + sorted(group.get_services()))) return ((u._('Group'), u._('Services')), sorted(data)) except ClientException as e: raise CommandError(str(e)) diff --git a/kollacli/commands/service.py b/kollacli/commands/service.py index 609852a..49c64a6 100644 --- a/kollacli/commands/service.py +++ b/kollacli/commands/service.py @@ -18,8 +18,6 @@ import kollacli.i18n as u from kollacli.api.client import ClientApi from kollacli.api.exceptions import ClientException from kollacli.commands.exceptions import CommandError -from kollacli.common.inventory import Inventory -from kollacli.common.utils import convert_to_unicode from cliff.command import Command from cliff.lister import Lister @@ -45,17 +43,13 @@ class ServiceAddGroup(Command): def take_action(self, parsed_args): try: groupname = parsed_args.groupname.strip() - groupname = convert_to_unicode(groupname) servicename = parsed_args.servicename.strip() - servicename = convert_to_unicode(servicename) - inventory = Inventory.load() + group = CLIENT.group_get([groupname])[0] + group.add_service(servicename) - inventory.add_group_to_service(groupname, servicename) - - Inventory.save(inventory) - except CommandError as e: - raise e + except ClientException as e: + raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc()) @@ -74,17 +68,13 @@ class ServiceRemoveGroup(Command): def take_action(self, parsed_args): try: groupname = parsed_args.groupname.strip() - groupname = convert_to_unicode(groupname) servicename = parsed_args.servicename.strip() - servicename = convert_to_unicode(servicename) - inventory = Inventory.load() + group = CLIENT.group_get([groupname])[0] + group.remove_service(servicename) - inventory.remove_group_from_service(groupname, servicename) - - Inventory.save(inventory) - except CommandError as e: - raise e + except ClientException as e: + raise CommandError(str(e)) except Exception as e: raise Exception(traceback.format_exc()) diff --git a/kollacli/common/ansible/job.py b/kollacli/common/ansible/job.py index 9cdb556..c000795 100644 --- a/kollacli/common/ansible/job.py +++ b/kollacli/common/ansible/job.py @@ -11,7 +11,6 @@ # 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 fcntl import json import logging @@ -20,6 +19,8 @@ import subprocess # nosec import tempfile import time +import kollacli.i18n as u + from kollacli.common.inventory import remove_temp_inventory from kollacli.common.utils import get_admin_uids from kollacli.common.utils import safe_decode @@ -176,7 +177,8 @@ class AnsibleJob(object): elif action == ACTION_TASK_START: return self._format_task_start(packet) else: - raise Exception('Invalid action [%s] from callback' % action) + raise Exception(u._('Invalid action [{action}] from callback') + .format(action=action)) def _format_include_file(self, packet): return 'included: %s' % packet['filename'] diff --git a/kollacli/common/inventory.py b/kollacli/common/inventory.py index 9f058b0..9d23484 100644 --- a/kollacli/common/inventory.py +++ b/kollacli/common/inventory.py @@ -885,10 +885,31 @@ class Inventory(object): def validate_hostnames(self, hostnames): if not hostnames: - raise MissingArgument(u._('host name(s)')) + raise MissingArgument(u._('Host name(s)')) invalid_hosts = [] for hostname in hostnames: if hostname not in self._hosts: invalid_hosts.append(hostname) if invalid_hosts: raise NotInInventory(u._('Host'), invalid_hosts) + + def validate_groupnames(self, groupnames): + if not groupnames: + raise MissingArgument(u._('Group name(s)')) + invalid_groups = [] + for groupname in groupnames: + if groupname not in self._groups: + invalid_groups.append(groupname) + if invalid_groups: + raise NotInInventory(u._('Group'), invalid_groups) + + def validate_servicenames(self, servicenames): + if not servicenames: + raise MissingArgument(u._('Service name(s)')) + invalid_services = [] + for servicename in servicenames: + if (servicename not in self._services and + servicename not in self._sub_services): + invalid_services.append(servicename) + if invalid_services: + raise NotInInventory(u._('Service'), invalid_services)