diff --git a/plexpy/config.py b/plexpy/config.py index 55545d0c..5ae95408 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -190,6 +190,56 @@ _CONFIG_DEFINITIONS = { _BLACKLIST_KEYS = ['_APITOKEN', '_TOKEN', '_KEY', '_SECRET', '_PASSWORD', '_APIKEY', '_ID', '_HOOK'] _WHITELIST_KEYS = ['HTTPS_KEY'] +_DO_NOT_IMPORT_KEYS = [ + 'FIRST_RUN_COMPLETE', 'GET_FILE_SIZES_HOLD', 'GIT_PATH', + 'BACKUP_DIR', 'CACHE_DIR', 'LOG_DIR', 'NEWSLETTER_DIR', 'NEWSLETTER_CUSTOM_DIR', + 'HTTP_HOST', 'HTTP_PORT', 'HTTP_ROOT', + 'HTTP_USERNAME', 'HTTP_PASSWORD', 'HTTP_HASH_PASSWORD', 'HTTP_HASHED_PASSWORD', + 'ENABLE_HTTPS', 'HTTPS_CREATE_CERT', 'HTTPS_CERT', 'HTTPS_CERT_CHAIN', 'HTTPS_KEY' +] +_DO_NOT_IMPORT_KEYS_DOCKER = [ + 'PLEXPY_AUTO_UPDATE', 'GIT_REMOTE', 'GIT_BRANCH' +] + +IS_IMPORTING = False + + +def set_is_importing(value): + global IS_IMPORTING + IS_IMPORTING = value + + +def import_tautulli_config(config=None, backup=False): + if backup: + # Make a backup of the current config first + logger.info("Tautulli Config :: Creating a config backup before importing.") + if not make_backup(): + logger.error("Tautulli Config :: Failed to import Tautulli config: failed to create config backup") + return False + + logger.info("Tautulli Config :: Importing Tautulli config '%s'...", config) + set_is_importing(True) + + # Create a new Config object with the imported config file + imported_config = Config(config, is_import=True) + + # Remove keys that should not be imported + for key in _DO_NOT_IMPORT_KEYS: + delattr(imported_config, key) + if plexpy.DOCKER: + for key in _DO_NOT_IMPORT_KEYS_DOCKER: + delattr(imported_config, key) + + # Merge the imported config file into the current config file + plexpy.CONFIG._config.merge(imported_config._config) + plexpy.CONFIG.write() + + logger.info("Tautulli Config :: Tautulli config import complete.") + set_is_importing(False) + + # Restart to apply changes + plexpy.SIGNAL = 'restart' + def make_backup(cleanup=False, scheduler=False): """ Makes a backup of config file, removes all but the last 5 backups """ @@ -233,14 +283,15 @@ def make_backup(cleanup=False, scheduler=False): class Config(object): """ Wraps access to particular values in a config file """ - def __init__(self, config_file): + def __init__(self, config_file, is_import=False): """ Initialize the config with values from a file """ self._config_file = config_file self._config = ConfigObj(self._config_file, encoding='utf-8') for key in _CONFIG_DEFINITIONS: self.check_setting(key) - self._upgrade() - self._blacklist() + if not is_import: + self._upgrade() + self._blacklist() def _blacklist(self): """ Add tokens and passwords to blacklisted words in logger """ @@ -337,6 +388,16 @@ class Config(object): self._config[section][ini_key] = definition_type(value) return self._config[section][ini_key] + def __delattr__(self, name): + """ + Deletes a key from the configuration object. + """ + if not re.match(r'[A-Z_]+$', name): + return super(Config, self).__delattr__(name) + else: + key, definition_type, section, ini_key, default = self._define(name) + del self._config[section][ini_key] + def process_kwargs(self, kwargs): """ Given a big bunch of key value pairs, apply them to the ini. diff --git a/plexpy/webserve.py b/plexpy/webserve.py index fc8e02b1..08cf53cd 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -3832,6 +3832,57 @@ class WebInterface(object): else: return {'result': 'error', 'message': 'App not recognized for import'} + @cherrypy.expose + @cherrypy.tools.json_out() + @requireAuth(member_of("admin")) + @addtoapi() + def import_config(self, config_file=None, config_path=None, backup=False, **kwargs): + """ Import a Tautulli config file. + + ``` + Required parameters: + config_file (file): The config file to import (multipart/form-data) + or + config_path (str): The full path to the config file to import + + + Optional parameters: + backup (bool): true or false whether to backup + the current config before importing + + Returns: + json: + {"result": "success", + "message": "Config import has started. Check the logs to monitor any problems. " + "Tautulli will restart automatically." + } + ``` + """ + if database.IS_IMPORTING: + return {'result': 'error', + 'message': 'Database import is in progress. Please wait until it is finished to import a config.'} + + if config_file: + config_path = os.path.join(plexpy.CONFIG.CACHE_DIR, config_file.filename + '.import.ini') + logger.info("Received config file '%s' for import. Saving to cache '%s'.", + config_file.filename, config_path) + with open(config_path, 'wb') as f: + while True: + data = config_file.file.read(8192) + if not data: + break + f.write(data) + + if not config_path: + return {'result': 'error', 'message': 'No config specified for import'} + + threading.Thread(target=config.import_tautulli_config, + kwargs={'config': config_path, + 'backup': helpers.bool_true(backup)}).start() + return {'result': 'success', + 'message': 'Config import has started. Check the logs to monitor any problems. ' + 'Tautulli will restart automatically.'} + @cherrypy.expose @requireAuth(member_of("admin")) def import_database_tool(self, app=None, **kwargs):