alignak_backend_client/backend_client.py
def create_update_resource(self, resource_name, name, update=False):
        # pylint: disable=too-many-return-statements, too-many-locals
        # pylint: disable=too-many-nested-blocks
        """Create or update a specific resource

        :param resource_name: backend resource endpoint (eg. host, user, ...)
        :param name: name of the resource to create/update
        :param update: True to update an existing resource, else will try to create
        :return:
        """
        if self.data is None:
            self.data = {}

        # If some data are provided, try to get them
        json_data = None
        if self.data:
            try:
                # Data may be provided on the command line or from a file
                if self.data == 'stdin':
                    input_file = sys.stdin
                else:
                    path = os.path.join(self.folder or os.getcwd(), self.data)
                    input_file = open(path)

                json_data = json.load(input_file)
                logger.info("Got provided data: %s", json_data)
                if input_file is not sys.stdin:
                    input_file.close()
            except IOError:
                logger.error("Error reading data file: %s", path)
                return False
            except ValueError:
                logger.error("Error malformed data file: %s", path)
                return False

        if name is None and json_data is None:
            logger.error("-> can not add/update a %s without a name and/or data!", resource_name)
            return False

        # Manage provided templates
        used_templates = []
        if self.templates is not None:
            logger.info("Searching the %s template(s): %s",
                        resource_name, self.templates)
            for template in self.templates:
                response = self.backend.get(
                    resource_name, params={'where': json.dumps({'name': template,
                                                                '_is_template': True})})
                if response['_items']:
                    used_templates.append(response['_items'][0]['_id'])

                    logger.info("-> found %s template '%s': %s",
                                resource_name, template, response['_items'][0]['_id'])
                else:
                    logger.error("-> %s required template not found '%s'", resource_name, template)
                    return False

        try:
            if json_data is None:
                json_data = {'name': name}

            if not isinstance(json_data, list):
                json_data = [json_data]

            logger.info("Got %d %ss", len(json_data), resource_name)
            count = 0
            for json_item in json_data:
                logger.info("-> json item: %s", json_item)
                if resource_name not in ['history', 'userrestrictrole', 'logcheckresult'] \
                        and name is None and ('name' not in json_item or not json_item['name']):
                    logger.warning("-> unnamed '%s'!", resource_name)
                    continue

                # Manage resource name
                item_name = name
                if 'name' in json_item:
                    item_name = json_item['name']

                # Got the item name
                params = {'name': item_name}

                if resource_name == 'service' and 'host' in json_item:
                    # Get host from name
                    host_search = {'name': json_item['host']}
                    if '_is_template' in json_item:
                        host_search.update({'_is_template': json_item['_is_template']})
                    logger.info("Host search: %s", host_search)
                    resp_host = self.backend.get(
                        'host', params={'where': json.dumps(host_search)})
                    if resp_host['_items']:
                        host = resp_host['_items'][0]
                        logger.info("Got host '%s' for the service '%s'", host['name'], item_name)
                    else:
                        logger.warning("Host not found: '%s' for the service: %s!",
                                       json_item['host'], item_name)
                        continue

                    params = {'name': item_name, 'host': host['_id']}

                if resource_name == 'service' and '/' in item_name:
                    splitted_name = item_name.split('/')

                    # Get host from name
                    host_search = {'name': splitted_name[0]}
                    if '_is_template' in json_item:
                        host_search.update({'_is_template': json_item['_is_template']})
                    resp_host = self.backend.get(
                        'host', params={'where': json.dumps(host_search)})
                    if resp_host['_items']:
                        host = resp_host['_items'][0]
                        logger.info("Got host '%s' for the service '%s'",
                                    splitted_name[0], splitted_name[1])
                    else:
                        logger.warning("Host not found: '%s' for the service: %s!",
                                       splitted_name[0], item_name)
                        continue

                    item_name = splitted_name[1]
                    params = {'name': item_name, 'host': host['_id']}

                if '_is_template' in json_item:
                    params.update({'_is_template': json_item['_is_template']})

                params = {'where': json.dumps(params)}

                if name:
                    logger.info("Trying to get %s: '%s', params: %s",
                                resource_name, item_name, params)
                    response = self.backend.get(resource_name, params=params)
                    if response['_items']:
                        found_item = response['_items'][0]
                        found_id = found_item['_id']
                        found_etag = found_item['_etag']
                        logger.info("-> found %s '%s': %s", resource_name, item_name, found_id)

                        if not update:
                            logger.warning("-> '%s' %s cannot be created because it already "
                                           "exists!", resource_name, item_name)
                            continue
                    else:
                        if update:
                            logger.warning("-> '%s' %s cannot be updated because it does not "
                                           "exist!", resource_name, item_name)
                            continue

                # Item data updated with provided information if some

                # Data to update
                item_data = {}
                if self.include_read_data:
                    # Include read data if required
                    item_data = found_item

                # Json provided data update existing data
                item_data.update(json_item)
                # Name is also updated (eg. for a service...)
                item_data['name'] = item_name
                # Template information if templating is required
                if used_templates:
                    item_data.update({'_templates': used_templates,
                                      '_templates_with_services': True})

                for field in item_data.copy():
                    logger.debug("Field: %s = %s", field, item_data[field])
                    # Filter Eve extra fields
                    if field in ['_created', '_updated', '_etag', '_links', '_status']:
                        item_data.pop(field)
                        continue
                    # Filter specific backend inner computed fields
                    # pylint: disable=fixme
                    # todo: list to be completed!
                    if field in ['_overall_state_id']:
                        item_data.pop(field)
                        continue

                    # Manage potential object link fields
                    if field not in ['realm', '_realm', '_templates',
                                     'command', 'host', 'service',
                                     'escalation_period', 'maintenance_period',
                                     'snapshot_period', 'check_period', 'dependency_period',
                                     'notification_period', 'host_notification_period',
                                     'escalation_period', 'service_notification_period',
                                     'host_notification_commands', 'service_notification_commands',
                                     'service_dependencies', 'users', 'usergroups',
                                     'check_command', 'event_handler', 'grafana', 'statsd']:
                        continue

                    field_values = item_data[field]
                    if not isinstance(item_data[field], list):
                        field_values = [item_data[field]]

                    found = None
                    for value in field_values:
                        logger.debug(" - %s, single value: %s", field, value)
                        try:
                            int(value, 16)
                            logger.debug(" - %s, uuid value: %s", field, value)

                            if not isinstance(item_data[field], list):
                                found = value
                            else:
                                if found is None:
                                    found = []
                                found.append(value)
                        except TypeError:
                            pass
                        except ValueError:
                            # Not an integer, consider an item name
                            field_params = {'where': json.dumps({'name': value})}
                            logger.debug(" - %s, params: %s", field, field_params)
                            if field in ['escalation_period', 'maintenance_period',
                                         'snapshot_period', 'check_period',
                                         'dependency_period', 'notification_period',
                                         'host_notification_period',
                                         'service_notification_period']:
                                response2 = self.backend.get('timeperiod', params=field_params)
                            elif field in ['_realm']:
                                response2 = self.backend.get('realm', params=field_params)
                            elif field in ['service_dependencies']:
                                response2 = self.backend.get('service', params=field_params)
                            elif field in ['users']:
                                response2 = self.backend.get('user', params=field_params)
                            elif field in ['usergroups']:
                                response2 = self.backend.get('usergroup', params=field_params)
                            elif field in ['check_command', 'event_handler',
                                           'service_notification_commands',
                                           'host_notification_commands']:
                                response2 = self.backend.get('command', params=field_params)
                            elif field in ['_templates']:
                                field_params = {'where': json.dumps({'name': value,
                                                                     '_is_template': True})}
                                response2 = self.backend.get(resource_name, params=field_params)
                            else:
                                response2 = self.backend.get(field, params=field_params)

                            if response2['_items']:
                                response2 = response2['_items'][0]
                                logger.info("Replaced %s = %s with found item _id",
                                            field, value)
                                if not isinstance(item_data[field], list):
                                    found = response2['_id']
                                else:
                                    if found is None:
                                        found = []
                                    found.append(response2['_id'])

                    if found is None:
                        logger.warning("Not found %s = %s, removing field!", field, field_values)
                        item_data.pop(field)
                    else:
                        item_data[field] = found

                if resource_name not in ['realm'] and '_realm' not in item_data:
                    logger.info("add default realm to the data")
                    item_data.update({'_realm': self.default_realm})

                if resource_name in ['realm'] and '_realm' not in item_data:
                    logger.info("add parent realm to the data")
                    item_data.update({'_parent': self.default_realm})

                if '_id' in item_data:
                    item_data.pop('_id')

                if not update:
                    # Trying to create a new element
                    if not item_data['name']:
                        item_data.pop('name')
                    logger.info("-> trying to create the %s: %s.", resource_name, item_name)
                    logger.debug("-> with: %s.", item_data)
                    if not self.dry_run:
                        try:
                            response = self.backend.post(resource_name, item_data, headers=None)
                        except BackendException as exp:
                            self.item = item_name
                            logger.error("Exception: %s", exp)
                            # logger.error("Response: %s", exp.response)
                            continue
                    else:
                        response = {'_status': 'OK', '_id': '_fake', '_etag': '_fake'}
                else:
                    if not name:
                        logger.warning("-> can not update '%s' with no name!", resource_name)
                        continue

                    # Trying to update an element
                    logger.info("-> trying to update the %s: %s.", resource_name, item_name)
                    logger.debug("-> with: %s.", item_data)
                    if not self.dry_run:
                        try:
                            headers = {'Content-Type': 'application/json', 'If-Match': found_etag}
                            response = self.backend.patch(resource_name + '/' + found_id,
                                                          item_data, headers=headers,
                                                          inception=True)
                        except BackendException as exp:
                            self.item = item_name
                            logger.exception("Exception: %s", exp)
                            # logger.error("Response: %s", exp.response)
                            continue
                    else:
                        response = {'_status': 'OK', '_id': '_fake', '_etag': '_fake'}

                if response['_status'] == 'ERR':
                    logger.warning("Response: %s", response)
                    return False

                if not update:
                    # Created a new element
                    if not self.dry_run:
                        logger.info("-> created: '%s': %s", resource_name, response['_id'])
                    else:
                        logger.info("Dry-run mode: should have created an %s '%s'",
                                    resource_name, name)
                else:
                    # Updated an element
                    if not self.dry_run:
                        logger.info("-> updated: '%s': %s", resource_name, response['_id'])
                    else:
                        logger.info("Dry-run mode: should have updated an %s '%s'",
                                    resource_name, name)
                count = count + 1

        except BackendException as exp:  # pragma: no cover, should never happen
            logger.exception("Exception: %s", exp)
            logger.error("Response: %s", exp.response)
            print("Creation/update error for  '%s' : %s" % (resource_name, name))
            print("~~~~~~~~~~~~~~~~~~~~~~~~~~")
            print("Exiting with error code: 5")
            return False

        if count == len(json_data):
            return True
        return False
def create_update_resource(self, resource_name, name, update=False):
        # pylint: disable=too-many-return-statements, too-many-locals
        # pylint: disable=too-many-nested-blocks
        """Create or update a specific resource

        :param resource_name: backend resource endpoint (eg. host, user, ...)
        :param name: name of the resource to create/update
        :param update: True to update an existing resource, else will try to create
        :return:
        """
        if self.data is None:
            self.data = {}

        # If some data are provided, try to get them
        json_data = None
        if self.data:
            try:
                # Data may be provided on the command line or from a file
                if self.data == 'stdin':
                    input_file = sys.stdin
                else:
                    path = os.path.join(self.folder or os.getcwd(), self.data)
                    input_file = open(path)

                json_data = json.load(input_file)
                logger.info("Got provided data: %s", json_data)
                if input_file is not sys.stdin:
                    input_file.close()
            except IOError:
                logger.error("Error reading data file: %s", path)
                return False
            except ValueError:
                logger.error("Error malformed data file: %s", path)
                return False

        if name is None and json_data is None:
            logger.error("-> can not add/update a %s without a name and/or data!", resource_name)
            return False

        # Manage provided templates
        used_templates = []
        if self.templates is not None:
            logger.info("Searching the %s template(s): %s",
                        resource_name, self.templates)
            for template in self.templates:
                response = self.backend.get(
                    resource_name, params={'where': json.dumps({'name': template,
                                                                '_is_template': True})})
                if response['_items']:
                    used_templates.append(response['_items'][0]['_id'])

                    logger.info("-> found %s template '%s': %s",
                                resource_name, template, response['_items'][0]['_id'])
                else:
                    logger.error("-> %s required template not found '%s'", resource_name, template)
                    return False

        try:
            if json_data is None:
                json_data = {'name': name}

            if not isinstance(json_data, list):
                json_data = [json_data]

            logger.info("Got %d %ss", len(json_data), resource_name)
            count = 0
            for json_item in json_data:
                logger.info("-> json item: %s", json_item)
                if resource_name not in ['history', 'userrestrictrole', 'logcheckresult'] \
                        and name is None and ('name' not in json_item or not json_item['name']):
                    logger.warning("-> unnamed '%s'!", resource_name)
                    continue

                # Manage resource name
                item_name = name
                if 'name' in json_item:
                    item_name = json_item['name']

                # Got the item name
                params = {'name': item_name}

                if resource_name == 'service' and 'host' in json_item:
                    # Get host from name
                    host_search = {'name': json_item['host']}
                    if '_is_template' in json_item:
                        host_search.update({'_is_template': json_item['_is_template']})
                    logger.info("Host search: %s", host_search)
                    resp_host = self.backend.get(
                        'host', params={'where': json.dumps(host_search)})
                    if resp_host['_items']:
                        host = resp_host['_items'][0]
                        logger.info("Got host '%s' for the service '%s'", host['name'], item_name)
                    else:
                        logger.warning("Host not found: '%s' for the service: %s!",
                                       json_item['host'], item_name)
                        continue

                    params = {'name': item_name, 'host': host['_id']}

                if resource_name == 'service' and '/' in item_name:
                    splitted_name = item_name.split('/')

                    # Get host from name
                    host_search = {'name': splitted_name[0]}
                    if '_is_template' in json_item:
                        host_search.update({'_is_template': json_item['_is_template']})
                    resp_host = self.backend.get(
                        'host', params={'where': json.dumps(host_search)})
                    if resp_host['_items']:
                        host = resp_host['_items'][0]
                        logger.info("Got host '%s' for the service '%s'",
                                    splitted_name[0], splitted_name[1])
                    else:
                        logger.warning("Host not found: '%s' for the service: %s!",
                                       splitted_name[0], item_name)
                        continue

                    item_name = splitted_name[1]
                    params = {'name': item_name, 'host': host['_id']}

                if '_is_template' in json_item:
                    params.update({'_is_template': json_item['_is_template']})

                params = {'where': json.dumps(params)}

                if name:
                    logger.info("Trying to get %s: '%s', params: %s",
                                resource_name, item_name, params)
                    response = self.backend.get(resource_name, params=params)
                    if response['_items']:
                        found_item = response['_items'][0]
                        found_id = found_item['_id']
                        found_etag = found_item['_etag']
                        logger.info("-> found %s '%s': %s", resource_name, item_name, found_id)

                        if not update:
                            logger.warning("-> '%s' %s cannot be created because it already "
                                           "exists!", resource_name, item_name)
                            continue
                    else:
                        if update:
                            logger.warning("-> '%s' %s cannot be updated because it does not "
                                           "exist!", resource_name, item_name)
                            continue

                # Item data updated with provided information if some

                # Data to update
                item_data = {}
                if self.include_read_data:
                    # Include read data if required
                    item_data = found_item

                # Json provided data update existing data
                item_data.update(json_item)
                # Name is also updated (eg. for a service...)
                item_data['name'] = item_name
                # Template information if templating is required
                if used_templates:
                    item_data.update({'_templates': used_templates,
                                      '_templates_with_services': True})

                for field in item_data.copy():
                    logger.debug("Field: %s = %s", field, item_data[field])
                    # Filter Eve extra fields
                    if field in ['_created', '_updated', '_etag', '_links', '_status']:
                        item_data.pop(field)
                        continue
                    # Filter specific backend inner computed fields
                    # pylint: disable=fixme
                    # todo: list to be completed!
                    if field in ['_overall_state_id']:
                        item_data.pop(field)
                        continue

                    # Manage potential object link fields
                    if field not in ['realm', '_realm', '_templates',
                                     'command', 'host', 'service',
                                     'escalation_period', 'maintenance_period',
                                     'snapshot_period', 'check_period', 'dependency_period',
                                     'notification_period', 'host_notification_period',
                                     'escalation_period', 'service_notification_period',
                                     'host_notification_commands', 'service_notification_commands',
                                     'service_dependencies', 'users', 'usergroups',
                                     'check_command', 'event_handler', 'grafana', 'statsd']:
                        continue

                    field_values = item_data[field]
                    if not isinstance(item_data[field], list):
                        field_values = [item_data[field]]

                    found = None
                    for value in field_values:
                        logger.debug(" - %s, single value: %s", field, value)
                        try:
                            int(value, 16)
                            logger.debug(" - %s, uuid value: %s", field, value)

                            if not isinstance(item_data[field], list):
                                found = value
                            else:
                                if found is None:
                                    found = []
                                found.append(value)
                        except TypeError:
                            pass
                        except ValueError:
                            # Not an integer, consider an item name
                            field_params = {'where': json.dumps({'name': value})}
                            logger.debug(" - %s, params: %s", field, field_params)
                            if field in ['escalation_period', 'maintenance_period',
                                         'snapshot_period', 'check_period',
                                         'dependency_period', 'notification_period',
                                         'host_notification_period',
                                         'service_notification_period']:
                                response2 = self.backend.get('timeperiod', params=field_params)
                            elif field in ['_realm']:
                                response2 = self.backend.get('realm', params=field_params)
                            elif field in ['service_dependencies']:
                                response2 = self.backend.get('service', params=field_params)
                            elif field in ['users']:
                                response2 = self.backend.get('user', params=field_params)
                            elif field in ['usergroups']:
                                response2 = self.backend.get('usergroup', params=field_params)
                            elif field in ['check_command', 'event_handler',
                                           'service_notification_commands',
                                           'host_notification_commands']:
                                response2 = self.backend.get('command', params=field_params)
                            elif field in ['_templates']:
                                field_params = {'where': json.dumps({'name': value,
                                                                     '_is_template': True})}
                                response2 = self.backend.get(resource_name, params=field_params)
                            else:
                                response2 = self.backend.get(field, params=field_params)

                            if response2['_items']:
                                response2 = response2['_items'][0]
                                logger.info("Replaced %s = %s with found item _id",
                                            field, value)
                                if not isinstance(item_data[field], list):
                                    found = response2['_id']
                                else:
                                    if found is None:
                                        found = []
                                    found.append(response2['_id'])

                    if found is None:
                        logger.warning("Not found %s = %s, removing field!", field, field_values)
                        item_data.pop(field)
                    else:
                        item_data[field] = found

                if resource_name not in ['realm'] and '_realm' not in item_data:
                    logger.info("add default realm to the data")
                    item_data.update({'_realm': self.default_realm})

                if resource_name in ['realm'] and '_realm' not in item_data:
                    logger.info("add parent realm to the data")
                    item_data.update({'_parent': self.default_realm})

                if '_id' in item_data:
                    item_data.pop('_id')

                if not update:
                    # Trying to create a new element
                    if not item_data['name']:
                        item_data.pop('name')
                    logger.info("-> trying to create the %s: %s.", resource_name, item_name)
                    logger.debug("-> with: %s.", item_data)
                    if not self.dry_run:
                        try:
                            response = self.backend.post(resource_name, item_data, headers=None)
                        except BackendException as exp:
                            self.item = item_name
                            logger.error("Exception: %s", exp)
                            # logger.error("Response: %s", exp.response)
                            continue
                    else:
                        response = {'_status': 'OK', '_id': '_fake', '_etag': '_fake'}
                else:
                    if not name:
                        logger.warning("-> can not update '%s' with no name!", resource_name)
                        continue

                    # Trying to update an element
                    logger.info("-> trying to update the %s: %s.", resource_name, item_name)
                    logger.debug("-> with: %s.", item_data)
                    if not self.dry_run:
                        try:
                            headers = {'Content-Type': 'application/json', 'If-Match': found_etag}
                            response = self.backend.patch(resource_name + '/' + found_id,
                                                          item_data, headers=headers,
                                                          inception=True)
                        except BackendException as exp:
                            self.item = item_name
                            logger.exception("Exception: %s", exp)
                            # logger.error("Response: %s", exp.response)
                            continue
                    else:
                        response = {'_status': 'OK', '_id': '_fake', '_etag': '_fake'}

                if response['_status'] == 'ERR':
                    logger.warning("Response: %s", response)
                    return False

                if not update:
                    # Created a new element
                    if not self.dry_run:
                        logger.info("-> created: '%s': %s", resource_name, response['_id'])
                    else:
                        logger.info("Dry-run mode: should have created an %s '%s'",
                                    resource_name, name)
                else:
                    # Updated an element
                    if not self.dry_run:
                        logger.info("-> updated: '%s': %s", resource_name, response['_id'])
                    else:
                        logger.info("Dry-run mode: should have updated an %s '%s'",
                                    resource_name, name)
                count = count + 1

        except BackendException as exp:  # pragma: no cover, should never happen
            logger.exception("Exception: %s", exp)
            logger.error("Response: %s", exp.response)
            print("Creation/update error for  '%s' : %s" % (resource_name, name))
            print("~~~~~~~~~~~~~~~~~~~~~~~~~~")
            print("Exiting with error code: 5")
            return False

        if count == len(json_data):
            return True
        return False
Create or update a specific resource

        :param resource_name: backend resource endpoint (eg. host, user, ...)
        :param name: name of the resource to create/update
        :param update: True to update an existing resource, else will try to create
        :return: