class TestRecalculateLivestate(unittest2.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.p = subprocess.Popen(
            ["uwsgi", "-w", "alignakbackend:app", "--socket", "0.0.0.0:5000", "--protocol=http", "--enable-threads"]
        )
        time.sleep(3)
        cls.backend = Backend("http://127.0.0.1:5000")
        cls.backend.login("admin", "admin", "force")
        cls.backend.delete("host", {})
        cls.backend.delete("service", {})
        cls.backend.delete("command", {})
        cls.backend.delete("livestate", {})
        cls.backend.delete("livesynthesis", {})

    def test_recalculate(self):
        # add host
        data = json.loads(open("cfg/host_srv001.json").read())
        self.backend.post("host", data)
        rh = self.backend.get("host")

        # Add command
        data = json.loads(open("cfg/command_ping.json").read())
        self.backend.post("command", data)
        # Check if command right in backend
        rc = self.backend.get("command")
        self.assertEqual(rc["_items"][0]["command_name"], "ping")

        # Add service
        data = json.loads(open("cfg/service_srv001_ping.json").read())
        data["host_name"] = rh["_items"][0]["_id"]
        data["check_command"] = rc["_items"][0]["_id"]
        self.backend.post("service", data)
        # Check if service right in backend
        rs = self.backend.get("service")
        self.assertEqual(rs["_items"][0]["service_description"], "ping")

        self.backend.delete("livestate", {})
        self.p.kill()
        time.sleep(3)
        self.p = subprocess.Popen(
            ["uwsgi", "-w", "alignakbackend:app", "--socket", "0.0.0.0:5000", "--protocol=http", "--enable-threads"]
        )
        time.sleep(3)

        # Check if livestate right recalculate
        self.backend = Backend("http://127.0.0.1:5000")
        self.backend.login("admin", "admin", "force")
        r = self.backend.get("livestate")
        self.assertEqual(len(r["_items"]), 2)
        self.assertEqual(r["_items"][1]["last_state"], "OK")
        self.assertEqual(r["_items"][1]["last_state_type"], "HARD")
        self.assertEqual(r["_items"][1]["last_check"], 0)
        self.assertEqual(r["_items"][1]["state_type"], "HARD")
        self.assertEqual(r["_items"][1]["state"], "OK")
        self.assertEqual(r["_items"][1]["host_name"], rh["_items"][0]["_id"])
        self.assertEqual(r["_items"][1]["service_description"], rs["_items"][0]["_id"])

        self.backend.delete("contact", {})
        self.p.kill()
class TestRecalculateLivestate(unittest2.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.p = subprocess.Popen(['uwsgi', '-w', 'alignakbackend:app', '--socket', '0.0.0.0:5000', '--protocol=http', '--enable-threads'])
        time.sleep(3)
        cls.backend = Backend('http://127.0.0.1:5000')
        cls.backend.login("admin", "admin", "force")
        cls.backend.delete("host", {})
        cls.backend.delete("service", {})
        cls.backend.delete("command", {})
        cls.backend.delete("livestate", {})
        cls.backend.delete("livesynthesis", {})

    def test_recalculate(self):
        # add host
        data = json.loads(open('cfg/host_srv001.json').read())
        self.backend.post("host", data)
        rh = self.backend.get('host')

        # Add command
        data = json.loads(open('cfg/command_ping.json').read())
        self.backend.post("command", data)
        # Check if command right in backend
        rc = self.backend.get('command')
        self.assertEqual(rc['_items'][0]['command_name'], "ping")

        # Add service
        data = json.loads(open('cfg/service_srv001_ping.json').read())
        data['host_name'] = rh['_items'][0]['_id']
        data['check_command'] = rc['_items'][0]['_id']
        self.backend.post("service", data)
        # Check if service right in backend
        rs = self.backend.get('service')
        self.assertEqual(rs['_items'][0]['service_description'], "ping")

        self.backend.delete("livestate", {})
        self.p.kill()
        time.sleep(3)
        self.p = subprocess.Popen(['uwsgi', '-w', 'alignakbackend:app', '--socket', '0.0.0.0:5000', '--protocol=http', '--enable-threads'])
        time.sleep(3)

        # Check if livestate right recalculate
        self.backend = Backend('http://127.0.0.1:5000')
        self.backend.login("admin", "admin", "force")
        r = self.backend.get('livestate')
        self.assertEqual(len(r['_items']), 2)
        self.assertEqual(r['_items'][1]['last_state'], 'OK')
        self.assertEqual(r['_items'][1]['last_state_type'], 'HARD')
        self.assertEqual(r['_items'][1]['last_check'], 0)
        self.assertEqual(r['_items'][1]['state_type'], 'HARD')
        self.assertEqual(r['_items'][1]['state'], 'OK')
        self.assertEqual(r['_items'][1]['host_name'], rh['_items'][0]['_id'])
        self.assertEqual(r['_items'][1]['service_description'], rs['_items'][0]['_id'])
        self.assertEqual(r['_items'][1]['type'], 'service')

        self.backend.delete("contact", {})
        self.p.kill()
示例#3
0
    def test_3_delete_connection_error(self):
        """
        Backend connection error when deleting an object...

        :return: None
        """
        print('test connection error when deleting an object')

        # Create client API
        backend = Backend(self.backend_address)
        backend.login('admin', 'admin')

        # Create a new timeperiod
        data = {
            "name":
            "Testing TP 2",
            "alias":
            "Test TP 2",
            "dateranges": [{
                u'monday': u'09:00-17:00'
            }, {
                u'tuesday': u'09:00-17:00'
            }, {
                u'wednesday': u'09:00-17:00'
            }, {
                u'thursday': u'09:00-17:00'
            }, {
                u'friday': u'09:00-17:00'
            }],
            "_realm":
            self.realmAll_id
        }
        response = backend.post('timeperiod', data=data)
        assert_true(response['_status'] == 'OK')
        timeperiod_id = response['_id']
        timeperiod_etag = response['_etag']

        headers = {'If-Match': timeperiod_etag}
        response = backend.delete('/'.join(['timeperiod', timeperiod_id]),
                                  headers=headers)
        assert_true(response['_status'] == 'OK')

        print("stop the alignak backend")
        self.pid.kill()

        with assert_raises(BackendException) as cm:
            headers = {'If-Match': timeperiod_etag}
            response = backend.delete('/'.join(['timeperiod', timeperiod_id]),
                                      headers=headers)
            assert_true(response['_status'] == 'OK')
        ex = cm.exception
        self.assertEqual(ex.code, 1000)
    def test_multiprocess(self):
        """
        Test multiprocess get right all elements

        :return: None
        """
        print('')
        print('test creation')

        print('Create client API for URL:', self.backend_address)
        backend = Backend(self.backend_address, 8)
        backend.login('admin', 'admin')

        items = backend.get('realm')
        realm_id = items['_items'][0]['_id']

        # add 2000 commands
        backend.delete("command", {})
        data = {'command_line': 'check_ping', '_realm': realm_id}
        for i in range(1, 2001):
            data['name'] = "cmd %d" % i
            backend.post('command', data)

        # get without multiprocess
        backend_yannsolo = Backend(self.backend_address)
        backend_yannsolo.login('admin', 'admin')
        start_time = time.time()
        resp = backend_yannsolo.get_all('command', {'max_results': 20})
        threads_1 = time.time() - start_time
        self.assertEqual(len(resp['_items']), 2002,
                         "Number of commands in non multiprocess mode")

        # get with multiprocess (8 processes)
        start_time = time.time()
        resp = backend.get_all('command', {'max_results': 20})
        threads_8 = time.time() - start_time
        self.assertEqual(len(resp['_items']), 2002,
                         "Number of commands in multiprocess mode")
        ids = []
        for dat in resp['_items']:
            ids.append(dat['_id'])
        self.assertEqual(len(ids), 2002, "Number of id")
        # remove duplicates
        ids_final = set(ids)
        self.assertEqual(len(ids_final), 2002, "Number of id unique")

        print(threads_1)
        print(threads_8)
    def test_1_delete_successful(self):
        """
        Test delete a timeperiod successfully

        :return: None
        """
        backend = Backend(self.backend_address)
        backend.login("admin", "admin")

        # Create a new timeperiod
        data = {
            "name": "Testing TP",
            "alias": "Test TP",
            "dateranges": [
                {u"monday": u"09:00-17:00"},
                {u"tuesday": u"09:00-17:00"},
                {u"wednesday": u"09:00-17:00"},
                {u"thursday": u"09:00-17:00"},
                {u"friday": u"09:00-17:00"},
            ],
            "_realm": self.realmAll_id,
        }
        response = backend.post("timeperiod", data=data)
        assert_true(response["_status"] == "OK")
        timeperiod_id = response["_id"]
        timeperiod_etag = response["_etag"]

        headers = {"If-Match": timeperiod_etag}
        response = backend.delete("/".join(["timeperiod", timeperiod_id]), headers=headers)
        assert_true(response["_status"] == "OK")
    def test_2_delete_exceptions(self):
        """
        Test delete a timeperiod with errors (so exceptions)

        :return: None
        """
        backend = Backend(self.backend_address)
        backend.login("admin", "admin")

        # Create a new timeperiod
        data = {
            "name": "Testing TP",
            "alias": "Test TP",
            "dateranges": [
                {u"monday": u"09:00-17:00"},
                {u"tuesday": u"09:00-17:00"},
                {u"wednesday": u"09:00-17:00"},
                {u"thursday": u"09:00-17:00"},
                {u"friday": u"09:00-17:00"},
            ],
            "_realm": self.realmAll_id,
        }
        response = backend.post("timeperiod", data=data)
        assert_true(response["_status"] == "OK")
        timeperiod_id = response["_id"]
        timeperiod_etag = response["_etag"]

        with assert_raises(BackendException) as cm:
            headers = {"If-Match": timeperiod_etag}
            response = backend.delete("/".join(["timeperiod", "5" + timeperiod_id]), headers)
        ex = cm.exception
        print("exception:", str(ex.code))
        assert_true(ex.code == 1003, str(ex))
    def test_multiprocess(self):
        """
        Test multiprocess get right all elements

        :return: None
        """
        print('')
        print('test creation')

        print('Create client API for URL:', self.backend_address)
        backend = Backend(self.backend_address, 8)
        backend.login('admin', 'admin')

        items = backend.get('realm')
        realm_id = items['_items'][0]['_id']

        # add 700 commands
        backend.delete("command", {})
        data = {'command_line': 'check_ping', '_realm': realm_id}
        for i in range(1, 2001):
            data['name'] = "cmd %d" % i
            backend.post('command', data)

        # get without multiprocess
        backend_yannsolo = Backend(self.backend_address)
        backend_yannsolo.login('admin', 'admin')
        start_time = time.time()
        resp = backend_yannsolo.get_all('command', {'max_results': 20})
        threads_1 = time.time() - start_time
        self.assertEqual(len(resp['_items']), 2000, "Number of commands in non multiprocess mode")

        # get with multiprocess (8 processes)
        start_time = time.time()
        resp = backend.get_all('command', {'max_results': 20})
        threads_8 = time.time() - start_time
        self.assertEqual(len(resp['_items']), 2000, "Number of commands in multiprocess mode")
        ids = []
        for dat in resp['_items']:
            ids.append(dat['_id'])
        self.assertEqual(len(ids), 2000, "Number of id")
        # remove doubles
        ids_final = set(ids)
        self.assertEqual(len(ids_final), 2000, "Number of id unique")

        print(threads_1)
        print(threads_8)
示例#8
0
    def test_2_delete_exceptions(self):
        """
        Test delete a timeperiod with errors (so exceptions)

        :return: None
        """
        backend = Backend(self.backend_address)
        backend.login('admin', 'admin')

        # Create a new timeperiod
        data = {
            "name":
            "Testing TP",
            "alias":
            "Test TP",
            "dateranges": [{
                u'monday': u'09:00-17:00'
            }, {
                u'tuesday': u'09:00-17:00'
            }, {
                u'wednesday': u'09:00-17:00'
            }, {
                u'thursday': u'09:00-17:00'
            }, {
                u'friday': u'09:00-17:00'
            }],
            "_realm":
            self.realmAll_id
        }
        response = backend.post('timeperiod', data=data)
        assert_true(response['_status'] == 'OK')
        timeperiod_id = response['_id']
        timeperiod_etag = response['_etag']

        with assert_raises(BackendException) as cm:
            headers = {'If-Match': timeperiod_etag}
            response = backend.delete(
                '/'.join(['timeperiod', '5' + timeperiod_id]), headers)
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 404, str(ex))
示例#9
0
    def test_1_delete_successful(self):
        """
        Test delete a timeperiod successfully

        :return: None
        """
        backend = Backend(self.backend_address)
        backend.login('admin', 'admin')

        # Create a new timeperiod
        data = {
            "name":
            "Testing TP",
            "alias":
            "Test TP",
            "dateranges": [{
                u'monday': u'09:00-17:00'
            }, {
                u'tuesday': u'09:00-17:00'
            }, {
                u'wednesday': u'09:00-17:00'
            }, {
                u'thursday': u'09:00-17:00'
            }, {
                u'friday': u'09:00-17:00'
            }],
            "_realm":
            self.realmAll_id
        }
        response = backend.post('timeperiod', data=data)
        assert_true(response['_status'] == 'OK')
        timeperiod_id = response['_id']
        timeperiod_etag = response['_etag']

        headers = {'If-Match': timeperiod_etag}
        response = backend.delete('/'.join(['timeperiod', timeperiod_id]),
                                  headers=headers)
        assert_true(response['_status'] == 'OK')
    def test_04_connection_username(self):
        global backend_address

        print ''
        print 'test accepted connection with username/password'

        # Create client API
        backend = Backend(backend_address)

        print 'Login ...'
        result = backend.login('admin', 'admin')
        print 'authenticated:', backend.authenticated
        print 'token:', backend.token
        assert_true(backend.authenticated)

        print 'Logout ...'
        result = backend.logout()
        print 'authenticated:', backend.authenticated
        print 'token:', backend.token
        assert_false(backend.authenticated)

        print 'Login ...'
        print 'authenticated:', backend.authenticated
        result = backend.login('admin', 'admin')
        print 'authenticated:', backend.authenticated
        print 'token:', backend.token
        assert_true(backend.authenticated)

        print 'Logout ...'
        result = backend.logout()
        print 'authenticated:', backend.authenticated
        print 'token:', backend.token
        assert_false(backend.authenticated)

        print 'Logout ...'
        result = backend.logout()
        print 'authenticated:', backend.authenticated
        print 'token:', backend.token
        assert_false(backend.authenticated)

        print 'get object ... must be refused!'
        with assert_raises(BackendException) as cm:
            items = backend.get('host')
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1001, str(ex))

        print 'get_all object ... must be refused!'
        with assert_raises(BackendException) as cm:
            items = backend.get_all('host')
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1001, str(ex))

        print 'get all domains ... must be refused!'
        with assert_raises(BackendException) as cm:
            items = backend.get_domains()
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1001, str(ex))

        print 'post data ... must be refused!'
        with assert_raises(BackendException) as cm:
            data = { 'fake': 'fake' }
            response = backend.post('contact', data=data)
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1001, str(ex))

        print 'patch data ... must be refused!'
        with assert_raises(BackendException) as cm:
            data = { 'fake': 'fake' }
            headers = { 'If-Match': '' }
            response = backend.patch('contact', data=data, headers=headers)
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1001, str(ex))

        print 'delete data ... must be refused!'
        with assert_raises(BackendException) as cm:
            data = { 'fake': 'fake' }
            headers = { 'If-Match': '' }
            response = backend.delete('contact', headers=headers)
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1001, str(ex))
示例#11
0
    class __BackendConnection(object):
        """
        Base class for all objects state management (displayed icon, ...)
        """

        def __init__(self, backend_endpoint='http://127.0.0.1:5002'):
            self.backend_endpoint = backend_endpoint
            self.backend = Backend(backend_endpoint)
            self.connected = False

        def login(self, username, password=None):
            """
            Log into the backend

            If password is provided, use the backend login function to authenticate the user

            If no password is provided, the username is assumed to be an authentication token and we
            use the backend connect function.
            """
            logger.info("login, connection requested, login: %s", username)

            self.connected = False

            if not username:  # pragma: no cover, should not happen
                # Refuse backend login without username
                logger.warning("No login without username!")
                return self.connected

            if not password:  # pragma: no cover, should not happen
                # Set backend token (no login request).
                logger.debug("Update backend token")
                self.backend.token = username
                self.connected = True
                return self.connected

            try:
                # Backend real login
                logger.info("Requesting backend authentication, username: %s", username)
                self.connected = self.backend.login(username, password)
            except BackendException:  # pragma: no cover, should not happen
                logger.warning("configured backend is not available!")
            except Exception as e:  # pragma: no cover, should not happen
                logger.warning("User login exception: %s", str(e))
                logger.error("traceback: %s", traceback.format_exc())

            logger.info("login result: %s", self.connected)
            return self.connected

        def logout(self):
            """
            Log out from the backend

            Do nothing except setting 'connected' attribute to False
            """
            logger.info("logout")

            self.connected = False

        def count(self, object_type, params=None):
            """
            If params is a string, it is considered to be an object id and params
            is modified to {'_id': params}.

            Else, params is used to 'get' objects from the backend.
            """
            logger.debug("count, %s, params: %s", object_type, params)

            if isinstance(params, basestring):
                params = {'where': {'_id': params}}

            # Update backend search parameters
            if params is None:
                params = {'page': 0, 'max_results': 1}
            if 'where' in params:
                params['where'] = json.dumps(params['where'])
            if 'max_results' not in params:
                params['max_results'] = 1
            logger.debug(
                "count, search in the backend for %s: parameters=%s", object_type, params
            )

            try:
                result = self.backend.get(object_type, params=params)
            except BackendException as e:  # pragma: no cover, simple protection
                logger.warning("count, backend exception for %s: %s", object_type, str(e))
                return 0

            logger.debug("count, search result for %s: result=%s", object_type, result)
            if not result['_status'] == 'OK':  # pragma: no cover, should not happen
                error = []
                if "content" in result:
                    error.append(result['content'])
                if "_issues" in result:
                    error.append(result['_issues'])
                logger.warning("count, %s: %s, not found: %s", object_type, params, error)
                return 0

            # If more than one element is found, we get an _items list
            if '_items' in result:
                logger.debug("count, found in the backend: %s: %s", object_type, result['_items'])
                return result['_meta']['total']

            return 0  # pragma: no cover, simple protection

        def get(self, object_type, params=None, all_elements=False):
            """
            If params is a string, it is considered to be an object id and params
            is modified to {'_id': params}.

            Else, params is used to 'get' objects from the backend.

            Returns an object or an array of matching objects. All extra attributes
            (_links, _status, _meta, ...) are not returned but an '_total' attribute is added
            in each element to get the total count of elements stored in the backend.

            Returns None if the search failed. Do not raise any exception to the caller.

            If all_elements is True, it calls the get_all function of the backend client to
            get all the elements without any pagination activated.
            """
            logger.debug("get, %s, params: %s", object_type, params)

            if isinstance(params, basestring):
                params = {'where': {'_id': params}}
                logger.debug("get, %s, params: %s", object_type, params)

            # Update backend search parameters
            if params is None:
                params = {'page': 0, 'max_results': BACKEND_PAGINATION_LIMIT}
            if 'where' in params:
                params['where'] = json.dumps(params['where'])
            if 'embedded' in params:
                params['embedded'] = json.dumps(params['embedded'])
            if 'where' not in params:
                params['where'] = {}
            if 'page' not in params:
                params['page'] = 0
            if 'max_results' not in params:
                params['max_results'] = BACKEND_PAGINATION_LIMIT
            logger.debug(
                "get, search in the backend for %s: parameters=%s", object_type, params
            )

            try:
                if all_elements:
                    result = self.backend.get_all(object_type, params=params)
                else:
                    result = self.backend.get(object_type, params=params)
            except BackendException as e:  # pragma: no cover, simple protection
                logger.warning("get, backend exception for %s: %s", object_type, str(e))
                return None

            logger.debug(
                "search, search result for %s: result=%s", object_type, result
            )
            if result['_status'] != 'OK':  # pragma: no cover, should not happen
                error = []
                if "content" in result:
                    error.append(result['content'])
                if "_issues" in result:
                    error.append(result['_issues'])
                logger.warning("get, %s: %s, not found: %s", object_type, params, error)
                raise ValueError(
                    '%s, search: %s was not found in the backend, error: %s' % (
                        object_type, params, error
                    )
                )

            # If more than one element is found, we get an _items list
            if '_items' in result:
                if '_meta' in result:
                    for item in result['_items']:
                        item.update({'_total': result['_meta']['total']})
                logger.debug("get, found in the backend: %s: %s", object_type, result['_items'])
                return result['_items']

            if '_status' in result:
                result.pop('_status')
            if '_meta' in result:
                # result.update({'_total': result['_meta']['total']})
                result['_total'] = result['_meta']['total']
            logger.debug("get, found one in the backend: %s: %s", object_type, result)
            return result

        def post(self, object_type, data=None, files=None):
            """ Add an element """
            logger.info("post, request to add a %s: data: %s", object_type, data)

            # Do not set header to use the client default behavior:
            # - set headers as {'Content-Type': 'application/json'}
            # - encode provided data to JSON
            headers = None
            if files:
                logger.info("post, request to add a %s with files: %s", object_type, files)
                # Set header to disable client default behavior
                headers = {'Content-type': 'multipart/form-data'}

            try:
                result = self.backend.post(object_type, data=data, files=files, headers=headers)
                logger.debug("post, response: %s", result)
                if result['_status'] != 'OK':
                    logger.warning("post, error: %s", result)
                    return None
            except BackendException as e:  # pragma: no cover, simple protection
                logger.error("post, backend exception: %s", str(e))
                logger.error("- response: %s", e.response)
                return None
            except Exception as e:  # pragma: no cover, simple protection
                logger.warning("post, error: %s", str(e))
                return None

            return result['_id']

        def delete(self, object_type, object_id):
            """
            Delete an element
            - object_type is the element type
            - object_id is the element identifier
            """
            logger.info("delete, request to delete the %s: %s", object_type, object_id)

            try:
                # Get most recent version of the element
                element = self.get('/'.join([object_type, object_id]))
                logger.debug("delete, element: %s", element)
            except ValueError:  # pragma: no cover, simple protection
                logger.warning("delete, object %s, _id=%s not found", object_type, object_id)
                return False

            try:
                # Request deletion
                headers = {'If-Match': element['_etag']}
                endpoint = '/'.join([object_type, object_id])
                logger.info("delete, endpoint: %s", endpoint)
                result = self.backend.delete(endpoint, headers)
                logger.debug("delete, response: %s", result)
                if result['_status'] != 'OK':  # pragma: no cover, should never happen
                    error = []
                    if "content" in result:
                        error.append(result["content"])
                    if "_issues" in result:
                        error.append(result["_issues"])
                        for issue in result["_issues"]:
                            error.append(result["_issues"][issue])
                    logger.warning("delete, error: %s", error)
                    return False
            except BackendException as e:  # pragma: no cover, should never happen
                logger.error("delete, backend exception: %s", str(e))
                return False
            except ValueError:  # pragma: no cover, should never happen
                logger.warning("delete, not found %s: %s", object_type, element)
                return False

            return True

        def update(self, object_type, object_id, data):
            """
            Update an element
            - object_type is the element type
            - object_id is the element identifier
            """
            logger.info("update, request to update the %s: %s", object_type, object_id)

            try:
                # Get most recent version of the element
                element = self.get('/'.join([object_type, object_id]))
                logger.debug("update, element: %s", element)
            except ValueError:  # pragma: no cover, simple protection
                logger.warning("update, object %s, _id=%s not found", object_type, object_id)
                return False

            try:
                # Request update
                headers = {'If-Match': element['_etag']}
                endpoint = '/'.join([object_type, object_id])
                logger.info("update, endpoint: %s, data: %s", endpoint, data)
                result = self.backend.patch(endpoint, data, headers)
                logger.debug("update, response: %s", result)
                if result['_status'] != 'OK':  # pragma: no cover, should never happen
                    error = []
                    if "content" in result:
                        error.append(result["content"])
                    if "_issues" in result:
                        error.append(result["_issues"])
                        for issue in result["_issues"]:
                            error.append(result["_issues"][issue])
                    logger.warning("update, error: %s", error)
                    return False
            except BackendException as e:  # pragma: no cover, should never happen
                logger.error("update, backend exception: %s", str(e))
                return False
            except ValueError:  # pragma: no cover, should never happen
                logger.warning("update, not found %s: %s", object_type, element)
                return False

            return True
示例#12
0
class DataManager(object):
    '''
    Base class for all data manager objects
    '''
    id = 1

    """
        Application data manager object
    """

    def __init__(self, backend_endpoint='http://127.0.0.1:5000', glpi=None):
        """
        Create an instance
        """
        # Set a unique id for each DM object
        self.__class__.id += 1

        # Associated backend object
        self.backend_endpoint = backend_endpoint
        self.backend = Backend(backend_endpoint)

        # Associated Glpi backend object
        self.glpi = None
        if glpi:
            self.glpi = Glpi(glpi.get('glpi_ws_backend', None))
            self.glpi_ws_login = glpi.get('glpi_ws_login', None)
            self.glpi_ws_password = glpi.get('glpi_ws_password', None)

        # Backend available objects (filled with objects received from backend)
        # self.backend_available_objets = []

        # Get known objects type from the imported modules
        # Search for classes including an _type attribute
        self.known_classes = []
        for k, v in globals().items():
            if isinstance(globals()[k], type) and '_type' in globals()[k].__dict__:
                self.known_classes.append(globals()[k])
                logger.debug(
                    "Known class %s for object type: %s",
                    globals()[k], globals()[k].getType()
                )

        self.connected = False
        self.logged_in_user = None
        self.connection_message = None
        self.loading = 0
        self.loaded = False

        self.refresh_required = False
        self.refresh_done = False

        self.updated = datetime.utcnow()

    def __repr__(self):
        return ("<DM, id: %s, objects count: %d, user: %s, updated: %s>") % (
            self.id,
            self.get_objects_count(),
            self.get_logged_user().get_username() if self.get_logged_user() else 'Not logged in',
            self.updated
        )

    ##
    # Connected user
    ##
    def user_login(self, username, password=None, load=True):
        """
        Set the data manager user

        If password is provided, use the backend login function to authenticate the user

        If no password is provided, the username is assumed to be an authentication token and we
        use the backend connect function.
        """
        logger.info("user_login, connection requested: %s, load: %s", username, load)

        self.connection_message = _('Backend connecting...')
        if not password:
            # Set backend token (no login request).
            # Do not include the token in the application logs !
            logger.debug("Update backend token")
            self.backend.token = username
            self.connected = True
            self.connection_message = _('Backend connected')
            # Load data if load required...
            if load:
                self.load(reset=True)
            return self.connected

        try:
            # Backend real login
            logger.info("Requesting backend authentication, username: %s", username)
            self.connected = self.backend.login(username, password)
            if self.connected:
                self.connection_message = _('Connection successful')

                # Fetch the logged-in user
                users = self.find_object(
                    'contact', {'where': {'token': self.backend.token}}
                )
                # Tag user as authenticated
                users[0].authenticated = True
                self.logged_in_user = users[0]

                # Get total objects count from the backend
                objects_count = self.get_objects_count(refresh=True, log=True)

                # Load data if load required...
                if load:
                    self.load(reset=True)
            else:
                self.connection_message = _('Backend connection refused...')
        except BackendException as e:  # pragma: no cover, should not happen
            logger.warning("configured applications backend is not available!")
            self.connection_message = e.message
            self.connected = False
        except Exception as e:  # pragma: no cover, should not happen
            logger.warning("User login exception: %s", str(e))
            logger.error("traceback: %s", traceback.format_exc())

        logger.info("user_login, connection message: %s", self.connection_message)
        return self.connected

    def user_logout(self):
        """
        Logout the data manager user. Do not log-out from the backend. Need to reset the
        datamanager to do it.
        """
        self.logged_in_user = None

    def get_logged_user(self):
        """
        Get the current logged in user
        """
        return self.logged_in_user

    ##
    # Find objects and load objects cache
    ##
    def find_object(self, object_type, params=None, all_elements=False):
        """
        Find an object ...

        Search in internal objects cache for an object matching the required parameters

        If params is a string, it is considered to be an object id and params
        is modified to {'_id': params}.

        Else, params is a dictionary of key/value to find a matching object in the objects cache
        If no objects are found in the cache, params is user to 'get' objects from the backend.

        Default behavior is to search in the backend if objects are not found in the cache. Call
        with backend=False to search only in local cache.

        If the backend search is successful, a new object is created if it exists a class in the
        imported modules (presumably alignak_webui.objects.item) which contains a 'bo_type' property
        and this property is valued as 'object_type'.

        Returns an array of matching objects
        """
        logger.debug("find_object, self: %s, updated:%s", self, self.updated)
        logger.debug("find_object, %s, params: %s", object_type, params)

        # -----------------------------------------------------------------------------------------
        # TODO: manage parameters like:
        # {'sort': '-opening_date', 'where': u'{"service_name": "userservice_1"}'}
        # -> ignore sort, ... but take car of 'where' to search in the cache!
        # -----------------------------------------------------------------------------------------
        # if params is None:
        # params = {'page': 0, 'max_results': BACKEND_PAGINATION_LIMIT}

        unique_element = False
        if isinstance(params, basestring):
            params = {'where': {'_id': params}}
            unique_element = True
            logger.debug("find_object, %s, params: %s", object_type, params)

        items = []

        # Update backend search parameters
        if params is None:
            params = {'page': 0, 'max_results': BACKEND_PAGINATION_LIMIT}
        if 'where' in params:
            params['where'] = json.dumps(params['where'])
        if 'embedded' in params:
            params['embedded'] = json.dumps(params['embedded'])
        if 'where' not in params:
            params['where'] = {}
        if 'page' not in params:
            params['page'] = 0
        if 'max_results' not in params:
            params['max_results'] = BACKEND_PAGINATION_LIMIT
        logger.debug(
            "find_object, search in the backend for %s: parameters=%s", object_type, params
        )

        try:
            if all_elements:
                result = self.backend.get_all(object_type, params=params)
            else:
                result = self.backend.get(object_type, params=params)
        except BackendException as e:  # pragma: no cover, simple protection
            logger.warning("find_object, backend exception: %s", str(e))
            return items

        logger.debug(
            "find_object, search result for %s: result=%s", object_type, result
        )
        if result['_status'] != 'OK':  # pragma: no cover, should not happen
            error = []
            if "content" in result:
                error.append(result["content"])
            if "_issues" in result:
                error.append(result["_issues"])
            logger.warning("find_object, %s: %s, not found: %s", object_type, params, error)
            raise ValueError(
                '%s, where %s was not found in the backend, error: %s' % (
                    object_type, params, error
                )
            )

        if not result['_items']:  # pragma: no cover, should occur rarely
            if items:
                logger.debug(
                    "find_object, no data in backend, found in the cache: %s: %s",
                    object_type, items
                )
                return items
            logger.debug(
                "find_object, %s: %s: not found: %s", object_type, params, result
            )
            raise ValueError(
                '%s, %s was not found in the cache nor in the backend' % (
                    object_type, params['where']
                )
            )

        logger.debug("find_object, found in the backend: %s: %s", object_type, result['_items'])
        for item in result['_items']:
            # Find "Backend object type" classes in file imported modules ...
            for k, v in globals().items():
                if isinstance(globals()[k], type) and '_type' in globals()[k].__dict__:
                    if globals()[k].getType() == object_type:
                        # Create a new object
                        bo_object = globals()[k](item)
                        items.append(bo_object)
                        self.updated = datetime.utcnow()
                        logger.debug("find_object, created: %s", bo_object)
                        break
            else:  # pragma: no cover, should not happen
                # No break, so not found a relative class!
                logger.error("find_object, %s class not found for: %s", object_type, item)

        return items

    def load(self, reset=False, refresh=False):
        """
            Load data in the data manager objects

            If reset is set, then all the existing objects are deleted and then created from
            scratch (first load ...). Else, existing objects are updated and new objects are
            created.

            Get all the users (related to current logged-in user)
            Get all the user services (related to current logged-in user)
            Get all the relations between users and services
            Get the most recent sessions for each user service

            :returns: the number of newly created objects
        """
        if not self.get_logged_user():
            logger.error("load, must be logged-in before loading")
            return False

        if self.loading > 0:  # pragma: no cover, protection if application shuts down ...
            logger.error("load, already loading: trial: %d", self.loading)
            if self.loading < 3:
                self.loading += 1
                return False
            logger.error("load, already loading: reset counter")
            self.loading = 0

        logger.debug("load, start loading: %s for %s", self, self.get_logged_user())
        logger.debug(
            "load, start as administrator: %s", self.get_logged_user().is_administrator()
        )
        start = time.time()

        if reset:
            logger.warning("Objects cache reset")
            self.reset(logout=False)

        self.loading += 1
        self.loaded = False

        # Get internal objects count
        objects_count = self.get_objects_count()
        logger.debug("Load, start, objects in cache: %d", objects_count)

        # -----------------------------------------------------------------------------------------
        # Get all users if current user is an administrator
        # -----------------------------------------------------------------------------------------
        self.get_users()

        # -----------------------------------------------------------------------------------------
        # Get all commands
        # -----------------------------------------------------------------------------------------
        commands = self.get_commands()

        # -----------------------------------------------------------------------------------------
        # Get all hosts
        # -----------------------------------------------------------------------------------------
        hosts = self.get_hosts()

        # Get internal objects count
        new_objects_count = self.get_objects_count()
        logger.debug("Load, end, objects in cache: %d", new_objects_count)

        logger.warning(
            "Data manager load (%s), new objects: %d,  duration: %s",
            refresh, new_objects_count - objects_count, (time.time() - start)
        )

        if new_objects_count > objects_count:
            self.require_refresh()

        self.loaded = True
        self.loading = 0
        return new_objects_count - objects_count

    def reset(self, logout=False):
        """
        Reset data in the data manager objects
        """
        logger.info("Data manager reset...")

        # Clean internal objects cache
        for known_class in self.known_classes:
            logger.info("Cleaning %s cache...", known_class.getType())
            known_class.cleanCache()

        if logout:
            self.backend.logout()
            self.user_logout()

        self.loaded = False
        self.loading = 0
        self.refresh_required = True

    def require_refresh(self):
        '''
        Require an immediate refresh
        '''
        self.refresh_required = True
        self.refresh_done = False

    def get_objects_count(self, object_type=None, refresh=False, log=False, search=None):
        '''
        Get the count of the objects stored in the data manager cache

        If an object_type is specified, only returns the count for this object type

        If refresh is True, get the total count from the backend. This is only useful if total
        count is required...

        If log is set, an information log is made
        '''
        log_function = logger.debug
        if log:
            log_function = logger.info

        if object_type:
            for known_class in self.known_classes:
                if object_type == known_class.getType():
                    objects_count = known_class.getCount()
                    log_function(
                        "get_objects_count, currently %d cached %ss",
                        objects_count, object_type
                    )
                    if refresh:
                        if hasattr(known_class, '_total_count'):
                            objects_count = known_class.getTotalCount()
                            log_function(
                                "get_objects_count, got _total_count attribute: %d",
                                objects_count
                            )
                        else:
                            objects_count = self.count_objects(object_type, search=search)

                        log_function(
                            "get_objects_count, currently %d total %ss for %s",
                            objects_count, object_type, search
                        )
                    return objects_count
            else:  # pragma: no cover, should not happen
                logger.warning("count_objects, unknown object type: %s", object_type)
                return 0

        objects_count = 0
        for known_class in self.known_classes:
            count = known_class.getCount()
            log_function(
                "get_objects_count, currently %d cached %ss",
                count, known_class.getType()
            )
            if refresh:
                count = self.count_objects(known_class.getType(), search=search)
                log_function(
                    "get_objects_count, currently %d total %ss for %s",
                    count, known_class.getType(), search
                )
            objects_count += count

        log_function("get_objects_count, currently %d elements", objects_count)
        return objects_count

    ##
    #
    # Elements add, delete, update, ...
    ##
    def count_objects(self, object_type, search=None):
        """
        Request objects from the backend to pick-up total records count.
        Make a simple request for 1 element and we will get back the total count of elements

        search is a dictionary of key / value to search for

        If log is set, an information log is made
        """
        total = 0

        params = {
            'page': 0, 'max_results': 1
        }
        if search is not None:
            params['where'] = json.dumps(search)

        # Request objects from the backend ...
        try:
            resp = self.backend.get(object_type, params)
            logger.debug("count_objects %s: %s", object_type, resp)

            # Total number of records
            if '_meta' in resp:
                total = int(resp['_meta']['total'])
        except BackendException as e:
            logger.warning(
                "count_objects exception for object type: %s: %s",
                object_type, e.message
            )

        return total

    def add_object(self, object_type, data=None, files=None):
        """ Add an element """
        logger.info("add_object, request to add a %s: data: %s", object_type, data)

        # Do not set header to use the client default behavior:
        # - set headers as {'Content-Type': 'application/json'}
        # - encode provided data to JSON
        headers = None
        if files:
            logger.info("add_object, request to add a %s with files: %s", object_type, files)
            # Set header to disable client default behavior
            headers = {'Content-type': 'multipart/form-data'}

        try:
            result = self.backend.post(object_type, data=data, files=files, headers=headers)
            logger.debug("add_object, response: %s", result)
            if result['_status'] != 'OK':
                logger.warning("add_object, error: %s", result)
                return None

            self.find_object(object_type, result['_id'])
        except BackendException as e:
            logger.error("add_object, backend exception: %s", str(e))
            return None
        except ValueError as e:  # pragma: no cover, should never happen
            logger.warning("add_object, error: %s", str(e))
            return None

        return result['_id']

    def delete_object(self, object_type, element):
        """
        Delete an element
        - object_type is the element type
        - element may be a string. In this case it is considered to be the element id
        """
        logger.info("delete_object, request to delete the %s: %s", object_type, element)

        if isinstance(element, basestring):
            object_id = element
        else:
            object_id = element.get_id()

        try:
            # Get most recent version of the element
            items = self.find_object(object_type, object_id)
            element = items[0]
        except ValueError:  # pragma: no cover, should never happen
            logger.warning("delete_object, object %s, _id=%s not found", object_type, object_id)
            return False

        try:
            # Request deletion
            headers = {'If-Match': element['_etag']}
            endpoint = '/'.join([object_type, object_id])
            logger.info("delete_object, endpoint: %s", endpoint)
            result = self.backend.delete(endpoint, headers)
            logger.debug("delete_object, response: %s", result)
            if result['_status'] != 'OK':  # pragma: no cover, should never happen
                error = []
                if "content" in result:
                    error.append(result["content"])
                if "_issues" in result:
                    error.append(result["_issues"])
                    for issue in result["_issues"]:
                        error.append(result["_issues"][issue])
                logger.warning("delete_object, error: %s", error)
                return False
        except BackendException as e:  # pragma: no cover, should never happen
            logger.error("delete_object, backend exception: %s", str(e))
            return False
        except ValueError as e:  # pragma: no cover, should never happen
            logger.warning("delete_object, not found %s: %s", object_type, element)
            return False

        try:
            # Try to get most recent version of the element
            items = self.find_object(object_type, object_id)
        except ValueError:
            logger.info("delete_object, object deleted: %s, _id=%s", object_type, object_id)
            # Object deletion
            element._delete()

        return True

    def update_object(self, object_type, element, data):
        """
        Update an element
        - object_type is the element type
        - element may be a string. In this case it is considered to be the element id
        """
        logger.info("update_object, request to update the %s: %s", object_type, element)

        if isinstance(element, basestring):
            object_id = element
        else:
            object_id = element.get_id()

        try:
            # Get most recent version of the element
            items = self.find_object(object_type, object_id)
            element = items[0]
        except ValueError:
            logger.warning("update_object, object %s, _id=%s not found", object_type, object_id)
            return False

        try:
            # Request update
            headers = {'If-Match': element['_etag']}
            endpoint = '/'.join([object_type, object_id])
            logger.info("update_object, endpoint: %s, data: %s", endpoint, data)
            result = self.backend.patch(endpoint, data, headers)
            logger.debug("update_object, response: %s", result)
            if result['_status'] != 'OK':  # pragma: no cover, should never happen
                error = []
                if "content" in result:
                    error.append(result["content"])
                if "_issues" in result:
                    error.append(result["_issues"])
                    for issue in result["_issues"]:
                        error.append(result["_issues"][issue])
                logger.warning("update_object, error: %s", error)
                return False

            items = self.find_object(object_type, object_id)
            logger.info("update_object, updated: %s", items[0])
        except BackendException as e:  # pragma: no cover, should never happen
            logger.error("update_object, backend exception: %s", str(e))
            return False
        except ValueError:  # pragma: no cover, should never happen
            logger.warning("update_object, not found %s: %s", object_type, element)
            return False

        return True

    ##
    # Hosts
    ##
    def get_hosts(self, search=None):
        """ Get a list of all hosts. """
        if search is None:
            search = {}
        if 'embedded' not in search:
            search.update({'embedded': {'userservice_session': 1, 'user': 1}})

        try:
            logger.info("get_hosts, search: %s", search)
            items = self.find_object('host', search)
            logger.info("get_hosts, got: %d elements, %s", len(items), items)
            return items
        except ValueError:
            logger.debug("get_hosts, none found")

        return []

    def get_host(self, search):
        """ Get a host by its id (default). """

        if isinstance(search, basestring):
            search = {'max_results': 1, 'where': {'_id': search}}
        elif 'max_results' not in search:
            search.update({'max_results': 1})

        items = self.get_hosts(search=search)
        return items[0] if items else None

    def get_hosts_synthesis(self, elts=None):
        """
        Hosts synthesis by status
        """
        if elts:
            hosts = [item for item in elts if item.getType() == 'host']
        else:
            # Use internal object list ...
            hosts = [item for _id, item in Host.getCache().items()]
        logger.debug("get_hosts_synthesis, %d hosts", len(hosts))

        synthesis = dict()
        synthesis['nb_elts'] = len(hosts)
        synthesis['nb_problem'] = 0
        if hosts:
            for state in 'up', 'unreachable', 'down', 'unknown':
                synthesis['nb_' + state] = sum(
                    1 for host in hosts if host.get_status().lower() == state
                )
                synthesis['pct_' + state] = round(
                    100.0 * synthesis['nb_' + state] / synthesis['nb_elts'], 2
                )
        else:
            for state in 'up', 'unreachable', 'down', 'unknown':
                synthesis['nb_' + state] = 0
                synthesis['pct_' + state] = 0

        logger.debug("get_hosts_synthesis: %s", synthesis)
        return synthesis

    def add_host(self, data, files):
        """
        Add a host.
        Update the concerned session internal objects.
        """

        return self.add_object('host', data, files)

    ##
    # Commands
    ##
    def get_commands(self, search=None):
        """ Get a list of all commands. """
        if search is None:
            search = {}
        if 'embedded' not in search:
            search.update({'embedded': {'userservice_session': 1, 'user': 1}})

        try:
            logger.info("get_commands, search: %s", search)
            items = self.find_object('command', search)
            return items
        except ValueError:
            logger.debug("get_commands, none found")

        return []

    def get_command(self, search):
        """ Get a command by its id. """

        if isinstance(search, basestring):
            search = {'max_results': 1, 'where': {'_id': search}}
        elif 'max_results' not in search:
            search.update({'max_results': 1})

        items = self.get_commands(search=search)
        return items[0] if items else None

    def get_commands_synthesis(self, elts=None):
        """
        Documents synthesis by status
        """
        if elts:
            commands = [item for item in elts if item.getType() == 'command']
        else:
            # Use internal object list ...
            commands = [item for _id, item in Command.getCache().items()]
        logger.debug("get_commands_synthesis, %d commands", len(commands))

        synthesis = dict()
        synthesis['nb_elts'] = len(commands)
        if commands:
            for state in 'attached', 'empty', 'problem', 'unknown':
                synthesis['nb_' + state] = sum(
                    1 for command in commands if command.get_status().lower() == state
                )
                synthesis['pct_' + state] = round(
                    100.0 * synthesis['nb_' + state] / synthesis['nb_elts'], 2
                )
        else:
            for state in 'attached', 'empty', 'problem', 'unknown':
                synthesis['nb_' + state] = 0
                synthesis['pct_' + state] = 0

        logger.debug("get_commands_synthesis: %s", synthesis)
        return synthesis

    def add_command(self, data):  # pragma: no cover, not yet implemented!
        """
        Add a command.
        Update the concerned session internal objects.
        """

        return self.add_object('command', data)

    ##
    # Users
    ##
    def get_users(self, search=None):
        """ Get a list of known users """
        if not self.get_logged_user().is_administrator():
            return [self.get_logged_user()]

        try:
            logger.info("get_users, search: %s", search)
            items = self.find_object('contact', search)
            return items
        except ValueError:
            logger.debug("get_users, none found")

        return []

    def get_user(self, search):
        """
        Get a user by its id or a search pattern
        """
        if isinstance(search, basestring):
            search = {'max_results': 1, 'where': {'_id': search}}
        elif 'max_results' not in search:
            search.update({'max_results': 1})

        items = self.get_users(search=search)
        return items[0] if items else None

    def add_user(self, data):
        """ Add a user. """

        return self.add_object('user', data)

    def delete_user(self, user):
        """ Delete a user.

        Cannot delete the currently logged in user ...

        If user is a string it is assumed to be the User object id to be searched in the objects
        cache.

        :param user: User object instance
        :type user: User (or string)

        Returns True/False depending if user closed
        """
        logger.info("delete_user, request to delete the user: %s", user)

        if isinstance(user, basestring):
            user = self.get_user(user)
            if not user:
                return False

        user_id = user.get_id()
        if user_id == self.get_logged_user().get_id():
            logger.warning("delete_user, request to delete the current logged-in user: %s", user_id)
            return False

        return self.delete_object('user', user)
class BackendConnection(object):    # pylint: disable=too-few-public-methods

    """Base class for Alignak backend connection"""

    def __init__(self, backend_endpoint='http://127.0.0.1:5000'):
        self.backend_endpoint = backend_endpoint
        self.alignak_backend = Backend(backend_endpoint)
        self.connected = False

    def login(self, username, password=None):
        """Log into the backend

        If password is provided, use the backend login function to authenticate the user

        If no password is provided, the username is assumed to be an authentication token and we
        use the backend connect function."""

        logger.info("login, connection requested, login: %s", username)

        self.connected = False

        if not username:
            # Refuse backend login without username
            logger.warning("No login without username!")
            return self.connected

        if not password:
            # Set backend token (no login request).
            logger.debug("Update backend token")
            self.alignak_backend.token = username
            self.connected = True
            return self.connected

        try:
            # Backend real login
            logger.info("Requesting backend (%s) authentication, username: %s",
                        self.backend_endpoint, username)
            self.connected = self.alignak_backend.login(username, password)
        except BackendException:  # pragma: no cover, should not happen
            logger.warning("configured backend is not available!")
        except Exception as e:  # pragma: no cover, should not happen
            logger.warning("User login exception: %s", str(e))
            logger.error("traceback: %s", traceback.format_exc())

        logger.info("login result: %s", self.connected)
        return self.connected

    def logout(self):
        """Log out from the backend

        Do nothing except setting 'connected' attribute to False"""

        logger.info("logout")

        self.connected = False

    def count(self, object_type, params=None):
        """Count backend elements

        If params is a string, it is considered to be an object id and params
        is modified to {'_id': params}.

        Else, params is used to 'get' objects from the backend."""

        logger.debug("count, %s, params: %s", object_type, params)

        if isinstance(params, string_types):
            params = {'where': {'_id': params}}

        # Update backend search parameters
        if params is None:
            params = {'page': 0, 'max_results': BACKEND_PAGINATION_LIMIT}
        if 'where' in params:
            params['where'] = json.dumps(params['where'])
        if 'embedded' in params:
            del params['embedded']
        if 'where' not in params:
            params['where'] = {}
        if 'page' not in params:
            params['page'] = 0
        if 'max_results' not in params:
            params['max_results'] = BACKEND_PAGINATION_LIMIT
        # if params is None:
            # params = {'page': 0, 'max_results': 1}
        # if 'where' in params:
            # params['where'] = json.dumps(params['where'])
        # if 'max_results' not in params:
            # params['max_results'] = 1
        logger.debug("count, search in the backend for %s: parameters=%s", object_type, params)

        try:
            result = self.alignak_backend.get(object_type, params=params)
        except BackendException as e:  # pragma: no cover, simple protection
            logger.warning("count, backend exception for %s: %s", object_type, str(e))
            return 0

        if not result['_status'] == 'OK':  # pragma: no cover, should not happen
            error = []
            if "content" in result:
                error.append(result['content'])
            if "_issues" in result:
                error.append(result['_issues'])
            logger.warning("count, %s: %s, not found: %s", object_type, params, error)
            return 0
        logger.debug("count, search result for %s: status=%s", object_type, result['_status'])

        # If more than one element is found, we get an _items list
        if '_items' in result:
            logger.debug("count, found in the backend: %d %s",
                         len(result['_items']), object_type)
            logger.debug("count, found in the backend: %d total %s",
                         result['_meta']['total'], object_type)
            return result['_meta']['total']

        return 0  # pragma: no cover, simple protection

    def get(self, object_type, params=None, all_elements=False):
        """Get backend elements

        If params is a string, it is considered to be an object id and params
        is modified to {'_id': params}.

        Else, params is used to 'get' objects from the backend.

        Returns an object or an array of matching objects. All extra attributes
        (_links, _status, _meta, ...) are not returned but an '_total' attribute is added
        in each element to get the total count of elements stored in the backend.

        Returns None if the search failed. Do not raise any exception to the caller.

        If all_elements is True, it calls the get_all function of the backend client to
        get all the elements without any pagination activated."""

        logger.debug("get, %s, params: %s", object_type, params)

        if '/' not in object_type:
            # Do not Get a specific element, manage search parameters
            if isinstance(params, string_types):
                params = {'where': {'_id': params}}
                logger.debug("get, %s, params: %s", object_type, params)

            # Update backend search parameters
            if params is None:
                params = {'page': 0, 'max_results': BACKEND_PAGINATION_LIMIT}
            if 'where' in params:
                params['where'] = json.dumps(params['where'])
            if 'embedded' in params:
                params['embedded'] = json.dumps(params['embedded'])
            if 'where' not in params:
                params['where'] = {}
            if 'page' not in params:
                params['page'] = 0
            if 'max_results' not in params:
                params['max_results'] = BACKEND_PAGINATION_LIMIT
        logger.debug("get, search in the backend for %s: parameters=%s, all: %s",
                     object_type, params, all_elements)

        try:
            if all_elements:
                result = self.alignak_backend.get_all(object_type, params=params)
            else:
                result = self.alignak_backend.get(object_type, params=params)
        except BackendException as e:  # pragma: no cover, simple protection
            logger.warning("get, backend exception for %s: %s", object_type, str(e))
            raise BackendException(code=e.code, message=e.message)

        logger.debug("search, search result for %s: result=%s", object_type, result)
        if result['_status'] != 'OK':  # pragma: no cover, should not happen
            error = []
            if "content" in result:
                error.append(result['content'])
            if "_issues" in result:
                error.append(result['_issues'])
            logger.warning("get, %s: %s, not found: %s", object_type, params, error)
            raise ValueError('%s, search: %s was not found in the backend, error: %s'
                             % (object_type, params, error))

        # If more than one element is found, we get an _items list
        if '_items' in result:
            total = len(result['_items'])
            if '_meta' in result:
                total = result['_meta']['total']
            logger.debug("get %s %s, %d total elements found in the backend",
                         object_type, ' (All required)' if all_elements else ' (filtered)', total)
            for item in result['_items']:
                item.update({'_total': total})
            return result['_items']

        if '_status' in result:
            result.pop('_status')
        if '_meta' in result:
            result['_total'] = result['_meta']['total']
            result.pop('_meta')
        if all_elements:
            logger.info("get %s %s, %d total elements found in the backend",
                        object_type, ' (All required)' if all_elements else ' (filtered)',
                        len(result))
            for item in result:
                item.update({'_total': len(result)})
        logger.debug("get, found one in the backend: %s: %s", object_type, result)
        return result

    def post(self, object_type, data=None, files=None):
        """Add an element"""

        logger.info("post, request to add a %s: data: %s", object_type, data)

        # Do not set header to use the client default behavior:
        # - set headers as {'Content-Type': 'application/json'}
        # - encode provided data to JSON
        headers = None
        if files:
            logger.info("post, request to add a %s with files: %s", object_type, files)
            # Set header to disable client default behavior
            headers = {'Content-type': 'multipart/form-data'}

        try:
            result = self.alignak_backend.post(object_type, data=data, files=files, headers=headers)
            logger.debug("post, response: %s", result)
            if result['_status'] != 'OK':
                logger.warning("post, error: %s", result)
                return None
        except BackendException as exp:  # pragma: no cover, simple protection
            logger.exception("post, backend exception: %s", exp)
            error = []
            if getattr(exp, "response", None) and "_issues" in exp.response:
                error = exp.response["_issues"]
            else:
                error.append(str(exp))
            logger.warning("post, error(s): %s", error)
            return error
        except Exception as e:  # pragma: no cover, simple protection
            logger.warning("post, error: %s", str(e))
            return None

        return result['_id']

    def delete(self, object_type, object_id):
        """Delete an element

        - object_type is the element type
        - object_id is the element identifier"""

        logger.info("delete, request to delete the %s: %s", object_type, object_id)

        try:
            # Get most recent version of the element
            element = self.get('/'.join([object_type, object_id]))
            logger.debug("delete, element: %s", element)
        except ValueError:  # pragma: no cover, simple protection
            logger.warning("delete, object %s, _id=%s not found", object_type, object_id)
            return False

        try:
            # Request deletion
            headers = {'If-Match': element['_etag']}
            endpoint = '/'.join([object_type, object_id])
            logger.debug("delete, endpoint: %s", endpoint)
            result = self.alignak_backend.delete(endpoint, headers)
            logger.debug("delete, response: %s", result)
            if result['_status'] != 'OK':  # pragma: no cover, should never happen
                error = []
                if "content" in result:
                    error.append(result["content"])
                if "_issues" in result:
                    error.append(result["_issues"])
                    for issue in result["_issues"]:
                        error.append(result["_issues"][issue])
                logger.warning("delete, error: %s", error)
                return False
        except BackendException as e:  # pragma: no cover, should never happen
            logger.error("delete, backend exception: %s", str(e))
            return False
        except ValueError:  # pragma: no cover, should never happen
            logger.warning("delete, not found %s: %s", object_type, element)
            return False

        return True

    def update(self, element, data):
        """Update an element"""

        logger.info("update, request to update: %s", element)

        try:
            # Request update
            headers = {'If-Match': element['_etag']}
            endpoint = '/'.join([element.get_type(), element.id])
            logger.debug("update, endpoint: %s, data: %s", endpoint, data)
            result = self.alignak_backend.patch(endpoint, data, headers, inception=True)
            logger.debug("update, response: %s", result)
            if result['_status'] != 'OK':  # pragma: no cover, should never happen
                error = []
                if "content" in result:
                    error.append(result["content"])
                if "_issues" in result:
                    error.append(result["_issues"])
                    for issue in result["_issues"]:
                        error.append(result["_issues"][issue])
                logger.warning("update, error(s): %s", error)
                return error
        except BackendException as e:  # pragma: no cover, should never happen
            error = []
            if e.response and "_issues" in e.response:
                error = e.response["_issues"]
            else:
                error.append(str(e))
            logger.error("update, backend exception: %s", str(e))
            return error
        except ValueError:  # pragma: no cover, should never happen
            logger.warning("update, not found %s: %s", element.get_type(), element)
            return False

        return True
    def test_04_login(self):
        """
        Test with right username / password

        :return: None
        """
        print('')
        print('test accepted connection with username/password')

        # Create client API
        backend = Backend(self.backend_address)

        print('Login ...')
        assert backend.login('admin', 'admin')
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_true(backend.authenticated)

        print('Logout ...')
        backend.logout()
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_false(backend.authenticated)

        print('Login ...')
        print('authenticated:', backend.authenticated)
        assert backend.login('admin', 'admin')
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_true(backend.authenticated)

        print('Logout ...')
        backend.logout()
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_false(backend.authenticated)

        print('Logout ...')
        backend.logout()
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_false(backend.authenticated)

        print('get object ... must be refused!')
        with assert_raises(BackendException) as cm:
            backend.get('host')
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 1001, str(ex))

        print('get_all object ... must be refused!')
        with assert_raises(BackendException) as cm:
            backend.get_all('host')
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 1001, str(ex))

        print('get all domains ... must be refused!')
        with assert_raises(BackendException) as cm:
            backend.get_domains()
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 1001, str(ex))

        print('post data ... must be refused!')
        with assert_raises(BackendException) as cm:
            data = {'fake': 'fake'}
            backend.post('user', data=data)
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 1001, str(ex))

        print('patch data ... must be refused!')
        with assert_raises(BackendException) as cm:
            data = {'fake': 'fake'}
            headers = {'If-Match': ''}
            backend.patch('user', data=data, headers=headers)
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 1001, str(ex))

        print('delete data ... must be refused!')
        with assert_raises(BackendException) as cm:
            headers = {'If-Match': ''}
            backend.delete('user', headers=headers)
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 1001, str(ex))
    def test_21_post_patch_delete(self):
        global backend_address

        print ''
        print 'post/delete/patch some elements'

        # Create client API
        backend = Backend(backend_address)

        print 'Login ...'
        print 'authenticated:', backend.authenticated
        result = backend.login('admin', 'admin')
        print 'authenticated:', backend.authenticated
        print 'token:', backend.token
        assert_true(backend.authenticated)

        # Get all contacts
        print 'get all contacts at once'
        parameters = { 'where': '{"register":true}' }
        items = backend.get_all('contact', params=parameters)
        print "Got %d elements:" % len(items)
        assert_true('_items' not in items)
        for item in items:
            assert_true('contact_name' in item)
            assert_true('_id' in item)
            assert_true('_etag' in item)
            print "Contact: ", item['contact_name'], item['_id']
            # Test contact still exists ... delete him!
            if item['contact_name'] == 'test':
                headers = { 'If-Match': item['_etag'] }
                response = backend.delete('/'.join(['contact', item['_id']]), headers)
                print "Response:", response

        # Get all timeperiods
        print 'get all timeperiods at once'
        parameters = { 'where': '{"register":true}' }
        items = backend.get_all('timeperiod', params=parameters)
        print "Got %d elements:" % len(items)
        assert_true('_items' not in items)
        tp_id = ''
        for item in items:
            assert_true('timeperiod_name' in item)
            assert_true('_id' in item)
            tp_id = item['_id']
            print item
            print "TP: %s (%s), id=%s" % (item['timeperiod_name'], item['name'], item['_id'])

        if not tp_id:
            # Create a new timeperiod
            print 'create a timeperiod'
            data = {
                "timeperiod_name": "test",
                "name": "Testing TP",
                "alias": "Test TP",
                "dateranges": [
                    {u'monday': u'09:00-17:00'},
                    {u'tuesday': u'09:00-17:00'},
                    {u'wednesday': u'09:00-17:00'},
                    {u'thursday': u'09:00-17:00'},
                    {u'friday': u'09:00-17:00'}
                ],
                "register": True
            }
            response = backend.post('timeperiod', data=data)
            print "Response:", response
            assert_true('_created' in response)
            assert_true('_updated' in response)
            assert_true(response['_created'] == response['_updated'])

            # Get all timeperiods
            print 'get all timeperiods at once'
            parameters = { 'where': '{"register":true}' }
            items = backend.get_all('timeperiod', params=parameters)
            print "Got %d elements:" % len(items)
            assert_true('_items' not in items)
            tp_id = ''
            for item in items:
                assert_true('timeperiod_name' in item)
                assert_true('_id' in item)
                tp_id = item['_id']
                print "TP: %s (%s), id=%s" % (item['timeperiod_name'], item['name'], item['_id'])

        assert_true(tp_id != '')

        # Create a new contact, bad parameters
        print 'create a contact, missing fields'
        # Mandatory field contact_name is missing ...
        data = {
            "name": "Testing contact",
            "alias": "Fred",
            "back_role_super_admin": False,
            "back_role_admin": [],
            "min_business_impact": 0,
        }
        with assert_raises(BackendException) as cm:
            response = backend.post('contact', data=data)
        ex = cm.exception
        print 'exception:', str(ex.code), ex.message, ex.response
        if "_issues" in ex.response:
            for issue in ex.response["_issues"]:
                print "Issue: %s - %s" %(issue, ex.response["_issues"][issue])
        assert_true(ex.code == 422)
        assert_true(ex.response["_issues"])

        # Create a new contact
        print 'create a contact'
        data = {
            "contact_name": "test",
            "name": "Testing contact",
            "alias": "Fred",
            "back_role_super_admin": False,
            "back_role_admin": [],
            "min_business_impact": 0,
            "email": "*****@*****.**",

            "is_admin": False,
            "expert": False,
            "can_submit_commands": False,

            "host_notifications_enabled": True,
            "host_notification_period": tp_id,
            "host_notification_commands": [
            ],
            "host_notification_options": [
                "d",
                "u",
                "r"
            ],

            "service_notifications_enabled": True,
            "service_notification_period": tp_id,
            "service_notification_commands": [ ],
            "service_notification_options": [
                "w",
                "u",
                "c",
                "r"
            ],
            "retain_status_information": False,
            "note": "Monitoring template : default",
            "retain_nonstatus_information": False,
            "definition_order": 100,
            "address1": "",
            "address2": "",
            "address3": "",
            "address4": "",
            "address5": "",
            "address6": "",
            "pager": "",
            "notificationways": [],
            "register": True
        }
        response = backend.post('contact', data=data)
        print "Response:", response
        assert_true('_created' in response)
        assert_true('_updated' in response)
        assert_true(response['_created'] == response['_updated'])

        # Get all contacts
        print 'get all contacts at once'
        # Filter the templates ...
        parameters = { 'where': '{"register":true}' }
        items = backend.get_all('contact', params=parameters)
        print "Got %d elements:" % len(items)
        assert_true('_items' not in items)
        assert_true(len(items) > 0)
        # Search test contact
        contact_id = ''
        contact_etag = ''
        for item in items:
            assert_true('contact_name' in item)
            print "Contact: ", item['contact_name']
            if item['contact_name'] == 'test':
                contact_id = item['_id']
                contact_etag = item['_etag']
        assert_true(contact_id != '')
        assert_true(contact_etag != '')

        print 'changing contact alias ... no _etag'
        print 'id:', contact_id
        print 'etag:', contact_etag
        with assert_raises(BackendException) as cm:
            data = {'alias': 'modified with no header'}
            # headers['If-Match'] = contact_etag
            response = backend.patch('/'.join(['contact', contact_id]), data=data)
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1005, str(ex))

        print 'changing contact alias ...'
        print 'id:', contact_id
        print 'etag:', contact_etag
        data = {'alias': 'modified test'}
        headers = {'If-Match': contact_etag}
        response = backend.patch('/'.join(['contact', contact_id]), data=data, headers=headers)
        print 'response:', response
        assert_true(response['_status'] == 'OK')

        response = backend.get('/'.join(['contact', contact_id]))
        print 'response:', response
        assert_true(response['alias'] == 'modified test')

        print 'changing contact alias ... bad _etag (inception = True)'
        print 'id:', contact_id
        print 'etag:', contact_etag
        data = {'alias': 'modified test again'}
        headers = {'If-Match': contact_etag}
        response = backend.patch('/'.join(['contact', contact_id]), data=data, headers=headers, inception=True)
        print 'response:', response
        assert_true(response['_status'] == 'OK')

        response = backend.get('/'.join(['contact', contact_id]))
        print 'response:', response
        assert_true(response['alias'] == 'modified test again')

        print 'changing contact unknown field ... must be refused'
        print 'id:', contact_id
        print 'etag:', contact_etag
        with assert_raises(BackendException) as cm:
            data = {'bad_field': 'bad field name ... unknown in data model'}
            headers = {'If-Match': contact_etag}
            response = backend.patch('/'.join(['contact', contact_id]), data=data, headers=headers, inception=True)
        ex = cm.exception
        print 'exception:', str(ex.code), ex.message, ex.response
        if "_issues" in ex.response:
            for issue in ex.response["_issues"]:
                print "Issue: %s - %s" %(issue, ex.response["_issues"][issue])
        assert_true(ex.code == 422)
        assert_true(ex.response["_issues"])

        print 'changing contact alias ... bad _etag (inception = False)'
        print 'id:', contact_id
        print 'etag:', contact_etag
        with assert_raises(BackendException) as cm:
            data = {'alias': 'modified test again and again'}
            headers = {'If-Match': contact_etag}
            response = backend.patch('/'.join(['contact', contact_id]), data=data, headers=headers)
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 412, str(ex))

        response = backend.get('/'.join(['contact', contact_id]))
        print 'response:', response
        # Not changed !
        assert_true(response['alias'] == 'modified test again')

        response = backend.get('/'.join(['contact', contact_id]))
        print 'response:', response
        # Not changed !
        assert_true(response['alias'] == 'modified test again')

        print 'deleting contact ... bad href'
        with assert_raises(BackendException) as cm:
            headers = { 'If-Match': item['_etag'] }
            response = backend.delete('/'.join(['contact', '5'+item['_id']]), headers)
            print "Response:", response
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1003, str(ex))
    def test_22_post_patch_delete(self):
        global backend_address

        print ''
        print 'post/delete/patch some hostgroups'

        # Create client API
        backend = Backend(backend_address)

        print 'Login ...'
        print 'authenticated:', backend.authenticated
        result = backend.login('admin', 'admin')
        print 'authenticated:', backend.authenticated
        print 'token:', backend.token
        assert_true(backend.authenticated)

        # Get all hostgroups
        print 'get all hostgroups at once'
        items = backend.get_all('hostgroup')
        print "Got %d elements:" % len(items)
        assert_true('_items' not in items)
        for item in items:
            assert_true('hostgroup_name' in item)
            assert_true('_id' in item)
            assert_true('_etag' in item)
            print "Group: ", item['hostgroup_name'], item['_id']
            # Test contact still exists ... delete him!
            if item['hostgroup_name'] == 'test':
                headers = { 'If-Match': item['_etag'] }
                response = backend.delete('/'.join(['hostgroup', item['_id']]), headers)
                print "Response:", response

        # Create a new hostgroup, bad parameters
        print 'create a hostgroup, missing fields'
        # Mandatory field hostgroup_name is missing ...
        data = {
            "name": "Testing hostgroup",
            "alias": "Fred",
            "back_role_super_admin": False,
            "back_role_admin": [],
            "min_business_impact": 0,
        }
        with assert_raises(BackendException) as cm:
            response = backend.post('hostgroup', data=data)
        ex = cm.exception
        print 'exception:', str(ex.code), ex.message, ex.response
        if "_issues" in ex.response:
            for issue in ex.response["_issues"]:
                print "Issue: %s - %s" %(issue, ex.response["_issues"][issue])
        assert_true(ex.code == 422)
        assert_true(ex.response["_issues"])

        # Create a new hostgroup
        print 'create a hostgroup'
        data = {
            "hostgroup_name": "test",
            "name": "Testing hostgroup",
            "alias": "Fred",
            "note": "Hostgroup note ...",
            "realm": "all"
        }
        response = backend.post('hostgroup', data=data)
        print "Response:", response
        assert_true('_created' in response)
        assert_true('_updated' in response)
        assert_true(response['_created'] == response['_updated'])

        # Get all hostgroups
        print 'get all hostgroups at once'
        # Filter the templates ...
        items = backend.get_all('hostgroup')
        print "Got %d elements:" % len(items)
        assert_true('_items' not in items)
        assert_true(len(items) > 0)
        # Search test hostgroup
        hostgroup_id = ''
        hostgroup_etag = ''
        for item in items:
            assert_true('hostgroup_name' in item)
            print "hostgroup: ", item['hostgroup_name']
            if item['hostgroup_name'] == 'test':
                hostgroup_id = item['_id']
                hostgroup_etag = item['_etag']
        assert_true(hostgroup_id != '')
        assert_true(hostgroup_etag != '')

        print 'changing hostgroup alias ... no _etag'
        print 'id:', hostgroup_id
        print 'etag:', hostgroup_etag
        with assert_raises(BackendException) as cm:
            data = {'alias': 'modified with no header'}
            # headers['If-Match'] = hostgroup_etag
            response = backend.patch('/'.join(['hostgroup', hostgroup_id]), data=data)
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1005, str(ex))

        print 'changing hostgroup alias ...'
        print 'id:', hostgroup_id
        print 'etag:', hostgroup_etag
        data = {'alias': 'modified test'}
        headers = {'If-Match': hostgroup_etag}
        response = backend.patch('/'.join(['hostgroup', hostgroup_id]), data=data, headers=headers)
        print 'response:', response
        assert_true(response['_status'] == 'OK')

        response = backend.get('/'.join(['hostgroup', hostgroup_id]))
        print 'response:', response
        assert_true(response['alias'] == 'modified test')

        print 'changing hostgroup alias ... bad _etag (inception = True)'
        print 'id:', hostgroup_id
        print 'etag:', hostgroup_etag
        data = {'alias': 'modified test again'}
        headers = {'If-Match': hostgroup_etag}
        response = backend.patch('/'.join(['hostgroup', hostgroup_id]), data=data, headers=headers, inception=True)
        print 'response:', response
        assert_true(response['_status'] == 'OK')

        response = backend.get('/'.join(['hostgroup', hostgroup_id]))
        print 'response:', response
        assert_true(response['alias'] == 'modified test again')

        print 'changing hostgroup alias ... bad _etag (inception = False)'
        print 'id:', hostgroup_id
        print 'etag:', hostgroup_etag
        with assert_raises(BackendException) as cm:
            data = {'alias': 'modified test again and again'}
            headers = {'If-Match': hostgroup_etag}
            response = backend.patch('/'.join(['hostgroup', hostgroup_id]), data=data, headers=headers)
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 412, str(ex))

        response = backend.get('/'.join(['hostgroup', hostgroup_id]))
        print 'response:', response
        # Not changed !
        assert_true(response['alias'] == 'modified test again')

        response = backend.get('/'.join(['hostgroup', hostgroup_id]))
        print 'response:', response
        # Not changed !
        assert_true(response['alias'] == 'modified test again')

        print 'deleting hostgroup ... bad href'
        with assert_raises(BackendException) as cm:
            headers = { 'If-Match': item['_etag'] }
            response = backend.delete('/'.join(['hostgroup', '5'+item['_id']]), headers)
            print "Response:", response
        ex = cm.exception
        print 'exception:', str(ex.code)
        assert_true(ex.code == 1003, str(ex))
def main():
    """
    Main function
    """
    # Store list of errors found
    errors_found = []

    # Get command line parameters
    args = docopt(__doc__, version='0.1.0')

    # Define here the path of the cfg files
    cfg = args['<cfg_file>']
    print ("Configuration to load: %s" % cfg)

    # Define here the url of the backend
    backend_url = args['--backend']
    print ("Backend URL: %s", backend_url)

    # Delete all objects in backend ?
    destroy_backend_data = args['--delete']
    print ("Delete existing backend data: %s" % destroy_backend_data)

    username = args['--username']
    password = args['--password']
    print ("Backend login with credentials: %s/%s" % (username, password))

    def check_mapping(items, mapping):
        """
        Check if elements are found ...
        """
        response = {
            'all_found': True,
            'data': items
        }
        new_list = []
        for item in items:
            if item in mapping:
                new_list.append(mapping[item])
            else:
                response['all_found'] = False
        if response['all_found']:
            response['data'] = new_list
        return response

    # Get flat files configuration
    alconfig = Config()
    buf = alconfig.read_config(cfg)
    print ("Configuration: %s" % (buf))
    conf = alconfig.read_config_buf(buf)
    print ("Configuration: %s" % (conf))

    print("~~~~~~~~~~~~~~~~~~~~~~ First authentication to delete previous data ~~~~~~~~~~~~~~")
    # Backend authentication with token generation
    headers = {'Content-Type': 'application/json'}
    payload = {'username': username, 'password': password, 'action': 'generate'}
    backend = Backend(backend_url)
    backend.login(username, password)

    if backend.token is None:
        print("Can't authenticated")
        exit()
    print("~~~~~~~~~~~~~~~~~~~~~~ Authenticated ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")

    # Destroy data in backend if defined
    if destroy_backend_data:
        headers = {'Content-Type': 'application/json'}
        backend.delete('command', headers)
        backend.delete('timeperiod', headers)
        backend.delete('hostgroup', headers)
        backend.delete('hostdependency', headers)
        backend.delete('servicedependency', headers)
        backend.delete('serviceextinfo', headers)
        backend.delete('trigger', headers)
        # backend.delete('contact', headers)
        backend.delete('contactgroup', headers)
        backend.delete('contactrestrictrole', headers)
        backend.delete('escalation', headers)
        backend.delete('host', headers)
        backend.delete('hostextinfo', headers)
        backend.delete('hostescalation', headers)
        backend.delete('servicegroup', headers)
        backend.delete('service', headers)
        backend.delete('serviceescalation', headers)
        backend.delete('livestate', headers)
        backend.delete('livesynthesis', headers)
        print("~~~~~~~~~~~~~~~~~~~~~~ Data destroyed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # Order of objects + fields to update post add
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #
    # COMMAND
    #    command.use
    # TIMEPERIOD
    #    timeperiod.use
    # HOSTGROUP
    #    hostgroup.hostgroup_members
    # HOSTDEPENDENCY
    #    hostdependency.use
    # SERVICEDEPENDENCY
    #    servicedependency.use
    # SERVICEEXTINFO
    #    serviceextinfo.use
    # TRIGGER
    #    trigger.use
    # CONTACT
    #    contact.use
    # CONTACTGROUP
    #    contact.contactgroups / contactgroup.contactgroup_members
    # CONTACTRESTRICTROLE
    #
    # ESCALATION
    #    escalation.use
    # HOST
    #    hostgroup.members / host.use / host.parents
    # HOSTEXTINFO
    #    hostextinfo.use
    # HOSTESCALATION
    #    hostescalation.use
    # SERVICEGROUP
    #    servicegroup.servicegroup_members
    # SERVICE
    #    service.use
    # SERVICEESCALATION
    #    serviceescalation.use
    #

    def update_types(source, schema):
        """
        Update elements types
        """
        for prop in source:
            source[prop] = ''.join(source[prop])
            if prop in schema:
                # get type
                if schema[prop]['type'] == 'boolean':
                    if source[prop] == '1':
                        source[prop] = True
                    else:
                        source[prop] = False
                elif schema[prop]['type'] == 'list':
                    source[prop] = source[prop].split(',')
                elif schema[prop]['type'] == 'integer':
                    source[prop] = int(source[prop])
        return source

    def update_later(later, inserted, ressource, field, schema):
        """
        Update field of ressource hav link with other ressources (objectid in backend)

        :param later:
        :type later: dict
        :param inserted:
        :type inserted: dict
        :param ressource: ressource name (command, contact, host...)
        :type ressource: str
        :param field: field of ressource to update
        :type field: str
        :return: None
        """
        def get_template(ressource, value):
            """

            :param ressource:
            :param value: value of use
            :return:
            """
            if isinstance(value, basestring):
                value = value.split()
            for template_value in reversed(value):
                template_value = template_value.strip()
                if template_value not in template[ressource]:
                    errors_found.append("# Undeclared template: %s for %s" %
                                        (template_value, ressource))
                    continue
                print ("Template: %s - %s" % (template_value, template[ressource][template_value]))
                if 'use' in template[ressource][template_value]:
                    get_template(ressource, template[ressource][template_value]['use'])
                for key, val in iteritems(template[ressource][template_value]):
                    if key not in ['register', 'name', 'use']:
                        data[key] = val
                    elif key == 'name':
                        if val not in inserted[ressource]:
                            errors_found.append("# Unknown resource %s for %s in %s" %
                                                (val, key, inserted[ressource]))
                        else:
                            data['use'].append(inserted[ressource][val])

        headers = {'Content-Type': 'application/json'}
        for (index, item) in iteritems(later[ressource][field]):
            if field == 'use':
                data = {'use': []}
                get_template(ressource, item['value'])
                # data = update_types(data, schema)
                use_data = []
                for template_id in reversed(data['use']):
                    use_data.append(template_id)
                data['use'] = use_data
            else:
                if item['type'] == 'simple':
                    data = {field: inserted[item['ressource']][item['value']]}
                elif item['type'] == 'list':
                    data = {field: []}
                    for val in item['value']:
                        val = val.strip()
                        if val not in inserted[item['ressource']]:
                            errors_found.append("# Unknown %s: %s for %s" % (item['ressource'],
                                                                             val, ressource))
                        else:
                            data[field].append(inserted[item['ressource']][val])

            headers['If-Match'] = item['_etag']
            resp = backend.patch(''.join([ressource, '/', index]), data, headers, True)
            if '_status' in resp:
                if resp['_status'] == 'ERR':
                    raise ValueError(resp['_issues'])
                elif resp['_status'] == 'OK':
                    for (ind, it) in iteritems(later[ressource]):
                        if index in later[ressource][ind]:
                            later[ressource][ind][index]['_etag'] = resp['_etag']

    def manage_ressource(r_name, inserted, later, data_later, id_name, schema):
        """

        data_later = [{'field': 'use', 'type': 'simple|list', 'ressource': 'command'}]

        :param r_name:
        :param inserted:
        :param later:
        :param data_later:
        :return:
        """
        if r_name not in inserted:
            inserted[r_name] = {}
        if r_name not in template:
            template[r_name] = {}
        if r_name not in later:
            later[r_name] = {}
        for k, values in enumerate(data_later):
            if values['field'] not in later[r_name]:
                later[r_name][values['field']] = {}
        if r_name in conf:
            for item in conf[r_name]:
                later_tmp = {}
                headers = {
                    'Content-Type': 'application/json',
                }
                if 'imported_from' in item:
                    del item['imported_from']
                for p in item:
                    item[p] = item[p][0]
                # Hack for check_command_args
                if r_name in ['host', 'service']:
                    if 'check_command' in item:
                        commands = item['check_command'].split('!', 1)
                        item['check_command'] = commands[0]
                        if len(commands) == 2:
                            item['check_command_args'] = commands[1]

                # convert type (boolean, integer...)
                item = update_types(item, schema['schema'])
                for k, values in enumerate(data_later):
                    # {'field': 'hostgroups', 'type': 'list', 'ressource': 'hostgroup'},
                    if values['field'] in item:
                        if values['type'] == 'simple':
                            if values['now'] \
                                    and values['ressource'] in inserted \
                                    and item[values['field']] in inserted[values['ressource']]:
                                item[values['field']] = inserted[
                                    values['ressource']
                                ][item[values['field']]]
                            else:
                                later_tmp[values['field']] = item[values['field']]
                                del item[values['field']]
                        elif values['type'] == 'list':
                            add = True
                            objectsid = []
                            if values['now']:
                                if isinstance(item[values['field']], basestring):
                                    item[values['field']] = item[values['field']].split()
                                for keylist, vallist in enumerate(item[values['field']]):
                                    vallist = vallist.strip()
                                    if values['ressource'] in inserted:
                                        if vallist in inserted[values['ressource']]:
                                            objectsid.append(inserted[values['ressource']][vallist])
                                    else:
                                        add = False
                            else:
                                add = False
                            if add:
                                item[values['field']] = objectsid
                            else:
                                later_tmp[values['field']] = item[values['field']]
                                del item[values['field']]

                # special case of timeperiod
                if r_name == 'timeperiod':
                    fields = ['imported_from', 'use', 'name', 'definition_order', 'register',
                              'timeperiod_name', 'alias', 'dateranges', 'exclude', 'is_active']
                    item['dateranges'] = []
                    prop_to_del = []
                    for prop in item:
                        if prop not in fields:
                            item['dateranges'].append({prop: item[prop]})
                            prop_to_del.append(prop)
                    for prop in prop_to_del:
                        del item[prop]
                # if template add to template
                if 'register' in item:
                    if not item['register']:
                        if 'name' in item:
                            template[r_name][item['name']] = item.copy()
                        else:
                            print("***** Missing name property in template: %s" % item)
                            if 'service_description' in item:
                                item['name'] = item['service_description']
                                template[r_name][item['name']] = item.copy()
                            elif 'host_name' in item:
                                item['name'] = item['host_name']
                                template[r_name][item['name']] = item.copy()
                print("before_post")
                print("%s : %s:" % (r_name, item))
                try:
                    response = backend.post(r_name, item, headers)
                except BackendException as e:
                    print("***** Exception: %s" % str(e))
                    if "_issues" in e.response:
                        print("ERROR: %s" % e.response['_issues'])
                else:
                    print("POST response : %s:" % (response))
                    if id_name in item:
                        inserted[r_name][item[id_name]] = response['_id']
                    else:
                        inserted[r_name][item['name']] = response['_id']
                    for k, values in enumerate(data_later):
                        if values['field'] in later_tmp:
                            later[r_name][values['field']][response['_id']] = {
                                'type': values['type'],
                                'ressource': values['ressource'],
                                'value': later_tmp[values['field']],
                                '_etag': response['_etag']
                            }

    later = {}
    inserted = {}
    template = {}

    print("~~~~~~~~~~~~~~~~~~~~~~ add commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [{'field': 'use', 'type': 'list', 'ressource': 'command', 'now': False}]
    schema = command.get_schema()
    manage_ressource('command', inserted, later, data_later, 'command_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'command', 'use', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add timeperiods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [{'field': 'use', 'type': 'list', 'ressource': 'timeperiod', 'now': False}]
    schema = timeperiod.get_schema()
    manage_ressource('timeperiod', inserted, later, data_later, 'timeperiod_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post timeperiods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'timeperiod', 'use', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add hostgroups ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [
        {
            'field': 'members', 'type': 'list', 'ressource': 'host', 'now': False
        },
        {
            'field': 'hostgroup_members', 'type': 'list', 'ressource': 'hostgroup', 'now': False
        }
    ]
    schema = hostgroup.get_schema()
    manage_ressource('hostgroup', inserted, later, data_later, 'hostgroup_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post hostgroups ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'hostgroup', 'hostgroup_members', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add hostdependency ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [{'field': 'use', 'type': 'list', 'ressource': 'hostdependency', 'now': False}]
    schema = hostdependency.get_schema()
    manage_ressource('hostdependency', inserted, later, data_later, 'name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post hostdependency ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'hostdependency', 'use', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add servicedependency ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [{'field': 'use', 'type': 'list', 'ressource': 'servicedependency', 'now': False}]
    schema = servicedependency.get_schema()
    manage_ressource('servicedependency', inserted, later, data_later, 'name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post servicedependency ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'servicedependency', 'use', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add serviceextinfo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [{'field': 'use', 'type': 'list', 'ressource': 'serviceextinfo', 'now': False}]
    schema = serviceextinfo.get_schema()
    manage_ressource('serviceextinfo', inserted, later, data_later, 'name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post serviceextinfo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'serviceextinfo', 'use', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add trigger ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [{'field': 'use', 'type': 'list', 'ressource': 'trigger', 'now': False}]
    schema = trigger.get_schema()
    manage_ressource('trigger', inserted, later, data_later, 'trigger_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post trigger ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'trigger', 'use', schema)

    # print("~~~~~~~~~~~~~~~~~~~~~~ add contact ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    # data_later = [
    # {'field': 'use', 'type': 'simple', 'ressource': 'contact'},
    # {'field': 'contactgroups', 'type': 'list', 'ressource': 'contactgroup'},
    # {'field': 'host_notification_period', 'type': 'simple', 'ressource': 'timeperiod'},
    # {'field': 'service_notification_period', 'type': 'simple', 'ressource': 'timeperiod'},
    # {'field': 'host_notification_commands', 'type': 'list', 'ressource': 'command'},
    # {'field': 'service_notification_commands', 'type': 'list', 'ressource': 'command'}
    # ]
    # schema = contact.get_schema()
    # manage_ressource('contact', inserted, later, data_later, 'contact_name', schema)
    # print("~~~~~~~~~~~~~~~~~~~~~~ post contact ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    # update_later(later, inserted, 'contact', 'use', schema)
    # update_later(later, inserted, 'contact', 'host_notification_period', schema)
    # update_later(later, inserted, 'contact', 'service_notification_period', schema)
    # update_later(later, inserted, 'contact', 'host_notification_commands', schema)
    # update_later(later, inserted, 'contact', 'service_notification_commands', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add contactgroup ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [
        {'field': 'members', 'type': 'list', 'ressource': 'contact', 'now': False},
        {'field': 'contactgroup_members', 'type': 'list', 'ressource': 'contactgroup', 'now': False}
    ]
    schema = contactgroup.get_schema()
    manage_ressource('contactgroup', inserted, later, data_later, 'contactgroup_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post contactgroup ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    # update_later(later, inserted, 'contactgroup', 'members', schema)
    update_later(later, inserted, 'contactgroup', 'contactgroup_members', schema)
    # update_later(later, inserted, 'contact', 'contactgroups', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add contactrestrictrole ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [
        {'field': 'contact', 'type': 'simple', 'ressource': 'contact', 'now': False}
    ]
    schema = contactrestrictrole.get_schema()
    manage_ressource('contactrestrictrole', inserted, later, data_later, 'contact', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post contactrestrictrole ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    # update_later(later, inserted, 'contactrestrictrole', 'contact', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add escalation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [
        {'field': 'use', 'type': 'list', 'ressource': 'escalation', 'now': False},
        {'field': 'contacts', 'type': 'list', 'ressource': 'contact', 'now': False},
        {'field': 'contact_groups', 'type': 'list', 'ressource': 'contactgroup', 'now': True}
    ]
    schema = escalation.get_schema()
    manage_ressource('escalation', inserted, later, data_later, 'escalation_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post escalation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'escalation', 'use', schema)
    # update_later(later, inserted, 'escalation', 'contacts', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add host ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [
        {'field': 'use', 'type': 'list', 'ressource': 'host', 'now': False},
        {'field': 'parents', 'type': 'list', 'ressource': 'host', 'now': False},
        {'field': 'hostgroups', 'type': 'list', 'ressource': 'hostgroup', 'now': True},
        {'field': 'check_command', 'type': 'simple', 'ressource': 'command', 'now': True},
        {'field': 'check_period', 'type': 'simple', 'ressource': 'timeperiod', 'now': True},
        {'field': 'contacts', 'type': 'list', 'ressource': 'contact', 'now': False},
        {'field': 'contact_groups', 'type': 'list', 'ressource': 'contactgroup', 'now': True},
        {'field': 'notification_period', 'type': 'simple', 'ressource': 'timeperiod', 'now': True},
        {'field': 'escalations', 'type': 'list', 'ressource': 'escalation', 'now': True}
    ]
    schema = host.get_schema()
    manage_ressource('host', inserted, later, data_later, 'host_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post host ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'host', 'use', schema)
    update_later(later, inserted, 'host', 'parents', schema)
    # update_later(later, inserted, 'host', 'contacts', schema)
    update_later(later, inserted, 'hostgroup', 'members', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add hostextinfo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [{'field': 'use', 'type': 'list', 'ressource': 'hostextinfo', 'now': False}]
    schema = hostextinfo.get_schema()
    manage_ressource('hostextinfo', inserted, later, data_later, 'host_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post hostextinfo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'hostextinfo', 'use', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add hostescalation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [{'field': 'use', 'type': 'list', 'ressource': 'hostescalation', 'now': False},
                  {'field': 'contacts', 'type': 'list', 'ressource': 'contact', 'now': False},
                  {'field': 'contact_groups', 'type': 'list', 'ressource': 'contactgroup',
                   'now': True}]
    schema = hostescalation.get_schema()
    manage_ressource('hostescalation', inserted, later, data_later, 'host_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post hostescalation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'hostescalation', 'use', schema)
    # update_later(later, inserted, 'hostescalation', 'contacts', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add servicegroups ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [
        {'field': 'members', 'type': 'list', 'ressource': 'service', 'now': False},
        {'field': 'servicegroup_members', 'type': 'list', 'ressource': 'servicegroup', 'now': False}
    ]
    schema = servicegroup.get_schema()
    manage_ressource('servicegroup', inserted, later, data_later, 'servicegroup_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post servicegroups ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'servicegroup', 'servicegroup_members', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [
        {'field': 'use', 'type': 'list', 'ressource': 'service', 'now': False},
        {'field': 'host_name', 'type': 'simple', 'ressource': 'host', 'now': True},
        {'field': 'servicegroups', 'type': 'list', 'ressource': 'servicegroup', 'now': True},
        {'field': 'check_command', 'type': 'simple', 'ressource': 'command', 'now': True},
        {'field': 'check_period', 'type': 'simple', 'ressource': 'timeperiod', 'now': True},
        {'field': 'notification_period', 'type': 'simple', 'ressource': 'timeperiod', 'now': True},
        {'field': 'contacts', 'type': 'list', 'ressource': 'contact', 'now': False},
        {'field': 'contact_groups', 'type': 'list', 'ressource': 'contactgroup', 'now': True},
        {'field': 'escalations', 'type': 'list', 'ressource': 'escalation', 'now': True},
        {'field': 'maintenance_period', 'type': 'simple', 'ressource': 'timeperiod', 'now': True},
        {'field': 'service_dependencies', 'type': 'list', 'ressource': 'service', 'now': True}
    ]
    schema = service.get_schema()
    manage_ressource('service', inserted, later, data_later, 'service_description', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'service', 'use', schema)
    # update_later(later, inserted, 'service', 'contacts', schema)
    update_later(later, inserted, 'servicegroup', 'members', schema)

    print("~~~~~~~~~~~~~~~~~~~~~~ add serviceescalation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    data_later = [
        {'field': 'use', 'type': 'list', 'ressource': 'serviceescalation', 'now': False},
        {'field': 'contacts', 'type': 'list', 'ressource': 'contact', 'now': False},
        {'field': 'contact_groups', 'type': 'list', 'ressource': 'contactgroup', 'now': True}
    ]
    schema = serviceescalation.get_schema()
    manage_ressource('serviceescalation', inserted, later, data_later, 'host_name', schema)
    print("~~~~~~~~~~~~~~~~~~~~~~ post serviceescalation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
    update_later(later, inserted, 'serviceescalation', 'use', schema)
    # update_later(later, inserted, 'serviceescalation', 'contacts', schema)

    # print all errors found
    print('################################## errors report ##################################')
    for error in errors_found:
        print(error)
    print('###################################################################################')
    def test_04_login(self):
        """
        Test with right username / password

        :return: None
        """
        print('')
        print('test accepted connection with username/password')

        # Create client API
        backend = Backend(self.backend_address)

        print('Login ...')
        assert backend.login('admin', 'admin')
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_true(backend.authenticated)

        print('Logout ...')
        backend.logout()
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_false(backend.authenticated)

        print('Login ...')
        print('authenticated:', backend.authenticated)
        assert backend.login('admin', 'admin')
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_true(backend.authenticated)

        print('Logout ...')
        backend.logout()
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_false(backend.authenticated)

        print('Logout ...')
        backend.logout()
        print('authenticated:', backend.authenticated)
        print('token:', backend.token)
        assert_false(backend.authenticated)

        print('get object ... must be refused!')
        with assert_raises(BackendException) as cm:
            backend.get('host')
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 401, str(ex))

        print('get_all object ... must be refused!')
        with assert_raises(BackendException) as cm:
            backend.get_all('host')
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 401, str(ex))

        print('get all domains ... must be refused!')
        with assert_raises(BackendException) as cm:
            backend.get_domains()
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 401, str(ex))

        print('post data ... must be refused!')
        with assert_raises(BackendException) as cm:
            data = {'fake': 'fake'}
            backend.post('user', data=data)
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 401, str(ex))

        print('patch data ... must be refused!')
        with assert_raises(BackendException) as cm:
            data = {'fake': 'fake'}
            headers = {'If-Match': ''}
            backend.patch('user', data=data, headers=headers)
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 405, str(ex))

        print('delete data ... must be refused!')
        with assert_raises(BackendException) as cm:
            headers = {'If-Match': ''}
            backend.delete('user', headers=headers)
        ex = cm.exception
        print('exception:', str(ex.code))
        assert_true(ex.code == 401, str(ex))
class AlignakBackendScheduler(BaseModule):
    """
    This class is used to send live states to alignak-backend
    """

    def __init__(self, mod_conf):
        """
        Module initialization

        mod_conf is a dictionary that contains:
        - all the variables declared in the module configuration file
        - a 'properties' value that is the module properties as defined globally in this file

        :param mod_conf: module configuration file as a dictionary
        """
        BaseModule.__init__(self, mod_conf)

        # pylint: disable=global-statement
        global logger
        logger = logging.getLogger("alignak.module.%s" % self.alias)

        logger.debug("inner properties: %s", self.__dict__)
        logger.debug("received configuration: %s", mod_conf.__dict__)

        self.url = getattr(mod_conf, "api_url", "http://localhost:5000")
        self.backend = Backend(self.url)
        self.backend.token = getattr(mod_conf, "token", "")
        self.backend_connected = False
        if self.backend.token == "":
            self.getToken(
                getattr(mod_conf, "username", ""),
                getattr(mod_conf, "password", ""),
                getattr(mod_conf, "allowgeneratetoken", False),
            )

    # Common functions
    def do_loop_turn(self):
        """This function is called/used when you need a module with
        a loop function (and use the parameter 'external': True)
        """
        logger.info("[Backend Scheduler] In loop")
        time.sleep(1)

    def getToken(self, username, password, generatetoken):
        """
        Authenticate and get the token

        :param username: login name
        :type username: str
        :param password: password
        :type password: str
        :param generatetoken: if True allow generate token, otherwise not generate
        :type generatetoken: bool
        :return: None
        """
        generate = "enabled"
        if not generatetoken:
            generate = "disabled"

        try:
            self.backend.login(username, password, generate)
            self.backend_connected = True
        except BackendException as exp:
            logger.warning("Alignak backend is not available for login. " "No backend connection.")
            logger.exception("Exception: %s", exp)
            self.backend_connected = False

    def hook_load_retention(self, scheduler):
        """
        Load retention data from alignak-backend

        :param scheduler: scheduler instance of alignak
        :type scheduler: object
        :return: None
        """

        all_data = {"hosts": {}, "services": {}}
        if not self.backend_connected:
            logger.error(
                "[Backend Scheduler] Alignak backend connection is not available. " "Skipping objects retention load."
            )
        else:
            # Get data from the backend
            response = self.backend.get_all("retentionhost")
            for host in response["_items"]:
                # clean unusable keys
                hostname = host["host"]
                for key in ["_created", "_etag", "_id", "_links", "_updated", "host"]:
                    del host[key]
                all_data["hosts"][hostname] = host
            response = self.backend.get_all("retentionservice")
            for service in response["_items"]:
                # clean unusable keys
                servicename = (service["service"][0], service["service"][1])
                for key in ["_created", "_etag", "_id", "_links", "_updated", "service"]:
                    del service[key]
                all_data["services"][servicename] = service

        scheduler.restore_retention_data(all_data)

    def hook_save_retention(self, scheduler):
        """
        Save retention data from alignak-backend

        :param scheduler: scheduler instance of alignak
        :type scheduler: object
        :return: None
        """
        data_to_save = scheduler.get_retention_data()

        if not self.backend_connected:
            logger.error("Alignak backend connection is not available. " "Skipping objects retention save.")
            return

        # clean hosts we will re-upload the retention
        response = self.backend.get_all("retentionhost")
        for host in response["_items"]:
            if host["host"] in data_to_save["hosts"]:
                delheaders = {"If-Match": host["_etag"]}
                self.backend.delete("/".join(["retentionhost", host["_id"]]), headers=delheaders)

        # Add all hosts after
        for host in data_to_save["hosts"]:
            data_to_save["hosts"][host]["host"] = host
            try:
                self.backend.post("retentionhost", data=data_to_save["hosts"][host])
            except BackendException as exp:
                logger.error("Post retentionhost for host error")
                logger.error("Response: %s", exp.response)
                logger.exception("Exception: %s", exp)
                return
        logger.info("%d hosts saved in retention", len(data_to_save["hosts"]))

        # clean services we will re-upload the retention
        response = self.backend.get_all("retentionservice")
        for service in response["_items"]:
            if (service["service"][0], service["service"][1]) in data_to_save["services"]:
                delheaders = {"If-Match": service["_etag"]}
                self.backend.delete("/".join(["retentionservice", service["_id"]]), headers=delheaders)

        # Add all services after
        for service in data_to_save["services"]:
            data_to_save["services"][service]["service"] = service
            try:
                self.backend.post("retentionservice", data=data_to_save["services"][service])
            except BackendException as exp:
                logger.error("Post retentionservice for service error")
                logger.exception("Exception: %s", exp)
                return
        logger.info("%d services saved in retention", len(data_to_save["services"]))