class Database(object): filename = constants.get_gateway_database_file() _db = SqliteDatabase(filename, pragmas={'foreign_keys': 1}) # used to store database metrics (e.g. number of saves) _metrics = {} # type: Dict[str,int] @classmethod def get_db(cls): return cls._db @classmethod def incr_metrics(cls, sender, incr=1): cls._metrics.setdefault(sender, 0) cls._metrics[sender] += incr @classmethod def get_models(cls): models = set() for (class_name, class_member) in inspect.getmembers(sys.modules[__name__], inspect.isclass): if issubclass(class_member, BaseModel): models.add(class_member.__name__) return models @classmethod def get_metrics(cls): return cls._metrics
class Meta: database = SqliteDatabase(constants.get_gateway_database_file(), pragmas={'foreign_keys': 1})
def restore_full_backup(self, data): """ Restore a full backup containing the master eeprom and the sqlite databases. :param data: The backup to restore. :type data: Tar containing multiple files: master.eep, config.db, scheduled.db, power.db, eeprom_extensions.db, metrics.db and plugins as a string of bytes. :returns: dict with 'output' key. """ import glob import shutil import tempfile import subprocess tmp_dir = tempfile.mkdtemp() tmp_sqlite_dir = '{0}/sqlite'.format(tmp_dir) try: with open('{0}/backup.tar'.format(tmp_dir), 'wb') as backup_file: backup_file.write(data) retcode = subprocess.call( 'cd {0}; tar xf backup.tar'.format(tmp_dir), shell=True) if retcode != 0: raise Exception('The backup tar could not be extracted.') # Check if the sqlite db's are in a folder or not for backwards compatibility src_dir = tmp_sqlite_dir if os.path.isdir( tmp_sqlite_dir) else tmp_dir with open('{0}/master.eep'.format(src_dir), 'r') as eeprom_file: eeprom_content = eeprom_file.read() self.master_restore(eeprom_content) for filename, target in { 'config.db': constants.get_config_database_file(), 'users.db': constants.get_config_database_file(), 'power.db': constants.get_power_database_file(), 'eeprom_extensions.db': constants.get_eeprom_extension_database_file(), 'metrics.db': constants.get_metrics_database_file(), 'gateway.db': constants.get_gateway_database_file() }.items(): source = '{0}/{1}'.format(src_dir, filename) if os.path.exists(source): shutil.copyfile(source, target) # Restore the plugins if there are any backup_plugin_dir = '{0}/plugins'.format(tmp_dir) backup_plugin_content_dir = '{0}/content'.format(backup_plugin_dir) backup_plugin_config_files = '{0}/config/pi_*'.format( backup_plugin_dir) if os.path.isdir(backup_plugin_dir): plugin_dir = constants.get_plugin_dir() plugins = [ name for name in os.listdir(backup_plugin_content_dir) if os.path.isdir( os.path.join(backup_plugin_content_dir, name)) ] for plugin in plugins: dest_dir = '{0}{1}'.format(plugin_dir, plugin) if os.path.isdir(dest_dir): shutil.rmtree(dest_dir) shutil.copytree( '{0}/{1}/'.format(backup_plugin_content_dir, plugin), '{0}{1}'.format(plugin_dir, plugin)) config_files = constants.get_plugin_config_dir() for config_file in glob.glob(backup_plugin_config_files): shutil.copy(config_file, '{0}/'.format(config_files)) return {'output': 'Restore complete'} finally: shutil.rmtree(tmp_dir) # Restart the Cherrypy server after 1 second. Lets the current request terminate. threading.Timer(1, lambda: os._exit(0)).start()
def get_full_backup(self): """ Get a backup (tar) of the master eeprom, the sqlite databases and the plugins :returns: Tar containing multiple files: master.eep, config.db, scheduled.db, power.db, eeprom_extensions.db, metrics.db and plugins as a string of bytes. """ _ = self # Not static for consistency def backup_sqlite_db(input_db_path, backup_db_path): """ Backup an sqlite db provided the path to the db to backup and the backup db. """ # Connect to database connection = sqlite3.connect(input_db_path) cursor = connection.cursor() # Lock database before making a backup cursor.execute('begin immediate') # Make new backup file shutil.copyfile(input_db_path, backup_db_path) # Unlock database connection.rollback() tmp_dir = tempfile.mkdtemp() tmp_sqlite_dir = '{0}/sqlite'.format(tmp_dir) os.mkdir(tmp_sqlite_dir) try: with open('{0}/master.eep'.format(tmp_sqlite_dir), 'w') as eeprom_file: eeprom_file.write(self.get_master_backup()) for filename, source in { 'config.db': constants.get_config_database_file(), 'power.db': constants.get_power_database_file(), 'eeprom_extensions.db': constants.get_eeprom_extension_database_file(), 'metrics.db': constants.get_metrics_database_file(), 'gateway.db': constants.get_gateway_database_file() }.items(): if os.path.exists(source): target = '{0}/{1}'.format(tmp_sqlite_dir, filename) backup_sqlite_db(source, target) # Backup plugins tmp_plugin_dir = '{0}/{1}'.format(tmp_dir, 'plugins') tmp_plugin_content_dir = '{0}/{1}'.format(tmp_plugin_dir, 'content') tmp_plugin_config_dir = '{0}/{1}'.format(tmp_plugin_dir, 'config') os.mkdir(tmp_plugin_dir) os.mkdir(tmp_plugin_content_dir) os.mkdir(tmp_plugin_config_dir) plugin_dir = constants.get_plugin_dir() plugins = [ name for name in os.listdir(plugin_dir) if os.path.isdir(os.path.join(plugin_dir, name)) ] for plugin in plugins: shutil.copytree( plugin_dir + plugin, '{0}/{1}/'.format(tmp_plugin_content_dir, plugin)) config_files = constants.get_plugin_configfiles() for config_file in glob.glob(config_files): shutil.copy(config_file, '{0}/'.format(tmp_plugin_config_dir)) # Backup hex files tmp_hex_dir = '{0}/{1}'.format(tmp_dir, 'hex') os.mkdir(tmp_hex_dir) hex_files = constants.get_hex_files() for hex_file in glob.glob(hex_files): shutil.copy(hex_file, '{0}/'.format(tmp_hex_dir)) # Backup general config stuff tmp_config_dir = '{0}/{1}'.format(tmp_dir, 'config') os.mkdir(tmp_config_dir) config_dir = constants.get_config_dir() for file_name in ['openmotics.conf', 'https.key', 'https.crt']: shutil.copy(os.path.join(config_dir, file_name), '{0}/'.format(tmp_config_dir)) retcode = subprocess.call( 'cd {0}; tar cf backup.tar *'.format(tmp_dir), shell=True) if retcode != 0: raise Exception('The backup tar could not be created.') with open('{0}/backup.tar'.format(tmp_dir), 'r') as backup_file: return backup_file.read() finally: shutil.rmtree(tmp_dir)