Mini Shell
# -*- coding: utf-8 -*-
# Command line arguments parser for cloudlinux-selector utility
# cloudlinux-license Utility to check/set Cloudlinux license
#
# 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 docopt import docopt
from docopt import DocoptExit
from schema import Schema, And, Use, Or, SchemaError
from .selectorlib import CloudlinuxSelectorLib
NODEJS = 'nodejs'
PYTHON = 'python'
RUBY = 'ruby'
PHP = 'php'
def _ensure_command_allowed(interpreter, args, as_from_root, selector_status):
"""
Do some additional checks to restrict commands not available for current
user or interpreter or whatever and do this only after args parsing
"""
if not as_from_root and any([
args["install-version"],
args["uninstall-version"],
args["enable-version"],
args["disable-version"],
]):
raise SchemaError(
None, 'This command should be run from administrator only')
if interpreter != NODEJS and any([
args["change-version-multiple"],
]):
raise SchemaError(None, 'This command is supported only for NodeJS')
if interpreter != PYTHON and any([
args["import-applications"],
args["migrate"],
args["uninstall-modules"],
]):
raise SchemaError(None, 'This command is supported only for Python')
if interpreter not in (NODEJS, PYTHON) and any([
args["enable-version"],
args["disable-version"],
args["create"],
args["read-config"],
args["save-config"],
args["install-version"],
args["uninstall-version"],
args["start"],
args["restart"],
args["stop"],
args["destroy"],
args["install-modules"],
args["run-script"],
]):
raise SchemaError(None, 'This command is supported only for NodeJS and Python')
if interpreter != PHP and (args["make-defaults-config"] or args['setup']):
raise SchemaError(None, 'This command is supported only for %s' % PHP)
if not as_from_root and any([
args['make-defaults-config'],
args['setup'],
args['--selector-status'],
args['--default-version'],
args['--supported-versions']
]):
raise SchemaError(None, 'Specified option(s) only for root')
if interpreter != PYTHON and any([
args["--entry-point"]
]):
raise SchemaError(None, 'This options(s) only for Python')
if interpreter in (NODEJS, PYTHON) and not args['get'] and not selector_status and not _run_from_admin():
raise SchemaError(None, 'Selector is disabled')
def _run_from_admin():
"""
Check who is owner of the parent process.
if owner is root - return True
if parent process can't be found - return True
:return:
"""
try:
return os.stat(os.path.join('/proc/', str(os.getppid()))).st_uid == 0
except OSError:
return True
def parse_cloudlinux_selector_opts(argv, _is_json_need=False,
as_from_root=True):
"""
Parse arguments for cloudlinux-selector command
:param argv: sys.argv
:param _is_json_need: sys.argv contains --json key
:param as_from_root: True if we assume that root has called this util
:return cortege: (error_flag, s_message)
"""
# program name
prog_name = "cloudlinux-selector"
docstring = """Utility to get/set Cloudlinux selector options
Usage:
{0} [get] [--json] [--interpreter <str>] [(--get-supported-versions | --get-default-version | --get-selector-status)] [(--user <str> | --domain <str>)]
{0} get [--json] [--interpreter <str>] [--get-current-version] [--user <str>]
{0} set [--json] [--interpreter <str>] (--selector-status <enabled,disabled> | --default-version <str> | --supported-versions <str> | --current-version <str>) [--user <str>]
{0} set [--json] [--interpreter <str>] --version <str> (--extensions <str> | --options <str>) [--user <str>]
{0} set [--json] [--interpreter <str>] [(--user <str> | --domain <str>)] --app-root <str> [--app-mode <str>] [--new-app-root <str>] [--new-domain <str>] [--new-app-uri <str>] [--new-version <str>] [--startup-file <str>] [--env-vars <str>] [--skip-web-check] [--entry-point <str>] [--config-files <str>] [--passenger-log-file <str>]
{0} create [--json] [--interpreter <str>] [(--user <str> | --domain <str>)] --app-root <str> --app-uri <str> [--version <str>] [--app-mode <str>] [--startup-file <str>] [--entry-point <str>] [--env-vars <str>] [--passenger-log-file <str>]
{0} set [--json] [--interpreter <str>] --reset-extensions --version <str> [--user <str>]
{0} (enable-version | disable-version) [--json] [--interpreter <str>] --version <str>
{0} install-modules [--json] [--interpreter <str>] [(--user <str> | --domain <str>)] --app-root <str> [(--requirements-file <str> | --modules <str>)] [--skip-web-check]
{0} uninstall-modules [--json] [--interpreter <str>] [(--user <str> | --domain <str>)] --app-root <str> --modules <str>
{0} install-version [--json] [--interpreter <str>] --version <str>
{0} uninstall-version [--json] [--interpreter <str>] --version <str>
{0} read-config [--json] [--interpreter <str>] [(--user <str> | --domain <str>)] --app-root <str> --config-file <str>
{0} save-config [--json] [--interpreter <str>] [(--user <str> | --domain <str>)] --app-root <str> --config-file <str> --content <str>
{0} (start | restart | stop | destroy) [--json] [--interpreter <str>] [(--user <str> | --domain <str>)] --app-root <str>
{0} run-script [--json] [--interpreter <str>] [(--user <str> | --domain <str>)] --app-root <str> --script-name <str>
{0} run-script [--json] [--interpreter <str>] [(--user <str> | --domain <str>)] --app-root <str> --script-name <str> -- <script_args>...
{0} change-version-multiple --json --interpreter <str> --from-version <str> --new-version <str>
{0} make-defaults-config [--json] [--interpreter <str>]
{0} import-applications [--json] --interpreter <str>
{0} migrate [--json] --interpreter <str> --user <str> --app-root <str>
{0} setup [--json] --interpreter <str>
{0} (-h | --help)
Options:
--json Return data in JSON format.
--interpreter <str> One of php/nodejs/python, default is php
--get-supported-versions Return info about supported versions
--get-current-version Return current version of interpretator for user
--get-default-version Return info about default version only
--get-selector-status Return info about selector status
--reset-extensions Replace user extensions with version default extensions
--supported-versions <str> Set supported versions of interpreter
--default-version <str> Set default version of interpreter
--current-version <str> Set alternative as user default
--selector-status <enabled|disabled> Set selector status enabled or disabled
--version <str> Version of interpreter
--extensions <str> JSON dict with extensions and their status
--options <str> JSON dict with options and their values
--user <str> Username to operate on
--app-root <str> Root of an application to be created
--domain <str> Domain to work in
--app-uri <str> URI path to get the application being created
--config-file <str> path to config file to be read or saved
--config-files <str> names of config files (such as requirements.txt or etc) (only for python interpreter)
--content <str> Base64-encoded config file contents to be saved
--app-mode <str> Application mode
--startup-file <str> Startup application file
--env-vars <str> Environment variable as json string
--new-app-root <str> Set new application directory
--new-app-uri <str> Set new application uri
--new-domain <str> Set new domain for application
--new-version <str> Set new nodejs version for application
--from-version <str> Old NodeJS version for group change version operations
--script-name <str> Command for an npm script to be run
--skip-web-check Skip check web application after change it's properties
--entry-point <str> Use the specified entrypoint for application (only for python interpeter)
--requirements-file <str> Use the specified file for install required modules
--modules <str> Install comma-separated list of modules
--passenger-log-file <str> Set passenger log full filename
-h, --help Show this help message and exit
""".format(prog_name)
try:
args = docopt(docstring, argv)
except DocoptExit:
s_error_string = 'ERROR: Invalid parameter passed'
if not _is_json_need:
s_error_string += "\n\n" + docstring
return False, s_error_string
interpreter = args.get('--interpreter')
def _convert_version(x):
"""For the NodeJS, ignore all the version parts except the major one"""
parts = x.split('.')
if interpreter == NODEJS:
return str(int(parts[0]))
if interpreter == PYTHON:
ver = x
if len(parts) == 3:
ver = x.rsplit('.', 1)[0]
return ver
return float(x) # keep old behavior for PHP
_version_validator = Or(None, And(str, lambda x: x == "native"), And(str, Use(_convert_version)),
error="Version must be Interpreter version number or native")
def _json_string_to_dict(s_json):
j_dict = json.loads(s_json)
# json.loads in some cases returns a string instead dictionary.
# So try to get keys to ensure that j_dict really dictionary
list(j_dict.keys())
return j_dict
s_json_error = '%s option should contain a valid JSON'
s = Schema({
"get": bool,
"set": bool,
"import-applications": bool,
"migrate": bool,
"create": bool,
"read-config": bool,
"enable-version": bool,
"disable-version": bool,
"save-config": bool,
"install-modules": bool,
"uninstall-modules": bool,
"start": bool,
"restart": bool,
"stop": bool,
"destroy": bool,
"install-version": bool,
"uninstall-version": bool,
"run-script": bool,
"change-version-multiple": bool,
"make-defaults-config": bool,
"setup": bool,
"<script_args>": Or(None, list),
"--": bool,
"--json": bool,
"--help": bool,
"--interpreter": Or(None, PHP, PYTHON, RUBY, NODEJS),
"--get-supported-versions": bool,
"--get-current-version": bool,
"--get-default-version": bool,
"--get-selector-status": bool,
"--reset-extensions": bool,
"--supported-versions": Or(None, And(str, Use(_json_string_to_dict)),
error=s_json_error % "--supported-versions"),
"--default-version": _version_validator,
"--current-version": _version_validator,
"--selector-status": Or(None, And(str, lambda x: x in ["enabled", "disabled"]),
error="Selector status must be enabled or disabled"),
"--version": _version_validator,
"--extensions": Or(None, And(str, Use(_json_string_to_dict)),
error=s_json_error % "--extensions"),
"--options": Or(None, And(str, Use(_json_string_to_dict)),
error=s_json_error % "--options"),
"--user": Or(None, str),
"--app-root": Or(None, str),
"--domain": Or(None, str),
"--app-uri": Or(None, str),
"--config-file": Or(None, str),
"--content": Or(None, str),
"--app-mode": Or(None, str),
"--startup-file": Or(None, And(str, lambda x: x != "package.json"),
error='Cannot set "package.json" as startup file'),
"--env-vars": Or(None, str),
"--config-files": Or(None, str),
"--new-app-root": Or(None, str),
"--new-app-uri": Or(None, str),
"--new-domain": Or(None, str),
"--new-version": Or(None, str),
"--from-version": Or(None, str),
"--script-name": Or(None, str),
"--entry-point": Or(None, str),
"--requirements-file": Or(None, str),
"--modules": Or(None, And(str, lambda x: bool(x)),
error="modules should be a comma-separated list of packages"),
"--skip-web-check": bool,
"--passenger-log-file": Or(None, str)
})
def _check_users_part_cli(_args):
"""
Check args for existing of mandatory arguments
:param _args: parsed arguments from command line
:return: True if checking passed, False - not passed
"""
if interpreter not in (NODEJS, PYTHON):
return
if as_from_root and any((
_args['create'],
_args['read-config'],
_args['save-config'],
_args['install-modules'],
_args['uninstall-modules'],
_args['stop'],
_args['restart'],
_args['start'],
_args['destroy'],
_args['run-script'],
)) and not (_args['--user'] or _args['--domain']):
raise SchemaError(None, 'Domain or user argument is mandatory while calling selector under root')
if args["--interpreter"] == PYTHON and args['--entry-point'] and not args['--startup-file']:
raise SchemaError(None, '--entry-point option is requires --startup-file option for interpreter python')
if args["--app-mode"] and args["--interpreter"] != NODEJS:
raise SchemaError(None, '--app-mode option is requires only for interpreter nodejs')
if args["--entry-point"] and args["--interpreter"] != PYTHON:
raise SchemaError(None, '--entry-point option is requires only for interpreter python')
try:
args = s.validate(args)
except SchemaError as e:
return False, str(e)
if not args['--json'] and not args['import-applications']:
return False, "use --json option, other modes currently unsupported"
if interpreter is None or interpreter in (PHP, ):
args["--interpreter"] = PHP
selector_status = False
elif interpreter in (RUBY,):
msg = 'ruby interpreter currently unsupported. Use selectorctl instead'
return False, msg
elif interpreter in (PYTHON, NODEJS):
lib = CloudlinuxSelectorLib(args["--interpreter"])
selector_status = list(lib.get_selector_status().values())[0]
try:
_ensure_command_allowed(interpreter, args, as_from_root, selector_status)
_check_users_part_cli(args)
except SchemaError as e:
return False, str(e)
return True, args
Zerion Mini Shell 1.0