async def test_unregister_service(self, client): async def async_mock(): return "" service_id = "c6bbf3c8-f43c-4b0f-ac48-f597f510da0b" sname = "name" stype = "Southbound" sprotocol = "http" saddress = "localhost" sport = 1234 smgtport = 4321 data = [] record = ServiceRecord(service_id, sname, stype, sprotocol, saddress, sport, smgtport) data.append(record) Server._storage_client = MagicMock(StorageClient) with patch.object(ServiceRegistry, 'get', return_value=data) as patch_get_unregister: with patch.object(ServiceRegistry, 'unregister') as patch_unregister: with patch.object(AuditLogger, '__init__', return_value=None): with patch.object(AuditLogger, 'information', return_value=async_mock()) as audit_info_patch: resp = await client.delete('/foglamp/service/{}'.format(service_id)) assert 200 == resp.status r = await resp.text() json_response = json.loads(r) assert {'id': service_id, 'message': 'Service unregistered'} == json_response args, kwargs = audit_info_patch.call_args assert 'SRVUN' == args[0] assert {'name': sname} == args[1] args1, kwargs1 = patch_unregister.call_args assert (service_id,) == args1 args2, kwargs2 = patch_get_unregister.call_args assert {'idx': service_id} == kwargs2
async def test_delete_service_exception(self, mocker, client): resp = await client.delete("/foglamp/service") assert 405 == resp.status assert 'Method Not Allowed' == resp.reason reg_id = 'd607c5be-792f-4993-96b7-b513674e7d3b' name = 'Test' mock_registry = [ ServiceRecord(reg_id, name, "Southbound", "http", "localhost", "8118", "8118") ] mocker.patch.object(connect, 'get_storage_async') mocker.patch.object(service, "get_schedule", side_effect=Exception) resp = await client.delete("/foglamp/service/{}".format(name)) assert 500 == resp.status assert resp.reason is '' async def mock_bad_result(): return {"count": 0, "rows": []} mock_registry[0]._status = ServiceRecord.Status.Shutdown mocker.patch.object(service, "get_schedule", return_value=mock_bad_result()) resp = await client.delete("/foglamp/service/{}".format(name)) assert 404 == resp.status assert '{} service does not exist.'.format(name) == resp.reason
async def test_ping_service_fail_bad_url(self, test_server, loop): # GIVEN a service is running at a given URL app = web.Application() with patch.object(FoglampMicroservice, "__init__", return_value=None): m = mServiceThing() m._start_time = time.time() # fill route table routes.setup(app, m) server = await test_server(app) server.start_server(loop=loop) # WHEN the service is pinged with a BAD URL with patch.object(utils._logger, "error") as log: service = ServiceRecord("d", "test", "Southbound", "http", server.host + "1", 1, server.port) url_ping = "{}://{}:{}/foglamp/service/ping".format( service._protocol, service._address, service._management_port) log_params = 'Ping not received for Service %s id %s at url %s attempt_count %s', service._name, service._id, \ url_ping, utils._MAX_ATTEMPTS+1 resp = await utils.ping_service(service, loop=loop) # THEN ping response is NOT received assert resp is False log.assert_called_once_with(*log_params)
async def test_shutdown_service_pass(self, test_server, loop): # GIVEN a service is running at a given URL app = web.Application() with patch.object(FoglampMicroservice, "__init__", return_value=None): m = mServiceThing() # fill route table routes.setup(app, m) server = await test_server(app) server.start_server(loop=loop) # WHEN shutdown call is made at the valid URL with patch.object(utils._logger, "info") as log: service = ServiceRecord("d", "test", "Southbound", "http", server.host, 1, server.port) url_shutdown = "{}://{}:{}/foglamp/service/shutdown".format( service._protocol, service._address, service._management_port) log_params1 = "Shutting down the %s service %s ...", service._type, service._name log_params2 = 'Service %s, id %s at url %s successfully shutdown', service._name, service._id, url_shutdown resp = await utils.shutdown_service(service, loop=loop) # THEN shutdown returns success assert resp is True log.assert_called_with(*log_params2) assert 2 == log.call_count
async def test_get_services(self, client): sid = "c6bbf3c8-f43c-4b0f-ac48-f597f510da0b" sname = "name" stype = "Southbound" sprotocol = "http" saddress = "localhost" sport = 1234 smgtport = 4321 data = [] record = ServiceRecord(sid, sname, stype, sprotocol, saddress, sport, smgtport) data.append(record) with patch.object(ServiceRegistry, 'all', return_value=data) as patch_get_all_service_reg: resp = await client.get('/foglamp/service') assert 200 == resp.status r = await resp.text() json_response = json.loads(r) assert { 'services': [{ 'id': sid, 'management_port': smgtport, 'address': saddress, 'name': sname, 'type': stype, 'protocol': sprotocol, 'status': 'running', 'service_port': sport }] } == json_response args, kwargs = patch_get_all_service_reg.call_args assert {} == kwargs
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
def connect(self, core_management_host, core_management_port): svc = self._get_storage_service(host=core_management_host, port=core_management_port) if len(svc) == 0: raise InvalidServiceInstance self.service = ServiceRecord(s_id=svc["id"], s_name=svc["name"], s_type=svc["type"], s_port=svc["service_port"], m_port=svc["management_port"], s_address=svc["address"], s_protocol=svc["protocol"]) return self
def register(cls, name, s_type, address, port, management_port, protocol='http'): """ registers the service instance :param name: name of the service :param s_type: a valid service type; e.g. Storage, Core, Southbound :param address: any IP or host address :param port: a valid positive integer :param management_port: a valid positive integer for management operations e.g. ping, shutdown :param protocol: defaults to http :return: registered services' uuid """ new_service = True try: current_service = cls.get(name=name) except service_registry_exceptions.DoesNotExist: pass else: # Re: FOGL-1123 if current_service[0]._status in [ ServiceRecord.Status.Running, ServiceRecord.Status.Unresponsive ]: raise service_registry_exceptions.AlreadyExistsWithTheSameName else: new_service = False current_service_id = current_service[0]._id if port is not None and cls.check_address_and_port(address, port): raise service_registry_exceptions.AlreadyExistsWithTheSameAddressAndPort if cls.check_address_and_mgt_port(address, management_port): raise service_registry_exceptions.AlreadyExistsWithTheSameAddressAndManagementPort if port is not None and (not isinstance(port, int)): raise service_registry_exceptions.NonNumericPortError if not isinstance(management_port, int): raise service_registry_exceptions.NonNumericPortError if new_service is False: # Remove current service to enable the service to register with new management port etc cls.remove_from_registry(current_service_id) service_id = str( uuid.uuid4()) if new_service is True else current_service_id registered_service = ServiceRecord(service_id, name, s_type, protocol, address, port, management_port) cls._registry.append(registered_service) cls._logger.info("Registered {}".format(str(registered_service))) return service_id
def services_health_litmus_test(): all_svc_status = [ ServiceRecord.Status(int(service_record._status)).name.upper() for service_record in ServiceRegistry.all() ] if 'FAILED' in all_svc_status: return 'red' elif 'UNRESPONSIVE' in all_svc_status: return 'amber' return 'green'
def test_init(self, s_port): obj = ServiceRecord("some id", "aName", "Storage", "http", "127.0.0.1", s_port, 1234) assert "some id" == obj._id assert "aName" == obj._name assert "Storage" == obj._type if s_port: assert int(s_port) == obj._port else: assert obj._port is None assert 1234 == obj._management_port assert 1 == obj._status
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})
def get_service_records(): sr_list = list() for service_record in ServiceRegistry.all(): sr_list.append( { 'name': service_record._name, 'type': service_record._type, 'address': service_record._address, 'management_port': service_record._management_port, 'service_port': service_record._port, 'protocol': service_record._protocol, 'status': ServiceRecord.Status(int(service_record._status)).name.lower() }) recs = {'services': sr_list} return recs
def connect(self, core_management_host, core_management_port): svc = self._get_storage_service(host=core_management_host, port=core_management_port) if len(svc) == 0: raise InvalidServiceInstance self.service = ServiceRecord(s_id=svc["id"], s_name=svc["name"], s_type=svc["type"], s_port=svc["service_port"], m_port=svc["management_port"], s_address=svc["address"], s_protocol=svc["protocol"]) # found_services = Service.Instances.get(name="FogLAMP Storage") # svc = found_services[0] # # retry for a while? # if svc is None: # raise InvalidServiceInstance # self.service = svc return self
def register(cls, name, s_type, address, port, management_port, protocol='http'): """ registers the service instance :param name: name of the service :param s_type: a valid service type; e.g. Storage, Core, Southbound :param address: any IP or host address :param port: a valid positive integer :param management_port: a valid positive integer for management operations e.g. ping, shutdown :param protocol: defaults to http :return: registered services' uuid """ try: cls.get(name=name) except service_registry_exceptions.DoesNotExist: pass else: raise service_registry_exceptions.AlreadyExistsWithTheSameName if port != None and cls.check_address_and_port(address, port): raise service_registry_exceptions.AlreadyExistsWithTheSameAddressAndPort if cls.check_address_and_mgt_port(address, management_port): raise service_registry_exceptions.AlreadyExistsWithTheSameAddressAndManagementPort if port != None and (not isinstance(port, int)): raise service_registry_exceptions.NonNumericPortError if not isinstance(management_port, int): raise service_registry_exceptions.NonNumericPortError service_id = str(uuid.uuid4()) registered_service = ServiceRecord(service_id, name, s_type, protocol, address, port, management_port) cls._registry.append(registered_service) cls._logger.info("Registered {}".format(str(registered_service))) return service_id
async def test_ping_service_pass(self, aiohttp_server, loop): # GIVEN a service is running at a given URL app = web.Application() with patch.object(FoglampMicroservice, "__init__", return_value=None): m = mServiceThing() m._start_time = time.time() # fill route table routes.setup(app, m) server = await aiohttp_server(app) await server.start_server(loop=loop) # WHEN the service is pinged with a valid URL with patch.object(utils._logger, "info") as log: service = ServiceRecord("d", "test", "Southbound", "http", server.host, 1, server.port) url_ping = "{}://{}:{}/foglamp/service/ping".format(service._protocol, service._address, service._management_port) log_params = 'Ping received for Service %s id %s at url %s', service._name, service._id, url_ping resp = await utils.ping_service(service, loop=loop) # THEN ping response is received assert resp is True log.assert_called_once_with(*log_params)
async def test_shutdown_service_fail_bad_url(self, aiohttp_server, loop): # GIVEN a service is running at a given URL app = web.Application() with patch.object(FoglampMicroservice, "__init__", return_value=None): m = mServiceThing() # fill route table routes.setup(app, m) server = await aiohttp_server(app) await server.start_server(loop=loop) # WHEN shutdown call is made at the invalid URL with patch.object(utils._logger, "info") as log1: with patch.object(utils._logger, "exception") as log2: service = ServiceRecord("d", "test", "Southbound", "http", server.host, 1, server.port+1) log_params1 = "Shutting down the %s service %s ...", service._type, service._name resp = await utils.shutdown_service(service, loop=loop) # THEN shutdown fails assert resp is False log1.assert_called_with(*log_params1) assert log2.called is True
async def test_delete_service(self, mocker, client): sch_id = '0178f7b6-d55c-4427-9106-245513e46416' reg_id = 'd607c5be-792f-4993-96b7-b513674e7d3b' name = "Test" sch_name = "Test Service" mock_registry = [ ServiceRecord(reg_id, name, "Southbound", "http", "localhost", "8118", "8118") ] async def mock_result(): return { "count": 1, "rows": [ { "id": sch_id, "process_name": name, "schedule_name": sch_name, "schedule_type": "1", "schedule_interval": "0", "schedule_time": "0", "schedule_day": "0", "exclusive": "t", "enabled": "t" }, ] } mocker.patch.object(connect, 'get_storage_async') get_schedule = mocker.patch.object(service, "get_schedule", return_value=mock_result()) scheduler = mocker.patch.object(server.Server, "scheduler", MagicMock()) delete_schedule = mocker.patch.object(scheduler, "delete_schedule", return_value=asyncio.sleep(.1)) disable_schedule = mocker.patch.object(scheduler, "disable_schedule", return_value=asyncio.sleep(.1)) delete_configuration = mocker.patch.object( ConfigurationManager, "delete_category_and_children_recursively", return_value=asyncio.sleep(.1)) get_registry = mocker.patch.object(ServiceRegistry, 'get', return_value=mock_registry) remove_registry = mocker.patch.object(ServiceRegistry, 'remove_from_registry') mock_registry[0]._status = ServiceRecord.Status.Shutdown resp = await client.delete("/foglamp/service/{}".format(sch_name)) assert 200 == resp.status result = await resp.json() assert "Service {} deleted successfully.".format( sch_name) == result['result'] assert 1 == get_schedule.call_count args, kwargs = get_schedule.call_args_list[0] assert sch_name in args assert 1 == delete_schedule.call_count delete_schedule_calls = [ call(UUID('0178f7b6-d55c-4427-9106-245513e46416')) ] delete_schedule.assert_has_calls(delete_schedule_calls, any_order=True) assert 1 == disable_schedule.call_count disable_schedule_calls = [ call(UUID('0178f7b6-d55c-4427-9106-245513e46416')) ] disable_schedule.assert_has_calls(disable_schedule_calls, any_order=True) assert 1 == delete_configuration.call_count args, kwargs = delete_configuration.call_args_list[0] assert sch_name in args assert 1 == get_registry.call_count get_registry_calls = [call(name=sch_name)] get_registry.assert_has_calls(get_registry_calls, any_order=True) assert 1 == remove_registry.call_count remove_registry_calls = [call('d607c5be-792f-4993-96b7-b513674e7d3b')] remove_registry.assert_has_calls(remove_registry_calls, any_order=True)
def test_init_with_valid_type(self, s_type): obj = ServiceRecord("some id", "aName", s_type, "http", "127.0.0.1", None, 1234) assert "some id" == obj._id assert "aName" == obj._name assert s_type == obj._type
def test_init_with_invalid_type(self): with pytest.raises(Exception) as excinfo: obj = ServiceRecord("some id", "aName", "BLAH", "http", "127.0.0.1", None, 1234) assert excinfo.type is ServiceRecord.InvalidServiceType
from foglamp.services.core import routes from foglamp.services.core import connect from foglamp.common.service_record import ServiceRecord from foglamp.common.configuration_manager import ConfigurationManager from foglamp.services.core.service_registry.service_registry import ServiceRegistry from foglamp.services.core.service_registry import exceptions as service_registry_exceptions from foglamp.services.core.api import notification from foglamp.common.audit_logger import AuditLogger __author__ = "Amarendra K Sinha" __copyright__ = "Copyright (c) 2017 Dianomic Systems" __license__ = "Apache 2.0" __version__ = "${VERSION}" mock_registry = [ ServiceRecord(uuid.uuid4(), "Notifications", "Notification", "http", "localhost", "8118", "8118") ] rule_config = [ { "name": "threshold", "version": "1.0.0", "type": "notificationRule", "interface": "1.0", "config": { "plugin": { "description": "The accepted tolerance", "default": "threshold", "type": "string" }, "builtin": { "description": "Is this a builtin plugin?",