File: //var/packages/CMS/scripts/CMSDelegate_dirsvs_group_delete.py
#!/usr/bin/env python3
import logging
import os
import sqlite3
import sys
import syslog
import traceback
from ctypes import CDLL, c_char_p
NAME = 'CMS'
VERSION = '1.0'
VENDOR = 'Synology Inc.'
SDK_MODVER = '1.0'
HOOK_NAME = 'dirsvs_group/delete'
# The 'SYNO.CMS' prefix helps us to log into CMS's own log file.
LOG_IDENT = 'SYNO.CMS.hook.{}'.format(HOOK_NAME)
CMS_DB_PATH = '/var/packages/CMS/var/db/cms_db.sqlite'
CMS_LIB_PATH = '/var/packages/CMS/target/lib/libcms.so'
syslog.openlog(LOG_IDENT)
logger = logging.getLogger('MyLogger')
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())
def log_info(msg: str):
new_msg = '[INFO] {}'.format(msg)
syslog.syslog(syslog.LOG_INFO, new_msg)
logger.info(new_msg)
def log_err(msg: str):
new_msg = '[ERR] {}'.format(msg)
syslog.syslog(syslog.LOG_ERR, new_msg)
logger.error(new_msg)
def log_err_n_raise(msg: str, base_exc: Exception):
log_err(msg)
raise Exception(msg) from base_exc
def log_progress(msg: str, task):
log_info(msg + ' [begin]')
try:
ret = task()
log_info(msg + ' [done]')
return ret
except Exception:
log_err(msg + ' [failed]')
raise
def print_help(bin_name: str):
print('Usage: {} --sdk-mod-ver|--name|--pkg-ver|--vendor|--pre|--post'
.format(bin_name))
def cmds():
return {
'--sdk-mod-ver': lambda: print(SDK_MODVER),
'--name': lambda: print(NAME),
'--pkg-ver': lambda: print(VERSION),
'--vendor': lambda: print(VENDOR),
'--pre': lambda: log_progress('Try running pre hook', pre_hook),
'--post': lambda: log_progress('Try running post hook', post_hook),
}
def main():
bin_name = sys.argv[0]
if len(sys.argv) < 2:
print_help(bin_name)
return
cmd = sys.argv[1]
cmds().get(cmd, lambda: print_help(bin_name))()
# Below is the interesting part of hook.
def pre_hook():
# Define your own pre hook here
pass
def post_hook():
try:
directory_server_type = log_progress(
'Try reading type of directory server from hook',
lambda: get_directory_server_type())
log_info('Directory server type from hook: {}'
.format(directory_server_type))
num_of_groups = log_progress(
'Try reading number of directory server groups from hook',
lambda: get_num_of_groups())
group_infos = log_progress(
'Try reading directory server group infos from hook',
lambda: get_group_infos(num_of_groups))
log_info('Directory server groups from hook: [{}]'.format(
', '.join(map(str, group_infos))))
log_progress(
'Try removing given directory server groups from CMS db',
lambda: remove_groups_of_directory_server_from_cms_db(
directory_server_type, group_infos))
except Exception as exc:
log_err_n_raise('Failed to run post hook', exc)
class GroupInfo:
def __init__(self, gid, name):
self.gid = gid
self.name = name
def __str__(self):
return 'GroupInfo{{gid={}, name={}}}'.format(self.gid, self.name)
def get_directory_server_type() -> str:
try:
return os.environ['SERVER_TYPE']
except Exception as exc:
log_err_n_raise(
"Failed to get type of direcotry server from hook", exc)
def get_num_of_groups() -> int:
try:
return int(os.environ['NITEMS'])
except Exception as exc:
log_err_n_raise("Failed to get num of deleted groups from hook", exc)
def get_group_infos(num_of_groups: int):
try:
return [
GroupInfo(
int(os.environ['GID_{}'.format(i)]),
os.environ['GROUP_NAME_{}'.format(i)]
)
for i in range(1, num_of_groups + 1)
]
except Exception as exc:
log_err_n_raise("Failed to get group infos from hook", exc)
def remove_groups_of_directory_server_from_cms_db(
directory_server_type,
group_infos):
try:
query = read_query_string()
with sqlite3.connect(CMS_DB_PATH) as conn:
conn.cursor().executemany(
query, [(g.gid, directory_server_type) for g in group_infos])
except Exception as exc:
str_group_infos = ', '.join(map(str, group_infos))
log_err_n_raise(
"Failed to remove direcotry server groups from cms db: [{}]"
.format(str_group_infos),
exc)
def read_query_string() -> str:
try:
libcms = CDLL(CMS_LIB_PATH)
query_fn = (
libcms.SYNOCMSDelegateRemoveDirectoryServerGroup_sqlite_query
)
query_fn.restype = c_char_p
return query_fn().decode('utf-8')
except Exception as exc:
log_err_n_raise(
'Failed to read query string for'
' removing delegations of directory server group',
exc)
if __name__ == '__main__':
try:
main()
except Exception:
# Here we log call stack to syslog only since python would log to
# stdout for us.
syslog.syslog(syslog.LOG_ERR, traceback.format_exc())
raise