def _modules_loaded_(self, **kwargs): """ Called after _load_ is called for all the modules. Get's a list of configuration items all library or modules define or use. Note: This complies with i18n translations for future use. **Hooks called**: * _configuration_details_ : Gets various details about a configuration item. Do not implement, not set in stone. Might migrate to i18n library. **Usage**: .. code-block:: python def _configuration_details_(self, **kwargs): return [{"webinterface": { "enabled": { "description": { "en": "Enables/disables the web interface.", }, "encrypt": True }, "port": { "description": { "en": "Port number for the web interface to listen on." } } }, }] """ config_details = yield global_invoke_all("_configuration_details_", called_by=self) for component, details in config_details.items(): if details is None: continue for list in details: # logger.warn("For module {component}, adding details: {list}", component=component, list=list) self.configs_details = dict_merge(self.configs_details, list) for section, options in self.configs.items(): for option, keys in options.items(): try: self.configs[section][option][ "details"] = self.configs_details[section][option] except: pass
def set(self, section, option, value, ignore_case=None, **kwargs): """ Set value of configuration option for a given section. The option length **cannot exceed 1000 characters**. The value cannot exceed 5000 bytes. Section and option will be converted to lowercase, rending the set/get function case insenstive. **Usage**: .. code-block:: python gatewayUUID = self._Config.set("section_name" , "myoption", "New Value") :raises YomboInvalidArgument: When an argument is invalid or illegal. :raises KeyError: When the requested section and option are not found. :param section: The configuration section to use. :type section: string :param option: The option (key) to use. :type option: string :param value: What to return if no result is found, default = None. :type value: int or string """ if len(section) > self.MAX_SECTION_LENGTH: self._Statistics.increment("lib.configuration.set.invalid_length", bucket_size=15, anon=True) raise YomboInvalidArgument( f"section cannot be more than {self.MAX_OPTION_LENGTH:d} chars" ) if len(option) > self.MAX_OPTION_LENGTH: self._Statistics.increment("lib.configuration.set.invalid_length", bucket_size=15, anon=True) raise YomboInvalidArgument( f"option cannot be more than {self.MAX_OPTION_LENGTH:d} chars") # Can't set value! if section == "yombo": self._Statistics.increment( "lib.configuration.set.no_setting_yombo", bucket_size=15, anon=True) raise YomboInvalidArgument("Not allowed to set value") if isinstance(value, str): if len(value) > self.MAX_VALUE_LENGTH: self._Statistics.increment( "lib.configuration.set.value_too_long", bucket_size=15, anon=True) raise YomboInvalidArgument( f"value cannot be more than {self.MAX_VALUE:d} chars") if ignore_case is not True: section = section.lower() option = option.lower() if section not in self.configs: self.configs[section] = {} if option not in self.configs[section]: self.configs[section][option] = { "created_at": int(time()), "reads": 0, "writes": 0, } self._Statistics.increment("lib.configuration.set.new", bucket_size=15, anon=True) else: # already have a value. If it's the same, we won't set it. if self.configs[section][option]["value"] == value: self._Statistics.increment( "lib.configuration.set.skipped_same_value", bucket_size=15, anon=True) return value self._Statistics.increment("lib.configuration.set.update", bucket_size=15, anon=True) self.configs[section][option] = dict_merge( self.configs[section][option], { "updated_at": int(time()), "value": value, "hash": sha224(str(value).encode("utf-8")).hexdigest(), }) self.configs_dirty = True if self.loading_yombo_ini is False: self.configs[section][option]["writes"] += 1 yield global_invoke_all( "_configuration_set_", called_by=self, section=section, option=option, value=value, ) return value
def set(self, section, option, value, **kwargs): """ Set value of configuration option for a given section. The option length **cannot exceed 1000 characters**. The value cannot exceed 5000 bytes. Section and option will be converted to lowercase, rending the set/get function case insenstive. **Usage**: .. code-block:: python gatewayUUID = self._Config.set("section_name", "myoption", "New Value") :raises InvalidArgumentError: When an argument is invalid or illegal. :raises KeyError: When the requested section and option are not found. :param section: The configuration section to use. :type section: string :param option: The option (key) to use. :type option: string :param value: What to return if no result is found, default = None. :type value: int or string """ # print("cfgs set: %s:%s = %s" % (section, option, value)) if len(section) > self.MAX_SECTION_LENGTH: self._Statistics.increment("lib.configuration.set.invalid_length", bucket_size=15, anon=True) raise InvalidArgumentError("section cannot be more than %d chars" % self.MAX_OPTION_LENGTH) if len(option) > self.MAX_OPTION_LENGTH: self._Statistics.increment("lib.configuration.set.invalid_length", bucket_size=15, anon=True) raise InvalidArgumentError("option cannot be more than %d chars" % self.MAX_OPTION_LENGTH) # Can't set value! if section == 'yombo': self._Statistics.increment( "lib.configuration.set.no_setting_yombo", bucket_size=15, anon=True) raise InvalidArgumentError("Not allowed to set value") if isinstance(value, str): if len(value) > self.MAX_VALUE_LENGTH: self._Statistics.increment( "lib.configuration.set.value_too_long", bucket_size=15, anon=True) raise InvalidArgumentError( "value cannot be more than %d chars" % self.MAX_VALUE) section = section.lower() option = option.lower() if section not in self.configs: self.configs[section] = {} if option not in self.configs[section]: self.configs[section][option] = { 'created_at': int(time()), 'reads': 0, 'writes': 0, } self._Statistics.increment("lib.configuration.set.new", bucket_size=15, anon=True) else: # already have a value. If it's the same, we won't set it. if self.configs[section][option]['value'] == value: self._Statistics.increment( "lib.configuration.set.skipped_same_value", bucket_size=15, anon=True) return self._Statistics.increment("lib.configuration.set.update", bucket_size=15, anon=True) self.configs[section][option] = dict_merge( self.configs[section][option], { 'updated_at': int(time()), 'value': value, 'hash': hashlib.sha224(str(value).encode('utf-8')).hexdigest(), }) self.configs_dirty = True if self.loading_yombo_ini is False: self.configs[section][option]['writes'] += 1 yield global_invoke_all('_configuration_set_', called_by=self, **{ 'section': section, 'option': option, 'value': value, 'action': 'set' })
def _init_(self, **kwargs): """ Open the yombo.ini file for reading. Import the configuration items into the database, also prime the configs for reading. """ self.exit_config_file = None # Holds a complete configuration file to save when exiting. self.cache_dirty = False self.configs = {} # Holds actual config data self.cfg_loaded = False self.yombo_ini_last_modified = 0 self.working_dir = settings.arguments["working_dir"] ini_norestore = settings.arguments["norestoreini"] self.yombo_ini_path = f"{self.working_dir}/yombo.ini" if os.path.exists(self.yombo_ini_path): if os.path.isfile(self.yombo_ini_path) is False: try: os.remove(self.yombo_ini_path) except Exception as e: logger.error( "'yombo.ini' file exists, but it's not a file and it can't be deleted!" ) reactor.stop() return if ini_norestore: self.restore_backup_yombi_ini() else: if os.path.getsize(self.yombo_ini_path) < 2: logger.warn( "yombo.ini appears corrupt, attempting to restore from backup." ) if ini_norestore: self.restore_backup_yombi_ini() else: if ini_norestore: self.restore_backup_yombi_ini() self.loading_yombo_ini = True if settings.yombo_ini is False: self._Loader.operating_mode = "first_run" else: for section, options in settings.yombo_ini.items(): for option, value in options.items(): try: value = yield self._GPG.decrypt(value) except: pass self.set(section, option, value, ignore_case=True) self.get("core", "rand_seed", random_string(length=128)) logger.debug( "done parsing yombo.ini. Now about to parse yombo.ini.info.") try: config_parser = configparser.ConfigParser() config_parser.read(f"{self.working_dir}/etc/yombo.ini.info") logger.debug("yombo.ini.info file read into memory.") for section in config_parser.sections(): if section not in self.configs: continue for option in config_parser.options(section): if option not in self.configs[section]: continue values = msgpack.loads( b64decode(config_parser.get(section, option))) self.configs[section][option] = dict_merge( self.configs[section][option], values) except IOError as e: logger.warn( "CAUGHT IOError!!!!!!!!!!!!!!!!!! In reading meta: {error}", error=e) except configparser.NoSectionError: logger.warn("CAUGHT ConfigParser.NoSectionError!!!! IN saving. ") logger.debug("done parsing yombo.ini.info") #setup some defaults if we are new.... self.get("core", "gwid", "local") self.get("core", "gwuuid", None) self.get("core", "is_master", 1) self.get("core", "master_gateway_id", "local") # Perform DB cleanup activites based on local section. if self.get("local", "deletedelayedmessages", False, False) is True: self._LocalDB.delete("sqldict", ["module = ?", "yombo.lib.messages"]) self.set("local", "deletedelayedmessages", False) if self.get("local", "deletedevicehistory", False, False) is True: self._LocalDB.truncate("devicestatus") self.set("local", "deletedevicehistory", False) current_time = int(time()) # Ask external services what they know about us. # detected_location states are based off this and is set in the locations library. # times uses this self.detected_location_info = self.get("core", "locationinfo", None, False) if self.detected_location_info is None or \ self.get("core", "locationinfotime", 0, False) < current_time - 3600: self.detected_location_info = yield detect_location_info() self.set( "core", "locationinfo", data_pickle(self.detected_location_info, encoder="msgpack_base64", local=True)) self.set("core", "locationinfotime", current_time) else: self.detected_location_info = data_unpickle( self.detected_location_info, encoder="msgpack_base64") self.set("core", "externalipaddress_v4", self.detected_location_info["ip"]) if self.get("core", "localipaddress_v4", False, False) is False or \ self.get("core", "localipaddresstime", False, False) is False: address_info = get_local_network_info() self.set("core", "localipaddress_v4", address_info["ipv4"]["address"]) self.set("core", "localipaddress_netmask_v4", address_info["ipv4"]["netmask"]) self.set("core", "localipaddress_cidr_v4", address_info["ipv4"]["cidr"]) self.set("core", "localipaddress_network_v4", address_info["ipv4"]["network"]) self.set("core", "localipaddress_v6", address_info["ipv6"]["address"]) self.set("core", "localipaddress_netmask_v6", address_info["ipv6"]["netmask"]) # self.set("core", "localipaddress_cidr_v6", address_info["ipv6"]["cidr"]) # self.set("core", "localipaddress_network_v6", address_info["ipv6"]["network"]) self.set("core", "localipaddresstime", int(time())) else: if int(self.configs["core"]["localipaddresstime"]["value"]) < ( int(time()) - 180): address_info = get_local_network_info() self.set("core", "localipaddress_v4", address_info["ipv4"]["address"]) self.set("core", "localipaddress_netmask_v4", address_info["ipv4"]["netmask"]) self.set("core", "localipaddress_cidr_v4", address_info["ipv4"]["cidr"]) self.set("core", "localipaddress_network_v4", address_info["ipv4"]["network"]) self.set("core", "localipaddress_v6", address_info["ipv6"]["address"]) self.set("core", "localipaddress_netmask_v6", address_info["ipv6"]["netmask"]) # self.set("core", "localipaddress_cidr_v6", address_info["ipv6"]["cidr"]) # self.set("core", "localipaddress_network_v6", address_info["ipv6"]["network"]) self.set("core", "localipaddresstime", int(time())) self.save_loop = LoopingCall(self.save) self.save_loop.start(randint(12600, 14400), False) # every 3.5-4 hours if self.get("core", "first_run", None, False) is None: self.set("core", "first_run", True) self.loading_yombo_ini = False # set system defaults. Reasons: 1) All in one place. 2) Somes values are needed before respective libraries # are loaded. self._Configs.get("mqtt", "client_enabled", True) self._Configs.get("mqtt", "server_enabled", True) self._Configs.get("mqtt", "server_max_connections", 1000) self._Configs.get("mqtt", "server_timeout_disconnect_delay", 2) self._Configs.get("mqtt", "server_listen_ip", "*") self._Configs.get("mqtt", "server_listen_port", 1883) self._Configs.get("mqtt", "server_listen_port_ss_ssl", 1884) self._Configs.get("mqtt", "server_listen_port_le_ssl", 1885) self._Configs.get("mqtt", "server_listen_port_websockets", 8081) self._Configs.get("mqtt", "server_listen_port_websockets_ss_ssl", 8444) self._Configs.get("mqtt", "server_listen_port_websockets_le_ssl", 8445) self._Configs.get("mqtt", "server_allow_anonymous", False) self._Configs.get("misc", "temperature_display", "f") self._Configs.get("misc", "length_display", "imperial") # will we ever get to metric? self.cfg_loaded = True
def _init_(self, **kwargs): """ Open the yombo.ini file for reading. Import the configuration items into the database, also prime the configs for reading. """ self.cache_dirty = False self.configs = {} self.automation_startup_check = [] self._loaded = False self.yombo_ini_last_modified = 0 ini_norestore = not os.path.exists('yombo.ini.norestore') if os.path.exists('yombo.ini'): if os.path.isfile('yombo.ini') is False: try: os.remove('yombo.ini') except Exception as e: logger.error( "'yombo.ini' file exists, but it's not a file and it can't be deleted!" ) reactor.stop() return if ini_norestore: self.restore_backup_yombi_ini() else: if os.path.getsize('yombo.ini') < 5: logger.warn( 'yombo.ini appears corrupt, attempting to restore from backup.' ) if ini_norestore: self.restore_backup_yombi_ini() else: if ini_norestore: self.restore_backup_yombi_ini() self.loading_yombo_ini = True self.last_yombo_ini_read = self.read_yombo_ini() if self.last_yombo_ini_read is False: self._Loader.operating_mode = 'first_run' logger.debug( "done parsing yombo.ini. Now about to parse yombo.ini.info.") try: config_parser = configparser.ConfigParser() config_parser.read('usr/etc/yombo.ini.info') logger.debug("yombo.ini.info file read into memory.") for section in config_parser.sections(): if section not in self.configs: continue for option in config_parser.options(section): if option not in self.configs[section]: continue values = msgpack.loads( b64decode(config_parser.get(section, option))) self.configs[section][option] = dict_merge( self.configs[section][option], values) except IOError as e: logger.warn( "CAUGHT IOError!!!!!!!!!!!!!!!!!! In reading meta: {error}", error=e) except configparser.NoSectionError: logger.warn("CAUGHT ConfigParser.NoSectionError!!!! IN saving. ") logger.debug("done parsing yombo.ini.info") #setup some defaults if we are new.... self.get('core', 'gwid', 'local') self.get('core', 'gwuuid', None) # Perform DB cleanup activites based on local section. if self.get('local', 'deletedelayedmessages', False, False) is True: self._Libraries['localdb'].delete( 'sqldict', ['module = ?', 'yombo.gateway.lib.messages']) self.set('local', 'deletedelayedmessages', False) if self.get('local', 'deletedevicehistory', False, False) is True: self._Libraries['localdb'].truncate('devicestatus') self.set('local', 'deletedevicehistory', False) if self.get('core', 'externalipaddress_v4', False, False) is False or self.get( 'core', 'externalipaddresstime', False, False) is False: ipv4_external = yield get_external_ip_address_v4() self.set("core", "externalipaddress_v4", ipv4_external) self.set("core", "externalipaddresstime", int(time())) else: if int(self.configs['core']['externalipaddresstime']['value']) < ( int(time()) - 3600): ipv4_external = yield get_external_ip_address_v4() self.set("core", "externalipaddress_v4", ipv4_external) self.set("core", "externalipaddresstime", int(time())) if self.get('core', 'localipaddress_v4', False, False) is False or self.get( 'core', 'localipaddresstime', False, False) is False: address_info = get_local_network_info() self.set("core", "localipaddress_v4", address_info['ipv4']['address']) self.set("core", "localipaddress_netmask_v4", address_info['ipv4']['netmask']) self.set("core", "localipaddress_cidr_v4", address_info['ipv4']['cidr']) self.set("core", "localipaddress_network_v4", address_info['ipv4']['network']) self.set("core", "localipaddress_v6", address_info['ipv6']['address']) self.set("core", "localipaddress_netmask_v6", address_info['ipv6']['netmask']) # self.set("core", "localipaddress_cidr_v6", address_info['ipv6']['cidr']) # self.set("core", "localipaddress_network_v6", address_info['ipv6']['network']) self.set("core", "localipaddresstime", int(time())) else: if int(self.configs['core']['localipaddresstime']['value']) < ( int(time()) - 180): address_info = get_local_network_info() self.set("core", "localipaddress_v4", address_info['ipv4']['address']) self.set("core", "localipaddress_netmask_v4", address_info['ipv4']['netmask']) self.set("core", "localipaddress_cidr_v4", address_info['ipv4']['cidr']) self.set("core", "localipaddress_network_v4", address_info['ipv4']['network']) self.set("core", "localipaddress_v6", address_info['ipv6']['address']) self.set("core", "localipaddress_netmask_v6", address_info['ipv6']['netmask']) # self.set("core", "localipaddress_cidr_v6", address_info['ipv6']['cidr']) # self.set("core", "localipaddress_network_v6", address_info['ipv6']['network']) self.set("core", "localipaddresstime", int(time())) self.periodic_save_yombo_ini = LoopingCall(self.save) self.periodic_save_yombo_ini.start(randint(12600, 14400), False) # every 3.5-4 hours # self.periodic_load_yombo_ini = LoopingCall(self.check_if_yombo_ini_modified) # self.periodic_load_yombo_ini.start(5) if self.get('core', 'first_run', None, False) is None: self.set('core', 'first_run', True) self.loading_yombo_ini = False # set system defaults. Reasons: 1) All in one place. 2) Somes values are needed before respective libraries # are loaded. self._Configs.get('mqtt', 'client_enabled', True) self._Configs.get('mqtt', 'server_enabled', True) self._Configs.get('mqtt', 'server_max_connections', 1000) self._Configs.get('mqtt', 'server_timeout_disconnect_delay', 2) self._Configs.get('mqtt', 'server_listen_ip', '*') self._Configs.get('mqtt', 'server_listen_port', 1883) self._Configs.get('mqtt', 'server_listen_port_ss_ssl', 1884) self._Configs.get('mqtt', 'server_listen_port_le_ssl', 1885) self._Configs.get('mqtt', 'server_listen_port_websockets', 8081) self._Configs.get('mqtt', 'server_listen_port_websockets_ss_ssl', 8444) self._Configs.get('mqtt', 'server_listen_port_websockets_le_ssl', 8445) self._Configs.get('mqtt', 'server_allow_anonymous', False) self._Configs.get('misc', 'temperature_display', 'f') self._Configs.get('misc', 'length_display', 'imperial') # will we ever get to metric?