def __init__(self, **kwargs): """ Default factory, used for TCP servers, implements IDefaultFactory :param kwargs: """ self.__logger = Logger() self.config = kwargs.get('config', None) if self.config is None: self.__logger.warning('Config for IDefaultFactory is not provided, failback to defaults...') tcp = TCP() self.config = { 'port': 8484, 'tcp_back_log': 50, 'service_name': 'Default TCP Server' } self.config = tcp.to_object(self.config) self.protocol = kwargs.get('protocol', DefaultLineReceiver) self.name = kwargs.get('name', self.config.service_name) self.port = kwargs.get('port', self.config.port) self.belong_to = kwargs.get('belong_to', False)
def __init__(self, **kwargs): """ Default factory, used for TCP servers, implements IDefaultFactory :param kwargs: """ self.__logger = Logger() self.config = kwargs.get('config', None) if self.config is None: web = WEB() self.__logger.warning('Config for IDefaultWebFactory is not provided, failback to defaults...') self.config = { 'port': 8000, 'www_root': '', 'service_name': 'Default WEB Server', 'http_methods': ['GET'] } self.config = web.to_object(self.config) self.name = kwargs.get('name', self.config.service_name) self.port = kwargs.get('port', self.config.port) self.methods = kwargs.get('http_methods', self.config.http_methods) self.belong_to = kwargs.get('belong_to', False)
def __init__(self, factory): """ Just settings :param factory: :return: void """ self.__logger = Logger() self.factory = factory
class DefaultWSFactory(object): """ A class that represent a default WS Factory """ def __init__(self, **kwargs): """ Default factory, used for TCP servers, implements IDefaultFactory :param kwargs: :return: """ self.__logger = Logger() self.config = kwargs.get('config', None) if self.config is None: ws = WS() self.__logger.warning( 'Config for IDefaultFactory is not provided, failback to defaults...' ) self.config = { 'url': 'ws://localhost:8585', 'port': 8585, 'hostname': 'localhost', 'protocol': 'ws' } self.config = ws.to_object(self.config) self.ws_protocol = self.config.protocol self.name = kwargs.get('name', 'DefaultWSFactory') self.port = kwargs.get('port', self.config.port) self.url = kwargs.get('port', self.config.url) self.belong_to = kwargs.get('belong_to', False) self.ws_server_factory = DefaultWSFactoryRunner if self.ws_protocol == 'wss': key = self.config.keys.key crt = self.config.keys.crt if key is None or crt is None: raise AttributeError( 'WS over SSL required attribute a key and a crt') self.crt_keys = dict(key=key, crt=crt) self.ws_msg_protocol = DefaultWSProtocol
class DefaultWSFactory(object): """ A class that represent a default WS Factory """ def __init__(self, **kwargs): """ Default factory, used for TCP servers, implements IDefaultFactory :param kwargs: :return: """ self.__logger = Logger() self.config = kwargs.get('config', None) if self.config is None: ws = WS() self.__logger.warning('Config for IDefaultFactory is not provided, failback to defaults...') self.config = { 'url': 'ws://localhost:8585', 'port': 8585, 'hostname': 'localhost', 'protocol': 'ws' } self.config = ws.to_object(self.config) self.ws_protocol = self.config.protocol self.name = kwargs.get('name', 'DefaultWSFactory') self.port = kwargs.get('port', self.config.port) self.url = kwargs.get('port', self.config.url) self.belong_to = kwargs.get('belong_to', False) self.ws_server_factory = DefaultWSFactoryRunner if self.ws_protocol == 'wss': key = self.config.keys.key crt = self.config.keys.crt if key is None or crt is None: raise AttributeError('WS over SSL required attribute a key and a crt') self.crt_keys = dict(key=key, crt=crt) self.ws_msg_protocol = DefaultWSProtocol
def __init__(self, validate_msg): """ Accept message that have to be validated :param validate_msg: currently we supporting only a json validators """ super(Validator, self).__init__(validate_msg) self.__msg = Message(validate_msg) self.__logger = Logger()
def __init__(self, comp): """ A RootAPI constructor will init an logger and will initialize a public attribute - self.comp which representing an input object as DynamicProtocol :param comp: :type comp: DynamicProtocol :return: void """ self.comp = comp self.__logger = Logger()
def on_connect(self): """ A function that is called when we got connected to a WAMP router. This function is attached to our WampDefaultComponent only if protocol is WSS :param self: :return: void """ cfg = Config().get_wamp() log = Logger() log.info('Connecting to router...') self.join(self.config.realm, [u'wampcra'], cfg.user)
def __init__(self, comp): """ A NonRootAPi constructor will init a logger and a comp property for an object that have to be compare with the existing registered protocols :param comp: :type comp: an input object as DynamicProtocol :return: void """ self.comp = comp self.__logger = Logger()
def __init__(self, validator): """ The constructor will init a logger and will init a public attribute - self.validator that is a validated requests, here we have to determinate which protocol and API have to process this request :param validator: :type validator: IValidator :return: void """ self.validator = validator self.__logger = Logger()
def __init__(self, factory): """ :param factory: :return: void """ base = BaseWebMethods() self.__logger = Logger() for meth in factory.config.http_methods: try: name = 'render_{}'.format(meth) web_resource = getattr(base, name) except AttributeError as e: allow_methods = [ render.replace('render_', '') for render in base.__class__.__dict__ if render.startswith('render') ] print('[ !!!!! Warning: ] Ivalid web methods was provided, available are: {}, error: {}'.format( ', '.join(allow_methods), e )) else: setattr(DefaultWebResource, name, web_resource) Resource.__init__(self)
def __init__(self, factories_source, file_loader=None, out_filter=None): """ The constructor will init the storage and all objects that are matched from our filters :param file_loader: :param factories_source: :return: void """ super(RegisterFactories, self).__init__() self.__gsm = getGlobalSiteManager() self.file_loader = file_loader or FileFactoryLoader() if not out_filter: out_filter = [] self.default_filter = list( set(out_filter + [IUserStorage, IUserFactory])) self.__objects = self.file_loader.load(factories_source, self.default_filter) self.__storage = createObject('storageregister') self.__logger = Logger()
def __init__(self, factory): """ :param factory: :type IDefaultFactory """ self.factory = factory self.__logger = Logger() self.listener = None if self.factory.ws_protocol == 'wss': self.__ssl_context = ssl.DefaultOpenSSLContextFactory( self.factory.crt_keys.get('key'), self.factory.crt_keys.get('crt') ) self.setName(self.factory.name) if self.factory.belong_to is False: self.__application = service.Application(self.factory.name, uid=1000, gid=1000) self.service_collection = service.IServiceCollection(self.__application) else: self.service_collection = self.factory.belong_to
class Validator(BaseValidator): """ Our main validator will trying to resolve the type of incoming request """ def __init__(self, validate_msg): """ Accept message that have to be validated :param validate_msg: currently we supporting only a json validators """ super(Validator, self).__init__(validate_msg) self.__msg = Message(validate_msg) self.__logger = Logger() def validate(self): """ Will walk through all subscribers from IValidator type the first one will be returned as valid type. In this way we can supporting a lot of types on the same port/service :return: matched IValidator subscriber """ for sub in subscribers([self.__msg], IValidator): msg = sub.validate() self.is_valid = msg.is_valid self.message_type = msg.message_type self.message = msg.message # we want only one correct type of our message so # only one validator will response with True if self.is_valid is True: self.__logger.debug('Matched type is: {}'.format(self.message_type)) return self self.__logger.warning('Main Validator - There are no subscribers from type IValidator') return False
def __init__(self, **kwargs): """ A constructor will init our wamp session and a runner. A runner is a callable that have to be processed when we lose the connection :param kwargs: keys: 'session' and 'runner' :return: void """ if 'session' not in kwargs or 'runner' not in kwargs: raise Exception('session is not provided') self.__session = kwargs['session'] self.__runner = kwargs['runner'] self.logger = Logger() self.config = kwargs.get('config')
class Validator(BaseValidator): """ Our main validator will trying to resolve the type of incoming request """ def __init__(self, validate_msg): """ Accept message that have to be validated :param validate_msg: currently we supporting only a json validators """ super(Validator, self).__init__(validate_msg) self.__msg = Message(validate_msg) self.__logger = Logger() def validate(self): """ Will walk through all subscribers from IValidator type the first one will be returned as valid type. In this way we can supporting a lot of types on the same port/service :return: matched IValidator subscriber """ for sub in subscribers([self.__msg], IValidator): msg = sub.validate() self.is_valid = msg.is_valid self.message_type = msg.message_type self.message = msg.message # we want only one correct type of our message so # only one validator will response with True if self.is_valid is True: self.__logger.debug('Matched type is: {}'.format( self.message_type)) return self self.__logger.warning( 'Main Validator - There are no subscribers from type IValidator') return False
def __init__(self, validate_msg): """ :param validate_msg: IMessage """ super(JSONValidator, self).__init__(validate_msg) self.__logger = Logger()
def on_challenge(self, challenge): """ A function that is called when we got onChallenge event aka authentication to a WAMP router. This function is attached to our WampDefaultComponent only if protocol is WSS :param self: :param challenge: :return: digital signature decode in ascii """ log = Logger() log.info('On Challenge...') if challenge.method == u"wampcra": cfg = Config().get_wamp() password = { u'%s' % cfg.user: u'%s' % cfg.password } if u'salt' in challenge.extra: key = auth.derive_key( password[cfg.user].encode('utf8'), challenge.extra['salt'].encode('utf8'), challenge.extra.get('iterations', None), challenge.extra.get('keylen', None) ) else: key = password[cfg.user].encode('utf8') signature = auth.compute_wcs(key, challenge.extra['challenge'].encode('utf8')) return signature.decode('ascii') else: raise Exception("don't know how to compute challenge for authmethod {}".format(challenge.method))
class Time(BaseRootAPI): def __init__(self, factory): super(Time, self).__init__(factory) self.factory = factory self.logger = Logger() def process_factory(self): if self.factory.time == 'get': self.factory.time = str(datetime.now()) else: self.factory.time = 'service unavailable' self.logger.debug('IN TIME API: {}'.format(self.factory.to_dict())) return self.factory
class RootAPI(object): """ An API that implements an IJSONResourceRootAPI """ def __init__(self, comp): """ A RootAPI constructor will init an logger and will initialize a public attribute - self.comp which representing an input object as DynamicProtocol :param comp: :type comp: DynamicProtocol :return: void """ self.comp = comp self.__logger = Logger() def check(self): """ Will trying to get a RootAPI and if match one will fired a process_factory of an implementation API :return: False if not success otherwise the response from process_factory """ for api in subscribers([self.comp], IJSONResourceRootAPI): if api.__class__.__name__.lower() in self.comp.to_dict().keys(): self.__logger.debug('Candidate API {} for {}'.format( api.__class__.__name__, self.comp.__class__.__name__ )) self.__logger.info('Successful apply API {} for {}'.format( api.__class__.__name__, self.comp.__class__.__name__ )) return api.process_factory() return False
class Clock(BaseRootAPIWampMixin): def __init__(self, factory=None): super(Clock, self).__init__(factory) self.logger = Logger() @staticmethod def print_result_callback(res, factory): factory.clock = res return factory @staticmethod def print_result_callback_error(err): print err return False def process_factory(self): self.logger.debug(self.factory.to_dict()) # we trying to get a wamo session session = self.get_session() # if exist we will get the time from a rpc with a name 'get_time' # registered as wamp component inside components/wamp/rpc if session: result = session.call('get_time') result.addCallback(self.print_result_callback, self.factory) result.addErrback(self.print_result_callback_error) return result else: # if does not exist self.factory.clock = str(datetime.now()) return self.factory
def on_challenge(self, challenge): """ A function that is called when we got onChallenge event aka authentication to a WAMP router. This function is attached to our WampDefaultComponent only if protocol is WSS :param self: :param challenge: :return: digital signature decode in ascii """ log = Logger() log.info('On Challenge...') if challenge.method == u"wampcra": cfg = Config().get_wamp() password = {u'%s' % cfg.user: u'%s' % cfg.password} if u'salt' in challenge.extra: key = auth.derive_key(password[cfg.user].encode('utf8'), challenge.extra['salt'].encode('utf8'), challenge.extra.get('iterations', None), challenge.extra.get('keylen', None)) else: key = password[cfg.user].encode('utf8') signature = auth.compute_wcs( key, challenge.extra['challenge'].encode('utf8')) return signature.decode('ascii') else: raise Exception( "don't know how to compute challenge for authmethod {}".format( challenge.method))
class Reconnect(object): """ A reconnect class """ def __init__(self, **kwargs): """ A constructor will init our wamp session and a runner. A runner is a callable that have to be processed when we lose the connection :param kwargs: keys: 'session' and 'runner' :return: void """ if 'session' not in kwargs or 'runner' not in kwargs: raise Exception('session is not provided') self.__session = kwargs['session'] self.__runner = kwargs['runner'] self.logger = Logger() self.config = kwargs.get('config') def start(self): """ Start the reconnection :return: void """ try: self.logger.info( 'TRYING TO CONNECT TO {}'.format( self.config ) ) run = self.__runner(config=self.config) run.run(self.__session) except Exception as e: self.logger.error('RECONNECTING ERROR: {}'.format(e.message))
def __init__(self, **kwargs): """ The constructor will init self.cfg self.__gsm and if the protocol is wss, will attach on_connect and on_challenge to this object as methods :param kwargs: we looking for key 'config' :return: void """ config = kwargs.get('config') super(WampDefaultComponent, self).__init__(config) self.__logger = Logger() self.cfg = Config().get_wamp() self.__gsm = getGlobalSiteManager() if self.cfg.protocol == 'wss': self.__logger.info('WAMP is secure, switch to wss...') WampDefaultComponent.onConnect = on_connect WampDefaultComponent.onChallenge = on_challenge
class NonRootAPI(object): """ An API that will check for a Non Root API's """ def __init__(self, comp): """ A NonRootAPi constructor will init a logger and a comp property for an object that have to be compare with the existing registered protocols :param comp: :type comp: an input object as DynamicProtocol :return: void """ self.comp = comp self.__logger = Logger() def check(self): """ Will trying to match the candidate Non Root API check IJSONResourceAPI for more info :return: False if not success otherwise the response from matched API.method() """ for api in subscribers([self.comp], IJSONResourceAPI): if api.__class__.__name__.lower() in self.comp.to_dict().keys(): self.__logger.debug('Candidate API {} for {}'.format( api.__class__.__name__, self.comp.__class__.__name__)) candidate_api_name = self.comp.to_dict().get( api.__class__.__name__.lower()) try: # execute the candidate API method # and return the result candidate_api_result = getattr(api, candidate_api_name)() except AttributeError as e: msg = 'Candidate API {} for {} does not implement method {} error: {}' self.__logger.warning( msg.format(api.__class__.__name__, self.comp.__class__.__name__, candidate_api_name, e.message)) else: self.__logger.info('Successful apply API {} for {}'.format( api.__class__.__name__, self.comp.__class__.__name__)) return candidate_api_result return False
class RootAPI(object): """ An API that implements an IJSONResourceRootAPI """ def __init__(self, comp): """ A RootAPI constructor will init an logger and will initialize a public attribute - self.comp which representing an input object as DynamicProtocol :param comp: :type comp: DynamicProtocol :return: void """ self.comp = comp self.__logger = Logger() def check(self): """ Will trying to get a RootAPI and if match one will fired a process_factory of an implementation API :return: False if not success otherwise the response from process_factory """ for api in subscribers([self.comp], IJSONResourceRootAPI): if api.__class__.__name__.lower() in self.comp.to_dict().keys(): self.__logger.debug('Candidate API {} for {}'.format( api.__class__.__name__, self.comp.__class__.__name__)) self.__logger.info('Successful apply API {} for {}'.format( api.__class__.__name__, self.comp.__class__.__name__)) return api.process_factory() return False
class Reconnect(object): """ A reconnect class """ def __init__(self, **kwargs): """ A constructor will init our wamp session and a runner. A runner is a callable that have to be processed when we lose the connection :param kwargs: keys: 'session' and 'runner' :return: void """ if 'session' not in kwargs or 'runner' not in kwargs: raise Exception('session is not provided') self.__session = kwargs['session'] self.__runner = kwargs['runner'] self.logger = Logger() self.config = kwargs.get('config') def start(self): """ Start the reconnection :return: void """ try: self.logger.info('TRYING TO CONNECT TO {}'.format(self.config)) run = self.__runner(config=self.config) run.run(self.__session) except Exception as e: self.logger.error('RECONNECTING ERROR: {}'.format(e.message))
def subscriber_dispatcher(self, sub_data): """ Callback function for our global subscriber will pass sub_data to GlobalSubscribeMessage and then will trying to get wamp component which implements IUserGlobalSubscriber and adapts IGlobalSubscribeMessage :param sub_data: """ log = Logger() try: result = IDispatcher(Validator(sub_data)).dispatch() # NETODO what exception can happen here? except Exception as e: import traceback print traceback.format_exc() log.warning('subscriber_dispatcher exception: {}'.format( e.message)) else: if IValidator.providedBy(result): log.warning('WAMP Message is invalid: {}'.format( result.message)) if result is not False and IJSONResource.providedBy(result): fac = None for sub in subscribers([result], IUserGlobalSubscriber): sub.subscribe(self) fac = True break if not fac: log.warning( 'There are no user definition for IUserGlobalSubscriber, message was skipped' )
def subscriber_dispatcher(self, sub_data): """ Callback function for our global subscriber will pass sub_data to GlobalSubscribeMessage and then will trying to get wamp component which implements IUserGlobalSubscriber and adapts IGlobalSubscribeMessage :param sub_data: """ log = Logger() try: result = IDispatcher(Validator(sub_data)).dispatch() # NETODO what exception can happen here? except Exception as e: import traceback print traceback.format_exc() log.warning('subscriber_dispatcher exception: {}'.format( e.message )) else: if IValidator.providedBy(result): log.warning('WAMP Message is invalid: {}'.format(result.message)) if result is not False and IJSONResource.providedBy(result): fac = None for sub in subscribers([result], IUserGlobalSubscriber): sub.subscribe(self) fac = True break if not fac: log.warning('There are no user definition for IUserGlobalSubscriber, message was skipped')
class DefaultLineReceiver(LineReceiver): """ A default implementation of Twisted LineReceiver protocol """ def __init__(self): """ A constructor will init the logger only :return: void """ self.__logger = Logger() def connectionMade(self): """ A callback method that is fired when connection is made :return: void """ self.__logger.info('Connections made') def connectionLost(self, reason='unexpected'): """ A callback method that is fired when connection is lost :param reason: :return: void """ self.__logger.info('Connection was closesd: {}'.format(reason)) def lineReceived(self, line): """ A callback method that is fired when live is received. Will validate, dispatch and choose the right API on the fly :param line: user input :type line: JSON :return: void """ self.__logger.info('Received line: {}'.format(line)) result = IDispatcher(Validator(line)).dispatch() result_helper = DispathcherResultHelper(result) result_helper.result_validation( self.sendLine, self.transport.loseConnection, 'TCP' )
class DefaultLineReceiver(LineReceiver): """ A default implementation of Twisted LineReceiver protocol """ def __init__(self): """ A constructor will init the logger only :return: void """ self.__logger = Logger() def connectionMade(self): """ A callback method that is fired when connection is made :return: void """ self.__logger.info('Connections made') def connectionLost(self, reason='unexpected'): """ A callback method that is fired when connection is lost :param reason: :return: void """ self.__logger.info('Connection was closesd: {}'.format(reason)) def lineReceived(self, line): """ A callback method that is fired when live is received. Will validate, dispatch and choose the right API on the fly :param line: user input :type line: JSON :return: void """ self.__logger.info('Received line: {}'.format(line)) result = IDispatcher(Validator(line)).dispatch() result_helper = DispathcherResultHelper(result) result_helper.result_validation(self.sendLine, self.transport.loseConnection, 'TCP')
def __init__(self, **kwargs): """ :param url: The WebSocket URL of the WAMP router to connect to (e.g. `ws://somehost.com:8090/somepath`) :type url: unicode :param realm: The WAMP realm to join the application session to. :type realm: unicode :param extra: Optional extra configuration to forward to the application component. :type extra: dict :param debug: Turn on low-level debugging. :type debug: bool :param debug_wamp: Turn on WAMP-level debugging. :type debug_wamp: bool :param debug_app: Turn on app-level debugging. :type debug_app: bool """ self.logger = Logger() self.config = kwargs.get('config', None) if self.config is None: self.config = {} wamp = WAMP() self.logger.warning('Config is not provided failback to defaults') self.config.update({ 'protocol': 'wss', 'hostname': 'localhost', 'port': 8080, # integer 'realm': 'realm1', 'path': 'ws', 'retry_interval': 2, # in seconds 'url': u'ws://localhost:8080/ws', 'service_name': 'A Default WAMP Service name"' }) self.config = wamp.to_object(self.config) self.url = kwargs.get('url', self.config.url) self.realm = u'{}'.format(kwargs.get('realm', self.config.realm)) self.extra = kwargs.get('extra', dict()) self.debug = kwargs.get('debug', False) self.debug_wamp = kwargs.get('debug_wamp', False) self.debug_app = kwargs.get('debug_app', False) self.belong_to = kwargs.get('belong_to', False) self.make = None self.protocol = kwargs.get('protocol', self.config.protocol) self.name = kwargs.get('name', self.config.service_name) self.port = kwargs.get('port', self.config.port) self.host = kwargs.get('host', self.config.hostname) self.path = kwargs.get('path', self.config.path)
def __init__(self, factory=None): super(Clock, self).__init__(factory) self.logger = Logger()
""" A Default implementation of WS protocol """ from zope.interface import implementer from autobahn.twisted.websocket import WebSocketServerProtocol from NetCatKS.NetCAT.api.interfaces import IWSProtocol from NetCatKS.Dispatcher import IDispatcher, DispathcherResultHelper from NetCatKS.Validators import Validator from NetCatKS.Logger import Logger __author__ = 'dimd' logger = Logger() @implementer(IWSProtocol) class DefaultWSProtocol(WebSocketServerProtocol): """ A class that implements a Default WS Protocol """ def onConnect(self, request): """ A callback that fired when connect occur :param request: :return: """ logger.info("Client connecting: {0}".format(request.peer))
class Dispatcher(object): """ Our Main dispatcher, Will trying to dispatch the request to API which provides functionality for it. The Dispatcher also adapts IValidator """ adapts(IValidator) def __init__(self, validator): """ The constructor will init a logger and will init a public attribute - self.validator that is a validated requests, here we have to determinate which protocol and API have to process this request :param validator: :type validator: IValidator :return: void """ self.validator = validator self.__logger = Logger() def __api_processor(self, valid_dispatch, valid_response, isubscriber): """ Will loop through a valid response objects and will compare it to a registered protocol subscribers :param valid_dispatch: :param valid_response: :param isubscriber: :return: The chosen API or False """ for sub in subscribers([valid_response], isubscriber): self.__logger.debug('Matched request subscribers: {}'.format(sub.__class__.__name__)) try: verifyObject(isubscriber, sub) except DoesNotImplement as e: self.__logger.warning('Incorrect implementation: {}'.format(e)) else: comp = sub.compare() if comp is not False and IJSONResource.providedBy(comp): self.__logger.debug('Signature compare to {}'.format(comp.__class__.__name__)) # trying to resolve API that will deal with these request if len(comp.to_dict().keys()) > 1: # process request without root element return NonRootAPI(comp).check() else: # root element return RootAPI(comp).check() # if there are no one subsciber from IJSONResource self.__logger.warning('The request {} from type {} was not recognized as a structure or an API'.format( valid_response.response, valid_dispatch.message_type )) return False def dispatch(self): """ When request is happening first will be validated, if valid_dispatch is False means we do not support this data type. You have to write your custom validator(s) inside components/validators :return: The response or False """ # validate for supporting types try: valid_dispatch = self.validator.validate() except Exception as e: self.__logger.debug('validate error: {}'.format(e.message)) return self.validator else: if valid_dispatch is False: return self.validator valid_response = ValidatorResponse(valid_dispatch.message) if valid_dispatch.message_type == 'JSON': return self.__api_processor( valid_dispatch, valid_response, IJSONResourceSubscriber )
class NonRootAPI(object): """ An API that will check for a Non Root API's """ def __init__(self, comp): """ A NonRootAPi constructor will init a logger and a comp property for an object that have to be compare with the existing registered protocols :param comp: :type comp: an input object as DynamicProtocol :return: void """ self.comp = comp self.__logger = Logger() def check(self): """ Will trying to match the candidate Non Root API check IJSONResourceAPI for more info :return: False if not success otherwise the response from matched API.method() """ for api in subscribers([self.comp], IJSONResourceAPI): if api.__class__.__name__.lower() in self.comp.to_dict().keys(): self.__logger.debug('Candidate API {} for {}'.format( api.__class__.__name__, self.comp.__class__.__name__ )) candidate_api_name = self.comp.to_dict().get(api.__class__.__name__.lower()) try: # execute the candidate API method # and return the result candidate_api_result = getattr(api, candidate_api_name)() except AttributeError as e: msg = 'Candidate API {} for {} does not implement method {} error: {}' self.__logger.warning(msg.format( api.__class__.__name__, self.comp.__class__.__name__, candidate_api_name, e.message )) else: self.__logger.info('Successful apply API {} for {}'.format( api.__class__.__name__, self.comp.__class__.__name__ )) return candidate_api_result return False
def __init__(self): """ A constructor will init the logger only :return: void """ self.__logger = Logger()
class DispathcherResultHelper(object): """ A helper class that care about of the result and the response of the request """ def __init__(self, factory): """ Just settings :param factory: :return: void """ self.__logger = Logger() self.factory = factory @staticmethod def deferred_response(response, sender): """ Care about global defered response :param response: response from defer operation :param sender: TBA :return: void """ if sender is not None: sender(response.to_json()) def deferred_response_error(self, err): """ fired if global defered response fail :param err: :return: False """ self.__logger.error('Cannot send message to user: {}'.format( err )) return False def result_validation(self, sender=None, drop=None, section='TCP'): """ A Result Helper more info TBA :param sender: :param drop: :param section: :return: mixin """ if IValidator.providedBy(self.factory): self.__logger.warning('{} Message is invalid: {}'.format(section, self.factory.message)) if drop is not None: drop() else: return 'message is invalid' else: if self.factory: self.__logger.info('{} Response: {}'.format(section, self.factory)) if IJSONResource.providedBy(self.factory): if sender is not None: sender(self.factory.to_json()) else: return self.factory.to_json() elif isinstance(self.factory, Deferred): self.factory.addCallback(self.deferred_response, sender) self.factory.addErrback(self.deferred_response_error) else: self.__logger.warning('{}: This message was not be dispatched'.format(section)) drop()
def __init__(self, factory): super(Time, self).__init__(factory) self.factory = factory self.logger = Logger()
class WampDefaultComponent(ApplicationSession): """ Class that represent our default WAMP component we inherit from autobahn.twisted.wamp.ApplicationSession """ def __init__(self, **kwargs): """ The constructor will init self.cfg self.__gsm and if the protocol is wss, will attach on_connect and on_challenge to this object as methods :param kwargs: we looking for key 'config' :return: void """ config = kwargs.get('config') super(WampDefaultComponent, self).__init__(config) self.__logger = Logger() self.cfg = Config().get_wamp() self.__gsm = getGlobalSiteManager() if self.cfg.protocol == 'wss': self.__logger.info('WAMP is secure, switch to wss...') WampDefaultComponent.onConnect = on_connect WampDefaultComponent.onChallenge = on_challenge def subscriber_dispatcher(self, sub_data): """ Callback function for our global subscriber will pass sub_data to GlobalSubscribeMessage and then will trying to get wamp component which implements IUserGlobalSubscriber and adapts IGlobalSubscribeMessage :param sub_data: """ log = Logger() try: result = IDispatcher(Validator(sub_data)).dispatch() # NETODO what exception can happen here? except Exception as e: import traceback print traceback.format_exc() log.warning('subscriber_dispatcher exception: {}'.format( e.message)) else: if IValidator.providedBy(result): log.warning('WAMP Message is invalid: {}'.format( result.message)) if result is not False and IJSONResource.providedBy(result): fac = None for sub in subscribers([result], IUserGlobalSubscriber): sub.subscribe(self) fac = True break if not fac: log.warning( 'There are no user definition for IUserGlobalSubscriber, message was skipped' ) @inlineCallbacks def onJoin(self, details): """ Autobahn callback function that fired when onJoin happens (we join to our realm) Here all components recognized as WAMP we will registering on the fly. The global subscriber will be started too, its name is formed by netcatks_global_subscriber_ + WAMP Service name. Also inside our Storage register will be saved the current WAMP session object in __wamp_session__ namespace. If there an API which providing a IWAMPLoadOnRunTime implementation will be started on run time :param details: :return: """ self.__logger.info('WAMP Session is ready') sub_topic = 'netcatks_global_subscriber_{}'.format( self.cfg.service_name.lower().replace(' ', '_')) yield self.subscribe(self.subscriber_dispatcher, sub_topic) self.__logger.info('Starting global subscriber: {}'.format(sub_topic)) # registration of all classes which ends with Wamp into shared wamp session for x in list(self.__gsm.registeredSubscriptionAdapters()): # storing wamp session inside storage sr = createObject('storageregister').components sr['__wamp_session__'] = self if IWAMPResource in x.required: if x.factory.__name__ != 'BaseWampComponent': f = x.factory() self.__logger.info('Register Wamp Component: {}'.format( f.__class__.__name__)) yield self.register(f) # here we provide wamp session to each wamp component, # in this way every component can access methods like publish, call etc, # which are hosted by default inside wamp session. f.set_session(self) if x.provided is IWAMPLoadOnRunTime: x.factory('init').load() def onDisconnect(self): """ Will be fired when we go a disconnecting from a WAMP router :return: """ self.__logger.warning('Disconnected...') try: reconnect = Reconnect(session=WampDefaultComponent, runner=AutobahnDefaultFactory, config=self.cfg) reactor.callLater( self.cfg.retry_interval, reconnect.start, ) # NETODO what exception trow here? except Exception as e: self.__logger.warning('disconnect warn: {}'.format(e.message))
class WampDefaultComponent(ApplicationSession): """ Class that represent our default WAMP component we inherit from autobahn.twisted.wamp.ApplicationSession """ def __init__(self, **kwargs): """ The constructor will init self.cfg self.__gsm and if the protocol is wss, will attach on_connect and on_challenge to this object as methods :param kwargs: we looking for key 'config' :return: void """ config = kwargs.get('config') super(WampDefaultComponent, self).__init__(config) self.__logger = Logger() self.cfg = Config().get_wamp() self.__gsm = getGlobalSiteManager() if self.cfg.protocol == 'wss': self.__logger.info('WAMP is secure, switch to wss...') WampDefaultComponent.onConnect = on_connect WampDefaultComponent.onChallenge = on_challenge def subscriber_dispatcher(self, sub_data): """ Callback function for our global subscriber will pass sub_data to GlobalSubscribeMessage and then will trying to get wamp component which implements IUserGlobalSubscriber and adapts IGlobalSubscribeMessage :param sub_data: """ log = Logger() try: result = IDispatcher(Validator(sub_data)).dispatch() # NETODO what exception can happen here? except Exception as e: import traceback print traceback.format_exc() log.warning('subscriber_dispatcher exception: {}'.format( e.message )) else: if IValidator.providedBy(result): log.warning('WAMP Message is invalid: {}'.format(result.message)) if result is not False and IJSONResource.providedBy(result): fac = None for sub in subscribers([result], IUserGlobalSubscriber): sub.subscribe(self) fac = True break if not fac: log.warning('There are no user definition for IUserGlobalSubscriber, message was skipped') @inlineCallbacks def onJoin(self, details): """ Autobahn callback function that fired when onJoin happens (we join to our realm) Here all components recognized as WAMP we will registering on the fly. The global subscriber will be started too, its name is formed by netcatks_global_subscriber_ + WAMP Service name. Also inside our Storage register will be saved the current WAMP session object in __wamp_session__ namespace. If there an API which providing a IWAMPLoadOnRunTime implementation will be started on run time :param details: :return: """ self.__logger.info('WAMP Session is ready') sub_topic = 'netcatks_global_subscriber_{}'.format( self.cfg.service_name.lower().replace(' ', '_') ) yield self.subscribe(self.subscriber_dispatcher, sub_topic) self.__logger.info('Starting global subscriber: {}'.format(sub_topic)) # registration of all classes which ends with Wamp into shared wamp session for x in list(self.__gsm.registeredSubscriptionAdapters()): # storing wamp session inside storage sr = createObject('storageregister').components sr['__wamp_session__'] = self if IWAMPResource in x.required: if x.factory.__name__ != 'BaseWampComponent': f = x.factory() self.__logger.info('Register Wamp Component: {}'.format(f.__class__.__name__)) yield self.register(f) # here we provide wamp session to each wamp component, # in this way every component can access methods like publish, call etc, # which are hosted by default inside wamp session. f.set_session(self) if x.provided is IWAMPLoadOnRunTime: x.factory('init').load() def onDisconnect(self): """ Will be fired when we go a disconnecting from a WAMP router :return: """ self.__logger.warning('Disconnected...') try: reconnect = Reconnect( session=WampDefaultComponent, runner=AutobahnDefaultFactory, config=self.cfg ) reactor.callLater( self.cfg.retry_interval, reconnect.start, ) # NETODO what exception trow here? except Exception as e: self.__logger.warning('disconnect warn: {}'.format(e.message))
class AutobahnDefaultFactory(service.Service): """ This class is a convenience tool mainly for development and quick hosting of WAMP application components. It can host a WAMP application component in a WAMP-over-WebSocket client connecting to a WAMP router. """ def __init__(self, **kwargs): """ :param url: The WebSocket URL of the WAMP router to connect to (e.g. `ws://somehost.com:8090/somepath`) :type url: unicode :param realm: The WAMP realm to join the application session to. :type realm: unicode :param extra: Optional extra configuration to forward to the application component. :type extra: dict :param debug: Turn on low-level debugging. :type debug: bool :param debug_wamp: Turn on WAMP-level debugging. :type debug_wamp: bool :param debug_app: Turn on app-level debugging. :type debug_app: bool """ self.logger = Logger() self.config = kwargs.get('config', None) if self.config is None: self.config = {} wamp = WAMP() self.logger.warning('Config is not provided failback to defaults') self.config.update({ 'protocol': 'wss', 'hostname': 'localhost', 'port': 8080, # integer 'realm': 'realm1', 'path': 'ws', 'retry_interval': 2, # in seconds 'url': u'ws://localhost:8080/ws', 'service_name': 'A Default WAMP Service name"' }) self.config = wamp.to_object(self.config) self.url = kwargs.get('url', self.config.url) self.realm = u'{}'.format(kwargs.get('realm', self.config.realm)) self.extra = kwargs.get('extra', dict()) self.debug = kwargs.get('debug', False) self.debug_wamp = kwargs.get('debug_wamp', False) self.debug_app = kwargs.get('debug_app', False) self.belong_to = kwargs.get('belong_to', False) self.make = None self.protocol = kwargs.get('protocol', self.config.protocol) self.name = kwargs.get('name', self.config.service_name) self.port = kwargs.get('port', self.config.port) self.host = kwargs.get('host', self.config.hostname) self.path = kwargs.get('path', self.config.path) def run(self, make): """ Run the application component. :param make: A factory that produces instances of :class:`autobahn.asyncio.wamp.ApplicationSession` when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`. :type make: callable """ is_secure, host, port, resource, path, params = parse_url(self.url) # start logging to console if self.debug or self.debug_wamp or self.debug_app: pass # log.startLogging(sys.stdout) # factory for use ApplicationSession def create(): """ Will trying to create an ApplicationSession object :return: ApplicationSession """ cfg = ComponentConfig(self.realm, self.extra) try: session = make(config=cfg) except Exception as e: # the app component could not be created .. fatal self.logger.critical('CREATE RUNNER EXCEPTION {}'.format(e.message)) else: session.debug_app = self.debug_app return session # create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory( create, url=self.url ) if is_secure: endpoint_descriptor = "ssl:{0}:{1}".format(host, port) else: endpoint_descriptor = "tcp:{0}:{1}".format(host, port) try: self.logger.info('Trying to connect to: {}'.format(endpoint_descriptor)) self.connect(endpoint_descriptor, transport_factory, make) return self except Exception as e: self.logger.error('CLIENT CONNECT ERROR: {}'.format(e.message)) def connect(self, endpoint, transport, session): """ Will make a connection to a WAMP router based on a Twisted endpoint :param endpoint: :param transport: :param session: :return: """ try: # start the client from a Twisted endpoint client = clientFromString(reactor, endpoint) res = client.connect(transport) def connect_result(result): """ A callback used as defer result :param result: :return: """ return result def connect_error(result): """ A callback used as defer if error occur :param result: :return: """ self.logger.error('CONNECTION ERROR: {}'.format(result.getErrorMessage())) try: reconnect = Reconnect( session=session, runner=AutobahnDefaultFactory, config=self.config ) reactor.callLater( self.config.retry_interval, reconnect.start, ) except Exception as e: self.logger.error('RECONNECTING ERROR: {}'.format(e.message)) res.addCallback(connect_result) res.addErrback(connect_error) return res except Exception as e: self.logger.error('CONNECTION GLOBAL ERRORS: {}'.format(e.message)) def startService(self): """ Start a WAMP service :return: """ service.Service.startService(self) def stopService(self): """ Stop a WAMP service :return: """ service.Service.stopService(self)