class LogEvents(YomboLibrary): """ Manages all notifications. """ def _init_(self): """ Setups up the basic framework. """ # We only cache the last few events, and only for certain time. self.notifications = ExpiringDict(max_len=100, max_age_seconds=600) # return self.init_deferred def _load_(self): self._LocalDB = self._Libraries['localdb'] self._checkExpiredLoop = LoopingCall(self.check_expired) self._checkExpiredLoop.start(self._Configs.get('notifications', 'check_expired', 30, False)) self.load_notifications() def _stop_(self): if self.init_deferred is not None and self.init_deferred.called is False: self.init_deferred.callback(1) # if we don't check for this, we can't stop! def _clear_(self): """ Clear all devices. Should only be called by the loader module during a reconfiguration event. B{Do not call this function!} """ self.notifications.clear() def _reload_(self): self._clear_() self.load_notifications() def check_expired(self): """ Called by looping call to periodically purge expired notifications. :return: """ cur_time = int(time()) for id, notice in self.notifications.iteritems(): print "cur : expired = %s : %s" % (cur_time, notice.expire) if cur_time > notice.expire: print "deleting notice: %s" % notice.title del self.notifications[id] self._LocalDB.delete_expired_notifications() def get(self, notification_requested): """ Performs the actual search. .. note:: Modules shouldn't use this function. Use the built in reference to find notification: `self._Notifications['8w3h4sa']` :raises YomboWarning: Raised when notifcation cannot be found. :param notification_requested: The input type ID or input type label to search for. :type notification_requested: string :return: A dict containing details about the notification :rtype: dict """ if notification_requested in self.notifications: return self.notifications[notification_requested] else: raise YomboWarning('Notification not found: %s' % notification_requested) def delete(self, notification_requested): """ Deletes a provided notification. :param notification_requested: :return: """ try: del self.notifications[notification_requested] except: pass self._LocalDB.delete_notification(notification_requested) @inlineCallbacks def load_notifications(self): """ Load the last few notifications into memory. """ notifications = yield self._LocalDB.get_notifications() for notice in notifications: notice = notice.__dict__ if notice['expire'] < int(time()): continue notice['meta'] = json.loads(notice['meta']) self.add_notice(notice, from_db=True) logger.debug("Done load_notifications: {notifications}", notifications=self.notifications) # self.init_deferred.callback(10) def add_notice(self, notice, from_db=False, persist=True, create_event=False): """ Add a new notice. :param notice: A dictionary containing notification details. :type record: dict :returns: Pointer to new notice. Only used during unittest """ print "adding notice1: %s" % notice if 'id' not in notice: notice['id'] = random_string(length=16) if 'type' not in notice: notice['type'] = 'system' if 'priority' not in notice: notice['priority'] = 'normal' if 'source' not in notice: notice['source'] = '' if 'expire' not in notice: if 'timeout' in notice: notice['expire'] = int(time()) + notice['timeout'] else: notice['expire'] = int(time()) + 3600 else: if notice['expire'] > int(time()): YomboWarning("New notification is set to expire before current time.") if 'created' not in notice: notice['created'] = int(time()) if 'acknowledged' not in notice: notice['acknowledged'] = False else: if notice['acknowledged'] not in (True, False): YomboWarning("New notification 'acknowledged' must be either True or False.") if 'title' not in notice: raise YomboWarning("New notification requires a title.") if 'message' not in notice: raise YomboWarning("New notification requires a message.") if 'meta' not in notice: notice['meta'] = {} logger.debug("notice: {notice}", notice=notice) if from_db is False: self._LocalDB.add_notification(notice) self.notifications.prepend(notice['id'], Notification(notice)) else: self.notifications[notice['id']] = Notification(notice) # self.notifications = OrderedDict(sorted(self.notifications.iteritems(), key=lambda x: x[1]['created'])) pass return notice['id']
class YomboAPI(YomboLibrary): contentType = None def _init_(self): self.custom_agent = Agent(reactor, connectTimeout=20) self.contentType = self._Configs.get('yomboapi', 'contenttype', 'application/json', False) # TODO: Msgpack later self.base_url = self._Configs.get('yomboapi', 'baseurl', "https://api.yombo.net/api", False) self.allow_system_session = self._Configs.get('yomboapi', 'allow_system_session', True) self.init_defer = None self.api_key = self._Configs.get('yomboapi', 'api_key', 'aBMKp5QcQoW43ipauw88R0PT2AohcE', False) self.valid_system_session = None self.session_validation_cache = ExpiringDict() if self.allow_system_session: self.system_session = self._Configs.get('yomboapi', 'auth_session') # to be encrypted with gpg later self.system_login_key = self._Configs.get('yomboapi', 'login_key') # to be encrypted with gpg later else: self.system_session = None self.system_login_key = None def _load_(self): if self._Atoms['loader.operation_mode'] == 'run': self.init_defer = Deferred() self.validate_system_login() return self.init_defer def _start_(self): # print "system_session status: %s" % self.system_session # print "system_login_key status: %s" % self.system_login_key pass def _stop_(self): pass def _unload_(self): pass @inlineCallbacks def gateway_index(self, session=None): results = yield self.request("GET", "/v1/gateway", None, session) if results['code'] == 200: returnValue(results) elif results['code'] == 404: raise YomboWarning("Server cannot get gateways") else: if results['content']['message'] == "Invalid Token.": raise YomboWarningCredentails("URI: '%s' requires credentials." % results['content']['response']['uri']) raise YomboWarning("Unknown error: %s" % results['content']) @inlineCallbacks def gateway_get(self, gateway_id, session=None): results = yield self.request("GET", "/v1/gateway/%s" % gateway_id, None, session) if results['code'] == 200: returnValue(results) elif results['code'] == 404: raise YomboWarning("Server cannot find requested gateway: %s" % gateway_id) else: raise YomboWarning("Unknown error: %s" % results['content']['message']) @inlineCallbacks def gateway_put(self, gateway_id, values, session=None): results = yield self.request("PATCH", "/v1/gateway/%s" % gateway_id, values, session) if results['code'] == 200: returnValue(results) elif results['code'] == 404: raise YomboWarning("Server cannot find requested gateway: %s" % gateway_id) else: raise YomboWarning("Unknown error: %s" % results['content']['message']) @inlineCallbacks def gateway__module_get(self, gateway_id, session=None): results = yield self.request("GET", "/v1/gateway/%s/modules" % gateway_id, None, session) if results['code'] == 200: returnValue(results) elif results['code'] == 404: raise YomboWarning("Server cannot find requested gateway: %s" % gateway_id) else: raise YomboWarning("Unknown error: %s" % results['content']['message']) @inlineCallbacks def gateway__module_put(self, gateway_id, values, session=None): results = yield self.request("PATCH", "/v1/gateway/%s/modules" % gateway_id, values, session) if results['code'] == 200: returnValue(results) elif results['code'] == 404: raise YomboWarning("Server cannot find requested gateway: %s" % gateway_id) else: raise YomboWarning("Unknown error: %s" % results['content']['message']) @inlineCallbacks def gateway_config_index(self, gateway_id, session=None): results = yield self.request("GET", "/v1/gateway/%s/config" % gateway_id, None, session) if results['code'] == 200: returnValue(results) elif results['code'] == 404: raise YomboWarning("Server cannot get gateways") else: raise YomboWarning("Unknown error: %s" % results['content']['message']) # Below are the core help functions def save_system_session(self, session): print "api save_system_session0: %s" % session self.system_session = session print "api save_system_session1: %s" % session self._Configs.set('yomboapi', 'auth_session', session) # to be encrypted with gpg later print "api save_system_session2: %s" % session def save_system_login_key(self, login_key): print "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@api save_system_login_key: %s" % login_key self.system_login_key = login_key print "api save_system_login_key1: %s" % login_key self._Configs.set('yomboapi', 'login_key', login_key) # to be encrypted with gpg later print "api save_system_login_key2: %s" % login_key def select_session(self, session_id=None, session_key=None): if session_id is None or session_key is None: if self.allow_system_session: return self.system_session, self.system_login_key logger.info("select_session: Yombo API has no session data for 'selection_session'") return None, None def clear_session_cache(self, session=None): if (session is None): self.session_validation_cache.clear() else: hashed = sha1(session) if hashed in self.session_validation_cache: del self.session_validation_cache[hashed] # None works too... @inlineCallbacks def validate_system_login(self): """ Validates a system session if it exists. If not, it tries the login_key and creates a new session. :return: """ if self.allow_system_session is False: self._States.set('yomboapi.valid_system_session', False) self.init_defer.callback(10) returnValue(False) if self.system_session is None and self.system_login_key is None: print "validate_system_login: self.system_session: %s" % self.system_session print "validate_system_login: self.system_login_key: %s" % self.system_login_key logger.warn("No saved system session information and no login_key. Disabling automated system changes.") self._States.set('yomboapi.valid_system_session', False) self.valid_system_session = False if self.init_defer is not None: self.init_defer.callback(10) returnValue(None) self.clear_session_cache() if self.system_session is not None: results = yield self.do_validate_session(self.system_session) if (results is True): print "has a system session!" self._States.set('yomboapi.valid_system_session', True) self.valid_system_session = True self.init_defer.callback(10) returnValue(True) if self.system_login_key is not None: results = yield self.user_login_with_key(self.system_login_key) print "reslts: %s" % results if (results is not False): print "has a system login key!" self._Configs.set('yomboapi', 'auth_session', results['session']) # to be encrypted with gpg later self.system_session = results['session'] self._States.set('yomboapi.valid_system_session', True) self.valid_system_session = True self.init_defer.callback(10) returnValue(True) print "API system has some data, but it's invalid!" self._States.set('yomboapi.valid_system_session', False) self.valid_system_session = False self.init_defer.callback(10) returnValue(False) @inlineCallbacks def validate_session(self, session_id=None, session_key=None, clear_cache=False): session_id, session_key = self.select_session(session_id, session_key) if session_id is None or session_key is None: logger.debug("Yombo API session information is not valid: {id}:{key}", id=session_id, key=session_key) hashed = sha1(session_id + session_key) if hashed in self.session_validation_cache: if clear_cache is True: del self.session_validation_cache[hashed] else: returnValue(self.session_validation_cache[hashed]) results = yield self.do_validate_session(session_id, session_key) self.session_validation_cache[hashed] = results returnValue(results) @inlineCallbacks def do_validate_session(self, session): try: results = yield self.request("GET", "/v1/user/session/validate", None, session) except Exception, e: logger.debug("$$$1 API Errror: {error}", error=e) returnValue(False) logger.debug("$$$a REsults from API: {results}", results=results['content']) # waiting on final API.yombo.com to complete this. If we get something, we are good for now. if (results['content']['code'] != 200): returnValue(False) elif (results['content']['response']['session_status'] == 'valid'): returnValue(True) else: returnValue(False)
class LogEvents(YomboLibrary): """ Manages all notifications. """ def _init_(self): """ Setups up the basic framework. """ # We only cache the last few events, and only for certain time. self.notifications = ExpiringDict(max_len=100, max_age_seconds=600) # return self.init_deferred def _load_(self): self._LocalDB = self._Libraries['localdb'] self._checkExpiredLoop = LoopingCall(self.check_expired) self._checkExpiredLoop.start( self._Configs.get('notifications', 'check_expired', 30, False)) self.load_notifications() def _stop_(self): if self.init_deferred is not None and self.init_deferred.called is False: self.init_deferred.callback( 1) # if we don't check for this, we can't stop! def _clear_(self): """ Clear all devices. Should only be called by the loader module during a reconfiguration event. B{Do not call this function!} """ self.notifications.clear() def _reload_(self): self._clear_() self.load_notifications() def check_expired(self): """ Called by looping call to periodically purge expired notifications. :return: """ cur_time = int(time()) for id, notice in self.notifications.items(): print("cur : expired = %s : %s" % (cur_time, notice.expire)) if cur_time > notice.expire: print("deleting notice: %s" % notice.title) del self.notifications[id] self._LocalDB.delete_expired_notifications() def get(self, notification_requested): """ Performs the actual search. .. note:: Modules shouldn't use this function. Use the built in reference to find notification: `self._Notifications['8w3h4sa']` :raises YomboWarning: Raised when notifcation cannot be found. :param notification_requested: The input type ID or input type label to search for. :type notification_requested: string :return: A dict containing details about the notification :rtype: dict """ if notification_requested in self.notifications: return self.notifications[notification_requested] else: raise YomboWarning('Notification not found: %s' % notification_requested) def delete(self, notification_requested): """ Deletes a provided notification. :param notification_requested: :return: """ try: del self.notifications[notification_requested] except: pass self._LocalDB.delete_notification(notification_requested) @inlineCallbacks def load_notifications(self): """ Load the last few notifications into memory. """ notifications = yield self._LocalDB.get_notifications() for notice in notifications: notice = notice.__dict__ if notice['expire'] < int(time()): continue notice['meta'] = json.loads(notice['meta']) self.add_notice(notice, from_db=True) logger.debug("Done load_notifications: {notifications}", notifications=self.notifications) # self.init_deferred.callback(10) def add_notice(self, notice, from_db=False, persist=True, create_event=False): """ Add a new notice. :param notice: A dictionary containing notification details. :type record: dict :returns: Pointer to new notice. Only used during unittest """ print("adding notice1: %s" % notice) if 'id' not in notice: notice['id'] = random_string(length=16) if 'type' not in notice: notice['type'] = 'system' if 'priority' not in notice: notice['priority'] = 'normal' if 'source' not in notice: notice['source'] = '' if 'expire' not in notice: if 'timeout' in notice: notice['expire'] = int(time()) + notice['timeout'] else: notice['expire'] = int(time()) + 3600 else: if notice['expire'] > int(time()): YomboWarning( "New notification is set to expire before current time.") if 'created_at' not in notice: notice['created_at'] = int(time()) if 'acknowledged' not in notice: notice['acknowledged'] = False else: if notice['acknowledged'] not in (True, False): YomboWarning( "New notification 'acknowledged' must be either True or False." ) if 'title' not in notice: raise YomboWarning("New notification requires a title.") if 'message' not in notice: raise YomboWarning("New notification requires a message.") if 'meta' not in notice: notice['meta'] = {} logger.debug("notice: {notice}", notice=notice) if from_db is False: self._LocalDB.add_notification(notice) self.notifications.prepend(notice['id'], Notification(notice)) else: self.notifications[notice['id']] = Notification(notice) # self.notifications = OrderedDict(sorted(self.notifications.items(), key=lambda x: x[1]['created_at'])) pass return notice['id']