Ejemplo n.º 1
0
    async def unregister(cls, request):
        """ Unregister a service

        :Example:
            curl -X DELETE  http://localhost:<core mgt port>/foglamp/service/dc9bfc01-066a-4cc0-b068-9c35486db87f
        """

        try:
            service_id = request.match_info.get('service_id', None)

            try:
                services = ServiceRegistry.get(idx=service_id)
            except service_registry_exceptions.DoesNotExist:
                raise ValueError(
                    'Service with {} does not exist'.format(service_id))

            ServiceRegistry.unregister(service_id)

            if cls._storage_client_async is not None and services[
                    0]._name not in ("FogLAMP Storage", "FogLAMP Core"):
                try:
                    cls._audit = AuditLogger(cls._storage_client_async)
                    await cls._audit.information('SRVUN',
                                                 {'name': services[0]._name})
                except Exception as ex:
                    _logger.exception(str(ex))

            _resp = {'id': str(service_id), 'message': 'Service unregistered'}

            return web.json_response(_resp)
        except ValueError as ex:
            raise web.HTTPNotFound(reason=str(ex))
Ejemplo n.º 2
0
    def test_unregister(self, mocker):
        mocker.patch.object(InterestRegistry, '__init__', return_value=None)
        mocker.patch.object(InterestRegistry, 'get', return_value=list())

        with patch.object(ServiceRegistry._logger, 'info') as log_info1:
            reg_id = ServiceRegistry.register("A name", "Storage", "127.0.0.1",
                                              1234, 4321, 'http')
            assert 1 == len(ServiceRegistry._registry)
        assert 1 == log_info1.call_count
        arg, kwarg = log_info1.call_args
        assert arg[0].startswith('Registered service instance id=')
        assert arg[0].endswith(
            ': <A name, type=Storage, protocol=http, address=127.0.0.1, service port=1234,'
            ' management port=4321, status=1>')

        with patch.object(ServiceRegistry._logger, 'info') as log_info2:
            s_id = ServiceRegistry.unregister(reg_id)
            assert 36 == len(s_id)  # uuid version 4 len
            assert 1 == len(ServiceRegistry._registry)
            s = ServiceRegistry.get(idx=s_id)
            assert s[0]._status == 2
        assert 1 == log_info2.call_count
        args, kwargs = log_info2.call_args
        assert args[0].startswith('Stopped service instance id=')
        assert args[0].endswith(
            ': <A name, type=Storage, protocol=http, address=127.0.0.1, service port=1234,'
            ' management port=4321, status=2>')
Ejemplo n.º 3
0
    async def unregister(cls, request):
        """ Unregister a service

        :Example: curl -X DELETE  http://localhost:8082/foglamp/service/dc9bfc01-066a-4cc0-b068-9c35486db87f
        """

        try:
            service_id = request.match_info.get('service_id', None)

            if not service_id:
                raise web.HTTPBadRequest(reason='Service id is required')

            try:
                ServiceRegistry.get(idx=service_id)
            except service_registry_exceptions.DoesNotExist:
                raise web.HTTPBadRequest(
                    reason='Service with {} does not exist'.format(service_id))

            ServiceRegistry.unregister(service_id)

            _resp = {'id': str(service_id), 'message': 'Service unregistered'}

            return web.json_response(_resp)
        except ValueError as ex:
            raise web.HTTPNotFound(reason=str(ex))
Ejemplo n.º 4
0
    async def test_unregister(self, mocker):
        # register a service
        with patch.object(Service._logger, 'info') as log_info:
            idx = Service.register("StorageService2", "Storage", "127.0.0.1",
                                   8888, 1888)
            assert str(uuid.UUID(idx, version=4)) == idx
        assert 1 == log_info.call_count
        arg, kwarg = log_info.call_args
        assert arg[0].startswith('Registered service instance id=')
        assert arg[0].endswith(
            ': <StorageService2, type=Storage, protocol=http, address=127.0.0.1, service port=8888,'
            ' management port=1888, status=1>')

        mocker.patch.object(InterestRegistry, '__init__', return_value=None)
        mocker.patch.object(InterestRegistry, 'get', return_value=list())

        # deregister the same
        with patch.object(Service._logger, 'info') as log_info2:
            t = Service.unregister(idx)
            assert idx == t
        assert 1 == log_info2.call_count
        args, kwargs = log_info2.call_args
        assert args[0].startswith('Stopped service instance id=')
        assert args[0].endswith(
            ': <StorageService2, type=Storage, protocol=http, address=127.0.0.1, '
            'service port=8888, management port=1888, status=2>')

        s = Service.get(idx)
        assert s[0]._status == 2  # Unregistered
Ejemplo n.º 5
0
 async def _monitor_loop(self):
     """Main loop for the scheduler"""
     # check health of all micro-services every N seconds
     while True:
         for service_record in ServiceRegistry.all():
             url = "{}://{}:{}/foglamp/service/ping".format(
                 service_record._protocol, service_record._address,
                 service_record._management_port)
             async with aiohttp.ClientSession() as session:
                 try:
                     async with session.get(
                             url, timeout=self._ping_timeout) as resp:
                         text = await resp.text()
                         res = json.loads(text)
                         if res["uptime"] is None:
                             raise ValueError('Improper Response')
                 except:
                     service_record._status = 0
                     ServiceRegistry.unregister(service_record._id)
                     self._logger.info(
                         "Unregistered the failed micro-service %s",
                         service_record.__repr__())
                 else:
                     service_record._status = 1
         await asyncio.ensure_future(asyncio.sleep(self._sleep_interval))
Ejemplo n.º 6
0
 async def test_duplicate_address_port_registration(self):
     idx1 = Service.register("StorageService1", "Storage", "127.0.0.1",
                             9999, 1999)
     assert str(uuid.UUID(idx1, version=4)) == idx1
     with pytest.raises(AlreadyExistsWithTheSameAddressAndPort) as excinfo:
         Service.register("StorageService2", "Storage", "127.0.0.1", 9999,
                          1998)
     assert str(excinfo).endswith('AlreadyExistsWithTheSameAddressAndPort')
Ejemplo n.º 7
0
    async def test_run_missing_service_record(self, reset_state):
        storage_client_mock = MagicMock(spec=StorageClient)
        cfg_mgr = ConfigurationManager(storage_client_mock)

        s_id_1 = ServiceRegistry.register('sname1', 'Storage', 'saddress1', 1,
                                          1, 'http')
        s_id_2 = ServiceRegistry.register('sname2', 'Southbound', 'saddress2',
                                          2, 2, 'http')
        s_id_3 = ServiceRegistry.register('sname3', 'Southbound', 'saddress3',
                                          3, 3, 'http')
        i_reg = InterestRegistry(cfg_mgr)
        id_fake_1 = i_reg.register('fakeid', 'catname1')
        id_1_1 = i_reg.register(s_id_1, 'catname1')
        id_1_2 = i_reg.register(s_id_1, 'catname2')
        id_2_1 = i_reg.register(s_id_2, 'catname1')
        id_2_2 = i_reg.register(s_id_2, 'catname2')
        id_3_3 = i_reg.register(s_id_3, 'catname3')

        # used to mock client session context manager
        async def async_mock(return_value):
            return return_value

        class AsyncSessionContextManagerMock(MagicMock):
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)

            async def __aenter__(self):
                client_response_mock = MagicMock(spec=aiohttp.ClientResponse)
                client_response_mock.text.side_effect = [async_mock(None)]
                status_mock = Mock()
                status_mock.side_effect = [200]
                client_response_mock.status = status_mock()
                return client_response_mock

            async def __aexit__(self, *args):
                return None

        with patch.object(ConfigurationManager,
                          'get_category_all_items',
                          return_value=async_mock(None)) as cm_get_patch:
            with patch.object(aiohttp.ClientSession,
                              'post',
                              return_value=AsyncSessionContextManagerMock()
                              ) as post_patch:
                with patch.object(cb._LOGGER, 'exception') as exception_patch:
                    await cb.run('catname1')
        cm_get_patch.assert_called_once_with('catname1')
        exception_patch.assert_called_once_with(
            'Unable to notify microservice with uuid %s as it is not found in the service registry',
            'fakeid')
        post_patch.assert_has_calls([
            call('http://saddress1:1/foglamp/change',
                 data='{"category": "catname1", "items": null}',
                 headers={'content-type': 'application/json'}),
            call('http://saddress2:2/foglamp/change',
                 data='{"category": "catname1", "items": null}',
                 headers={'content-type': 'application/json'})
        ])
Ejemplo n.º 8
0
 def test_register_with_bad_management_port(self):
     """raise NonNumericPortError"""
     with pytest.raises(Exception) as excinfo:
         with patch.object(ServiceRegistry._logger, 'info') as log_info:
             ServiceRegistry.register("B name", "Storage", "127.0.0.1",
                                      1234, "m01", 'http')
             assert 0 == len(ServiceRegistry._registry)
         assert 0 == log_info.call_count
     assert excinfo.type is NonNumericPortError
Ejemplo n.º 9
0
    async def test__monitor_good_uptime(self):
        async def async_mock(return_value):
            return return_value

        # used to mock client session context manager

        class AsyncSessionContextManagerMock(MagicMock):
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)

            async def __aenter__(self):
                client_response_mock = MagicMock(spec=aiohttp.ClientResponse)
                # mock response (good)
                client_response_mock.text.side_effect = [
                    async_mock('{"uptime": "bla"}')
                ]
                return client_response_mock

            async def __aexit__(self, *args):
                return None

        # as monitor loop is as infinite loop, this exception is thrown when we need to exit the loop

        class TestMonitorException(Exception):
            pass

        # register a service
        with patch.object(ServiceRegistry._logger, 'info') as log_info:
            s_id_1 = ServiceRegistry.register('sname1', 'Storage', 'saddress1',
                                              1, 1, 'protocol1')
        assert 1 == log_info.call_count
        args, kwargs = log_info.call_args
        assert args[0].startswith('Registered service instance id=')
        assert args[0].endswith(
            ': <sname1, type=Storage, protocol=protocol1, address=saddress1, service port=1, '
            'management port=1, status=1>')
        monitor = Monitor()
        monitor._sleep_interval = Monitor._DEFAULT_SLEEP_INTERVAL
        monitor._max_attempts = Monitor._DEFAULT_MAX_ATTEMPTS

        # throw the TestMonitorException when sleep is called (end of infinite loop)
        with patch.object(Monitor,
                          '_sleep',
                          side_effect=TestMonitorException()):
            with patch.object(aiohttp.ClientSession,
                              'get',
                              return_value=AsyncSessionContextManagerMock()):
                with pytest.raises(Exception) as excinfo:
                    await monitor._monitor_loop()
                assert excinfo.type is TestMonitorException
        # service is good, so it should remain in the service registry
        assert len(ServiceRegistry.get(idx=s_id_1)) is 1
        assert ServiceRegistry.get(
            idx=s_id_1)[0]._status is ServiceRecord.Status.Running
Ejemplo n.º 10
0
    async def get_service(cls, request):
        """ Returns a list of all services or as per name &|| type filter

        :Example:
            curl -X GET  http://localhost:<core mgt port>/foglamp/service
            curl -X GET  http://localhost:<core mgt port>/foglamp/service?name=X&type=Storage
        """
        service_name = request.query[
            'name'] if 'name' in request.query else None
        service_type = request.query[
            'type'] if 'type' in request.query else None

        try:
            if not service_name and not service_type:
                services_list = ServiceRegistry.all()
            elif service_name and not service_type:
                services_list = ServiceRegistry.get(name=service_name)
            elif not service_name and service_type:
                services_list = ServiceRegistry.get(s_type=service_type)
            else:
                services_list = ServiceRegistry.filter_by_name_and_type(
                    name=service_name, s_type=service_type)
        except service_registry_exceptions.DoesNotExist as ex:
            if not service_name and not service_type:
                msg = 'No service found'
            elif service_name and not service_type:
                msg = 'Service with name {} does not exist'.format(
                    service_name)
            elif not service_name and service_type:
                msg = 'Service with type {} does not exist'.format(
                    service_type)
            else:
                msg = 'Service with name {} and type {} does not exist'.format(
                    service_name, service_type)

            raise web.HTTPNotFound(reason=msg)

        services = []
        for service in services_list:
            svc = dict()
            svc["id"] = service._id
            svc["name"] = service._name
            svc["type"] = service._type
            svc["address"] = service._address
            svc["management_port"] = service._management_port
            svc["protocol"] = service._protocol
            svc["status"] = ServiceRecord.Status(int(
                service._status)).name.lower()
            if service._port:
                svc["service_port"] = service._port
            services.append(svc)

        return web.json_response({"services": services})
Ejemplo n.º 11
0
async def delete_service(request):
    """ Delete an existing service

    :Example:
        curl -X DELETE http://localhost:8081/foglamp/service/<svc name>
    """
    try:
        svc = request.match_info.get('service_name', None)
        storage = connect.get_storage_async()

        result = await get_schedule(storage, svc)
        if result['count'] == 0:
            return web.HTTPNotFound(
                reason='{} service does not exist.'.format(svc))

        config_mgr = ConfigurationManager(storage)

        # In case of notification service, if notifications exists, then deletion is not allowed
        if 'notification' in result['rows'][0]['process_name']:
            notf_children = await config_mgr.get_category_child(
                category_name="Notifications")
            children = [x['key'] for x in notf_children]
            if len(notf_children) > 0:
                return web.HTTPBadRequest(
                    reason=
                    'Notification service `{}` can not be deleted, as {} notification instances exist.'
                    .format(svc, children))

        # First disable the schedule
        svc_schedule = result['rows'][0]
        sch_id = uuid.UUID(svc_schedule['id'])
        if svc_schedule['enabled'].lower() == 't':
            await server.Server.scheduler.disable_schedule(sch_id)

        # Delete all configuration for the service name
        await config_mgr.delete_category_and_children_recursively(svc)

        # Remove from registry as it has been already shutdown via disable_schedule() and since
        # we intend to delete the schedule also, there is no use of its Service registry entry
        try:
            services = ServiceRegistry.get(name=svc)
            ServiceRegistry.remove_from_registry(services[0]._id)
        except service_registry_exceptions.DoesNotExist:
            pass

        # Delete schedule
        await server.Server.scheduler.delete_schedule(sch_id)
    except Exception as ex:
        raise web.HTTPInternalServerError(reason=ex)
    else:
        return web.json_response(
            {'result': 'Service {} deleted successfully.'.format(svc)})
Ejemplo n.º 12
0
    def test_register_with_same_address_and_mgt_port(self):
        """raise AlreadyExistsWithTheSameAddressAndManagementPort"""
        ServiceRegistry.register("A name", "Storage", "127.0.0.1", 1, 1234,
                                 'http')
        assert 1 == len(ServiceRegistry._registry)

        with pytest.raises(Exception) as excinfo:
            with patch.object(ServiceRegistry._logger, 'info') as log_i:
                ServiceRegistry.register("B name", "Storage", "127.0.0.1", 2,
                                         1234, 'http')
        assert 0 == log_i.call_count
        assert excinfo.type is AlreadyExistsWithTheSameAddressAndManagementPort
        assert 1 == len(ServiceRegistry._registry)
Ejemplo n.º 13
0
    async def test_unregister(self):
        # register a service
        idx = Service.register("StorageService2", "Storage", "127.0.0.1", 8888,
                               1888)
        assert str(uuid.UUID(idx, version=4)) == idx

        # deregister the same
        t = Service.unregister(idx)
        assert idx == t

        with pytest.raises(DoesNotExist) as excinfo:
            Service.get(idx)
        assert str(excinfo).endswith('DoesNotExist')
Ejemplo n.º 14
0
 async def test_get_fail(self):
     with pytest.raises(DoesNotExist) as excinfo:
         with patch.object(Service._logger, 'info') as log_info:
             Service.register("StorageService", "Storage", "127.0.0.1",
                              8888, 9999)
             Service.get('incorrect_id')
         assert 1 == log_info.call_count
         args, kwargs = log_info.call_args
         assert args[0].startswith('Registered service instance id=')
         assert args[0].endswith(
             ': <StorageService1, type=Storage, protocol=http, address=127.0.0.1, service port=8888,'
             ' management port=9999, status=1>')
     assert str(excinfo).endswith('DoesNotExist')
Ejemplo n.º 15
0
    def test_register_with_same_name(self):
        """raise AlreadyExistsWithTheSameName"""

        ServiceRegistry.register("A name", "Storage", "127.0.0.1", 1, 2,
                                 'http')
        assert 1 == len(ServiceRegistry._registry)

        with pytest.raises(Exception) as excinfo:
            with patch.object(ServiceRegistry._logger, 'info') as log_i:
                ServiceRegistry.register("A name", "Storage", "127.0.0.2", 3,
                                         4, 'http')
        assert 0 == log_i.call_count
        assert excinfo.type is AlreadyExistsWithTheSameName
        assert 1 == len(ServiceRegistry._registry)
Ejemplo n.º 16
0
    async def test_duplicate_address_port_registration(self):
        with patch.object(Service._logger, 'info') as log_info:
            idx1 = Service.register("StorageService1", "Storage", "127.0.0.1",
                                    9999, 1999)
            assert str(uuid.UUID(idx1, version=4)) == idx1
        assert 1 == log_info.call_count
        args, kwargs = log_info.call_args
        assert args[0].startswith('Registered service instance id=')
        assert args[0].endswith(
            ': <StorageService1, type=Storage, protocol=http, address=127.0.0.1, service port=9999,'
            ' management port=1999, status=1>')

        with pytest.raises(AlreadyExistsWithTheSameAddressAndPort) as excinfo:
            Service.register("StorageService2", "Storage", "127.0.0.1", 9999,
                             1998)
        assert str(excinfo).endswith('AlreadyExistsWithTheSameAddressAndPort')
Ejemplo n.º 17
0
async def get_plugin(request):
    """ GET lists of rule plugins and delivery plugins

    :Example:
        curl -X GET http://localhost:8081/foglamp/notification/plugin
    """
    try:
        notification_service = ServiceRegistry.get(
            s_type=ServiceRecord.Type.Notification.name)
        _address, _port = notification_service[
            0]._address, notification_service[0]._port
    except service_registry_exceptions.DoesNotExist:
        raise web.HTTPNotFound(reason="No Notification service available.")

    try:
        url = 'http://{}:{}/notification/rules'.format(_address, _port)
        rule_plugins = json.loads(await _hit_get_url(url))

        url = 'http://{}:{}/notification/delivery'.format(_address, _port)
        delivery_plugins = json.loads(await _hit_get_url(url))
    except Exception as ex:
        raise web.HTTPInternalServerError(reason=ex)
    else:
        return web.json_response({
            'rules': rule_plugins,
            'delivery': delivery_plugins
        })
Ejemplo n.º 18
0
    def stop_storage(cls):
        """ stop Storage service """

        # TODO: FOGL-653 shutdown implementation
        # remove me, and allow this call in service registry API

        found_services = ServiceRegistry.get(name="FogLAMP Storage")
        svc = found_services[0]
        if svc is None:
            return

        management_api_url = '{}:{}'.format(svc._address, svc._management_port)

        conn = http.client.HTTPConnection(management_api_url)
        # TODO: need to set http / https based on service protocol

        conn.request('POST', url='/foglamp/service/shutdown', body=None)
        r = conn.getresponse()

        # TODO: FOGL-615
        # log error with message if status is 4xx or 5xx
        if r.status in range(400, 500):
            _logger.error("Client error code: %d", r.status)
        if r.status in range(500, 600):
            _logger.error("Server error code: %d", r.status)

        res = r.read().decode()
        conn.close()
        return json.loads(res)
Ejemplo n.º 19
0
async def _services_with_assets(storage_client, south_services):
    sr_list = list()
    try:
        try:
            services_from_registry = ServiceRegistry.get(s_type="Southbound")
        except DoesNotExist:
            services_from_registry = []

        def is_svc_in_service_registry(name):
            return next(
                (svc for svc in services_from_registry if svc._name == name),
                None)

        for s_record in services_from_registry:
            sr_list.append({
                'name':
                s_record._name,
                'address':
                s_record._address,
                'management_port':
                s_record._management_port,
                'service_port':
                s_record._port,
                'protocol':
                s_record._protocol,
                'status':
                ServiceRecord.Status(int(s_record._status)).name.lower(),
                'assets':
                await _get_tracked_assets_and_readings(storage_client,
                                                       s_record._name),
                'schedule_enabled':
                await _get_schedule_status(storage_client, s_record._name)
            })
        for s_name in south_services:
            south_svc = is_svc_in_service_registry(s_name)
            if not south_svc:
                sr_list.append({
                    'name':
                    s_name,
                    'address':
                    '',
                    'management_port':
                    '',
                    'service_port':
                    '',
                    'protocol':
                    '',
                    'status':
                    '',
                    'assets':
                    await
                    _get_tracked_assets_and_readings(storage_client, s_name),
                    'schedule_enabled':
                    await _get_schedule_status(storage_client, s_name)
                })
    except:
        raise
    else:
        return sr_list
Ejemplo n.º 20
0
    def _register_core(cls, host, mgt_port, service_port):
        core_service_id = ServiceRegistry.register(name="FogLAMP Core",
                                                   s_type="Core",
                                                   address=host,
                                                   port=service_port,
                                                   management_port=mgt_port)

        return core_service_id
Ejemplo n.º 21
0
    async def test_run_no_interests_in_cat(self):
        storage_client_mock = MagicMock(spec=StorageClientAsync)
        cfg_mgr = ConfigurationManager(storage_client_mock)

        with patch.object(ServiceRegistry._logger, 'info') as log_info:
            s_id_1 = ServiceRegistry.register('sname1', 'Storage', 'saddress1',
                                              1, 1, 'http')
            s_id_2 = ServiceRegistry.register('sname2', 'Southbound',
                                              'saddress2', 2, 2, 'http')
            s_id_3 = ServiceRegistry.register('sname3', 'Southbound',
                                              'saddress3', 3, 3, 'http')
        assert 3 == log_info.call_count
        i_reg = InterestRegistry(cfg_mgr)
        i_reg.register(s_id_1, 'catname2')
        i_reg.register(s_id_2, 'catname2')
        i_reg.register(s_id_3, 'catname3')

        # used to mock client session context manager
        async def async_mock(return_value):
            return return_value

        class AsyncSessionContextManagerMock(MagicMock):
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)

            async def __aenter__(self):
                client_response_mock = MagicMock(spec=aiohttp.ClientResponse)
                client_response_mock.text.side_effect = [async_mock(None)]
                status_mock = Mock()
                status_mock.side_effect = [200]
                client_response_mock.status = status_mock()
                return client_response_mock

            async def __aexit__(self, *args):
                return None

        with patch.object(ConfigurationManager,
                          'get_category_all_items') as cm_get_patch:
            with patch.object(aiohttp.ClientSession, 'post') as post_patch:
                await cb.run('catname1')
            post_patch.assert_not_called()
        cm_get_patch.assert_not_called()
Ejemplo n.º 22
0
 async def test_register(self):
     with patch.object(Service._logger, 'info') as log_info:
         idx = Service.register("StorageService1", "Storage", "127.0.0.1",
                                9999, 1999)
         assert str(uuid.UUID(idx, version=4)) == idx
     assert 1 == log_info.call_count
     args, kwargs = log_info.call_args
     assert args[0].startswith('Registered service instance id=')
     assert args[0].endswith(
         ': <StorageService1, type=Storage, protocol=http, address=127.0.0.1, service port=9999,'
         ' management port=1999, status=1>')