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()
|