| Viewing file:  config.py (6.67 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
# coding=utf-8
 # Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved
 #
 # Licensed under CLOUD LINUX LICENSE AGREEMENT
 # http://cloudlinux.com/docs/LICENSE.TXT
 from __future__ import absolute_import
 from __future__ import print_function
 from __future__ import division
 import json
 import os
 from abc import ABCMeta, abstractmethod, abstractproperty
 from future.utils import iteritems
 from secureio import write_file_via_tempfile
 
 from clselect import utils
 from .pkgmanager import BasePkgManager  # NOQA
 from . import BaseSelectorError, ENABLED_STATUS, DISABLED_STATUS
 from future.utils import with_metaclass
 
 
 class BaseSelectorConfig(with_metaclass(ABCMeta, object)):
 """
 Base class that responsible for all interaction with CL selector config files
 """
 
 def __init__(self, pkg):
 self.Cfg = self._get_config_object()
 self.pkg = pkg  # type: BasePkgManager
 
 self.reload()
 
 @abstractproperty
 def _config_file(self):
 """Should return path to the config file"""
 raise NotImplementedError()
 
 @abstractmethod
 def _create_config_dirs(self):
 """Should create all needed directories for configs"""
 raise NotImplementedError()
 
 @staticmethod
 def _get_config_object():
 """Override this method to change config parameters"""
 # Useful for IDE-level auto-completion and type checking
 class Cfg:
 # Defaults. None values means that it's not specified in config yet
 # and effective values depends on some logic in class properties
 default_version = None
 selector_enabled = None
 disabled_versions = None
 
 return Cfg
 
 @property
 def is_config_exists(self):
 """Check whether config file exists and is a regular file"""
 return os.path.isfile(self._config_file)
 
 def _dump(self):
 """
 Returns underlying config as a plain dict. It will contain only
 explicitly configured options (e.g. no elements with None values)
 """
 tmp = {}
 for k, v in iteritems(self.Cfg.__dict__):
 if not k.startswith('__') and v is not None:
 tmp[k] = v
 return tmp
 
 def _reset_cfg(self):
 """
 Reset self.Cfg object to all None values before it will be loaded
 from file as a part of self.reload()
 """
 for k, v in iteritems(self.Cfg.__dict__):
 if not k.startswith('__'):
 setattr(self.Cfg, k, None)
 
 def reload(self):
 data = self._read_file_data()
 if not data:
 return  # No file or it's empty - nothing to load, use defaults
 
 try:
 tmp = json.loads(data)
 except (ValueError, TypeError) as e:
 raise BaseSelectorError('Unable to parse json from {} ; Error: {}'
 .format(self._config_file, e))
 
 self._reset_cfg()
 for k, v in iteritems(tmp):
 setattr(self.Cfg, k, v)
 
 def _read_file_data(self):
 """
 Should return:
 - whole file data for normal case
 - None if file doesn't exists
 - '' for empty file
 """
 if not self.is_config_exists:
 return None
 
 try:
 with open(self._config_file, 'rb') as fd:
 data = fd.read()
 except (IOError, OSError) as e:
 raise BaseSelectorError('Unable to read data from {} ; Error: {}'
 .format(self._config_file, e))
 return data
 
 def save(self):
 if not self.is_config_exists:
 self._create_config_dirs()
 
 data = utils.pretty_json(self._dump())
 return self._write_file_data(data)
 
 def _write_file_data(self, data):
 try:
 write_file_via_tempfile(
 content=data,
 dest_path=self._config_file,
 perm=0o644,
 suffix='_tmp',
 )
 except (IOError, OSError) as e:
 raise BaseSelectorError('Could not write system config ({})'.format(e))
 
 def _ensure_version_installed(self, version):
 if version not in self.pkg.installed_versions:
 raise BaseSelectorError('Version "{}" is not installed'
 .format(version))
 
 @property
 def selector_enabled(self):
 """Returns effective selector_enabled value"""
 if self.Cfg.selector_enabled is None:
 # Selector is disabled by default until explicitly enabled by admin
 return False
 return self.Cfg.selector_enabled and bool(self.pkg.installed_versions)
 
 @selector_enabled.setter
 def selector_enabled(self, value):
 if value and not self.pkg.installed_versions:
 raise BaseSelectorError(
 "It's not allowed to enable Selector when "
 "interpreter is not installed")
 self.Cfg.selector_enabled = value
 
 def get_default_version(self):
 # If unspecified - we still return None so Frontend can show this
 # somehow user-friendly
 return self.Cfg.default_version
 
 def set_default_version(self, version):
 if version is None:
 # We allow to reset to 'unspecified' state
 self.Cfg.default_version = None
 return
 
 if version in (self.Cfg.disabled_versions or []):
 raise BaseSelectorError(
 "It's not allowed to set disabled version as the default one")
 self._ensure_version_installed(version)
 self.Cfg.default_version = version
 
 def set_version_status(self, version, new_status):
 disabled_list = self.Cfg.disabled_versions
 if new_status == ENABLED_STATUS:
 
 if disabled_list is not None and version in disabled_list:
 disabled_list.remove(version)
 if len(disabled_list) == 0:
 self.Cfg.disabled_versions = None
 
 elif new_status == DISABLED_STATUS:
 
 if version == self.get_default_version():
 raise BaseSelectorError("It's not allowed to disable currently "
 "default version")
 
 # We explicitly allow to disable even not installed versions too
 # for future usage
 if disabled_list is None:
 self.Cfg.disabled_versions = [version]
 else:
 if version not in disabled_list:
 disabled_list.append(version)
 
 else:
 raise BaseSelectorError('Unknown version status: "{}"'
 .format(new_status))
 
 @abstractproperty
 def available_versions(self):
 raise NotImplementedError()
 
 
 |