Mini Shell
#!/opt/cloudlinux/venv/bin/python3 -sbb
# 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
# for details see LVEMAN-622: Create autorestore quota limits after 1.0-9.9 update
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from future.utils import iteritems
import glob
import gzip
import os
import sys
import shutil
import subprocess
import time
UPDATE_USER = "/scripts/modcpuser"
UPDATE_QUOTA = "/scripts/editquota"
QUOTA_CONF = "/etc/quota.conf"
USERS_DIR = "/var/cpanel/users"
PACKAGES_DIR = "/var/cpanel/packages"
PLAN_KEYS = ["HASCGI", "HASDKIM", "HASSPF", "QUOTA", "BWLIMIT", "MAXPOP",
"MAXFTP", "MAXLST", "MAXSQL", "MAXSUB", "MAXPARK", "MAXADDON",
"MAX_EMAIL_PER_HOUR", "MAX_DEFER_FAIL_PERCENTAGE"]
class Main(object):
"""
"""
_cached_quota = {}
_cached_users = {}
_cached_packages = {}
_packages = {}
_users = {}
def __init__(self, backup_varcpanel, backup_etcquota):
"""
Constructor
"""
self._backup_etcquota = backup_etcquota
self._backup_varcpanel = backup_varcpanel
self._load_quota_cache()
def run(self):
"""
Run main action
"""
# load packages data
self._load_packages()
# load users conf
self._load_users()
self._load_users_quota()
# load varcpanel cache for all users
self._load_varcpanel_cache()
# create backup user configs before restore
self._backup_users()
# check users differs
self._check_users()
def _load_quota_cache(self):
"""
Load cached quota settings
"""
# load backup files
f = gzip.open(self._backup_etcquota, "rb")
for line in f:
line = line.strip()
if not line:
continue
user, value = line.split("=")
self._cached_quota[user.strip()] = value.strip()
f.close()
def _load_varcpanel_cache(self):
"""
Load cache for users and packages
"""
cmd = ["/bin/tar -C /tmp -xf %s %s %s" % (self._backup_varcpanel,
USERS_DIR.lstrip("/"), PACKAGES_DIR.lstrip("/"))]
p = subprocess.Popen(cmd, shell=True, executable='/bin/bash',
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
if err:
print(err)
sys.exit(0)
# load users cache
users_cache_dir = "/tmp%s" % USERS_DIR
for cache_path in glob.glob("%s/*" % users_cache_dir):
data = self._read_file(cache_path)
username = cache_path.split("/")[-1]
self._cached_users[username] = data
# load packages cache
packages_cache_dir = "/tmp%s" % PACKAGES_DIR
for cache_path in glob.glob("%s/*" % packages_cache_dir):
if not os.path.isfile(cache_path):
# skip extensions directory
continue
data = self._read_file(cache_path)
if "CGI" in data:
data["CGI"] = {"y": "1", "n": "0"}.get(data["CGI"], data["CGI"])
username = cache_path.split("/")[-1]
self._cached_packages[username] = data
shutil.rmtree(users_cache_dir)
shutil.rmtree(packages_cache_dir)
def _load_packages(self):
"""
Load current packages configures
"""
for package_path in glob.glob("%s/*" % PACKAGES_DIR):
if not os.path.isfile(package_path):
continue
pack = self._read_file(package_path)
if "CGI" in pack:
pack["CGI"] = {"y": "1", "n": "0"}.get(pack["CGI"], pack["CGI"])
self._packages[package_path.split("/")[-1]] = pack
def _load_users(self):
"""
Load current users settings
"""
for user_path in glob.glob("%s/*" % USERS_DIR):
if not os.path.isfile(user_path):
continue
user = self._read_file(user_path)
self._users[user_path.split("/")[-1]] = user
def _load_users_quota(self):
"""
Load config file with all users quotas
"""
quota = self._read_file(QUOTA_CONF)
for user, value in iteritems(quota):
if user in self._users:
self._users[user]["QUOTA"] = value
def _check_users(self):
"""
Check users settings
"""
for username, conf in iteritems(self._users):
if not self._is_need_restore_settings(username, conf):
# skip if different values
continue
# restore only users with package values
print("Restore %s" % username)
# reset to backup quota settings
if username in self._cached_quota \
and conf["QUOTA"] != self._cached_quota[username]:
# reset quota config
params = [UPDATE_QUOTA, username, "%sM" % self._cached_quota[username]]
p = subprocess.Popen(params, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# print params
out, err = p.communicate()
if err:
print("UPDATE_QUOTA error:")
print(err)
# skip users without cache
if username not in self._cached_users:
print(" %s has not cached data - skip" % username)
continue
# skip users with plan differ from cached
if self._cached_users[username]["PLAN"] != conf["PLAN"]:
print(" %s changed plan from '%s' to '%s' - skip" % \
(username, self._cached_users[username]["PLAN"], conf["PLAN"]))
continue
# reset to backup limits settings
for key in PLAN_KEYS:
if key in self._cached_users[username] \
and conf[key] != self._cached_users[username][key]:
# check difference between old package value and current package value
if conf["PLAN"] in self._cached_packages and conf["PLAN"] in self._packages \
and self._cached_packages[conf["PLAN"]].get(key) \
!= self._packages[conf["PLAN"]].get(key):
print("Package value '%s' was changed - skip" % key)
continue
params = [UPDATE_USER, "--user", username, "--action", "set",
"--key", key, "--value", self._cached_users[username][key]]
p = subprocess.Popen(params, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# print params
out, err = p.communicate()
if err:
print("UPDATE_USER error:")
print(err)
def _backup_users(self):
"""
Backup users configs files before restore
"""
ts = time.time()
# backup users directory
backup_dir = "%s.%s" % (USERS_DIR, ts)
shutil.copytree(USERS_DIR, backup_dir)
# backup quota.conf file
backup_file = "%s.%s" % (QUOTA_CONF, ts)
shutil.copy2(QUOTA_CONF, backup_file)
def _is_need_restore_settings(self, username, conf):
"""
Compare plan settings and users config
:return: bool. if true - need restore data, false - no
"""
if "default" == conf["PLAN"]:
# default plan has no settings, but has user personal settings
return True
if conf["PLAN"] not in self._packages:
# if user current plan no in packages - skip it
print(" %s plan no in current packages - skip" % username)
return False
COMPARE_KEYS = {"CGI": "HASCGI"}
for key, value in iteritems(self._packages[conf["PLAN"]]):
if key not in ["CGI", "DIGESTAUTH", "HASSHELL"] + PLAN_KEYS:
continue
conf_key = COMPARE_KEYS.get(key, key)
if conf_key in conf and conf[conf_key] != value:
return False
return True
def _read_file(self, filename):
"""
helper util
"""
result = {}
u = open(filename, "r")
for line in u:
line = line.strip()
if not line or line.startswith("#"):
continue
key, value = line.split("=")
result[key.strip()] = value.strip()
u.close()
return result
if "__main__" == __name__:
if len(sys.argv[1:]) < 2:
print("%s <backup_varcpanel> <backup_etcquota>" % sys.argv[0])
sys.exit(1)
Main(*sys.argv[1:]).run()
#./autorestore.py /backup/cpbackup/weekly/dirs/_var_cpanel.tar.gz /backup/cpbackup/weekly/files/_etc_quota.conf.gz
#./autorestore.py /tmp/var.tgz /backup/cpbackup/weekly/files/_etc_quota.conf.gz
Zerion Mini Shell 1.0