forked from ungleich-public/cdist
Add support for python type defined argument parser
This commit is contained in:
parent
3dcd270158
commit
a2243cf59e
6 changed files with 96 additions and 65 deletions
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
from cdist.core.pytypes import *
|
from cdist.core.pytypes import *
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
class FileType(PythonType):
|
class FileType(PythonType):
|
||||||
|
@ -101,3 +101,15 @@ class FileType(PythonType):
|
||||||
self.die('Unknown state {}'.format(state_should))
|
self.die('Unknown state {}'.format(state_should))
|
||||||
|
|
||||||
return "\n".join(code)
|
return "\n".join(code)
|
||||||
|
|
||||||
|
def get_args_parser(self):
|
||||||
|
parser = argparse.ArgumentParser(add_help=False,
|
||||||
|
argument_default=argparse.SUPPRESS)
|
||||||
|
parser.add_argument('--state', dest='state', action='store',
|
||||||
|
required=False, default='present')
|
||||||
|
for param in ('group', 'mode', 'owner', 'source'):
|
||||||
|
parser.add_argument('--' + param, dest=param, action='store',
|
||||||
|
required=False, default=None)
|
||||||
|
|
||||||
|
parser.add_argument("object_id", nargs=1)
|
||||||
|
return parser
|
||||||
|
|
|
@ -22,10 +22,8 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import importlib.util
|
|
||||||
import inspect
|
import inspect
|
||||||
import cdist
|
from cdist.core.pytypes import get_pytype_class
|
||||||
from cdist.core.pytypes import PythonType
|
|
||||||
from . import util
|
from . import util
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,25 +120,8 @@ class Code(object):
|
||||||
|
|
||||||
def run_py(self, cdist_object):
|
def run_py(self, cdist_object):
|
||||||
cdist_type = cdist_object.cdist_type
|
cdist_type = cdist_object.cdist_type
|
||||||
module_name = cdist_type.name
|
type_class = get_pytype_class(cdist_type)
|
||||||
file_path = os.path.join(cdist_type.absolute_path, '__init__.py')
|
if type_class is not None:
|
||||||
|
|
||||||
if os.path.isfile(file_path):
|
|
||||||
spec = importlib.util.spec_from_file_location(module_name,
|
|
||||||
file_path)
|
|
||||||
m = importlib.util.module_from_spec(spec)
|
|
||||||
spec.loader.exec_module(m)
|
|
||||||
classes = inspect.getmembers(m, inspect.isclass)
|
|
||||||
type_class = None
|
|
||||||
for _, cl in classes:
|
|
||||||
if cl != PythonType and issubclass(cl, PythonType):
|
|
||||||
if type_class:
|
|
||||||
raise cdist.Error("Only one python type class is "
|
|
||||||
"supported, but at least two "
|
|
||||||
"found: {}".format((type_class,
|
|
||||||
cl, )))
|
|
||||||
else:
|
|
||||||
type_class = cl
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env.update(self.env)
|
env.update(self.env)
|
||||||
message_prefix = cdist_object.name
|
message_prefix = cdist_object.name
|
||||||
|
|
|
@ -22,12 +22,11 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import importlib.util
|
|
||||||
import inspect
|
import inspect
|
||||||
import cdist
|
import cdist
|
||||||
import cdist.emulator
|
import cdist.emulator
|
||||||
from . import util
|
from . import util
|
||||||
from cdist.core.pytypes import PythonType, Command
|
from cdist.core.pytypes import Command, get_pytype_class
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
@ -248,27 +247,11 @@ class Manifest(object):
|
||||||
|
|
||||||
def run_py_type_manifest(self, cdist_object):
|
def run_py_type_manifest(self, cdist_object):
|
||||||
cdist_type = cdist_object.cdist_type
|
cdist_type = cdist_object.cdist_type
|
||||||
module_name = cdist_type.name
|
type_class = get_pytype_class(cdist_type)
|
||||||
file_path = os.path.join(cdist_type.absolute_path, '__init__.py')
|
if type_class is not None:
|
||||||
message_prefix = cdist_object.name
|
|
||||||
if os.path.isfile(file_path):
|
|
||||||
self.log.verbose("Running python type manifest for object %s",
|
self.log.verbose("Running python type manifest for object %s",
|
||||||
cdist_object.name)
|
cdist_object.name)
|
||||||
spec = importlib.util.spec_from_file_location(module_name,
|
message_prefix = cdist_object.name
|
||||||
file_path)
|
|
||||||
m = importlib.util.module_from_spec(spec)
|
|
||||||
spec.loader.exec_module(m)
|
|
||||||
classes = inspect.getmembers(m, inspect.isclass)
|
|
||||||
type_class = None
|
|
||||||
for _, cl in classes:
|
|
||||||
if cl != PythonType and issubclass(cl, PythonType):
|
|
||||||
if type_class:
|
|
||||||
raise cdist.Error("Only one python type class is "
|
|
||||||
"supported, but at least two "
|
|
||||||
"found: {}".format((type_class,
|
|
||||||
cl, )))
|
|
||||||
else:
|
|
||||||
type_class = cl
|
|
||||||
env = self.env_py_type_manifest(cdist_object)
|
env = self.env_py_type_manifest(cdist_object)
|
||||||
type_obj = type_class(env=env, cdist_object=cdist_object,
|
type_obj = type_class(env=env, cdist_object=cdist_object,
|
||||||
local=self.local, remote=None,
|
local=self.local, remote=None,
|
||||||
|
|
|
@ -4,6 +4,9 @@ import io
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
from cdist import message, Error
|
from cdist import message, Error
|
||||||
|
import importlib.util
|
||||||
|
import inspect
|
||||||
|
import cdist
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["PythonType", "Command", "command"]
|
__all__ = ["PythonType", "Command", "command"]
|
||||||
|
@ -13,18 +16,20 @@ class PythonType:
|
||||||
def __init__(self, env, cdist_object, local, remote, message_prefix=None):
|
def __init__(self, env, cdist_object, local, remote, message_prefix=None):
|
||||||
self.env = env
|
self.env = env
|
||||||
self.cdist_object = cdist_object
|
self.cdist_object = cdist_object
|
||||||
self.object_id = cdist_object.object_id
|
|
||||||
self.object_name = cdist_object.name
|
|
||||||
self.cdist_type = cdist_object.cdist_type
|
|
||||||
self.local = local
|
self.local = local
|
||||||
self.remote = remote
|
self.remote = remote
|
||||||
self.object_path = cdist_object.absolute_path
|
if self.cdist_object:
|
||||||
self.type_path = cdist_object.cdist_type.absolute_path
|
self.object_id = cdist_object.object_id
|
||||||
self.explorer_path = os.path.join(self.object_path, 'explorer')
|
self.object_name = cdist_object.name
|
||||||
self.parameters = cdist_object.parameters
|
self.cdist_type = cdist_object.cdist_type
|
||||||
self.stdin_path = os.path.join(self.object_path, 'stdin')
|
self.object_path = cdist_object.absolute_path
|
||||||
self.log = logging.getLogger(
|
self.explorer_path = os.path.join(self.object_path, 'explorer')
|
||||||
self.local.target_host[0] + ':' + self.object_name)
|
self.type_path = cdist_object.cdist_type.absolute_path
|
||||||
|
self.parameters = cdist_object.parameters
|
||||||
|
self.stdin_path = os.path.join(self.object_path, 'stdin')
|
||||||
|
if self.local:
|
||||||
|
self.log = logging.getLogger(
|
||||||
|
self.local.target_host[0] + ':' + self.object_name)
|
||||||
|
|
||||||
self.message_prefix = message_prefix
|
self.message_prefix = message_prefix
|
||||||
self.message = None
|
self.message = None
|
||||||
|
@ -62,12 +67,6 @@ class PythonType:
|
||||||
def die(self, msg):
|
def die(self, msg):
|
||||||
raise Error("{}: {}".format(self.cdist_object, msg))
|
raise Error("{}: {}".format(self.cdist_object, msg))
|
||||||
|
|
||||||
def type_manifest(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def type_gencode(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def manifest(self, stdout=None, stderr=None):
|
def manifest(self, stdout=None, stderr=None):
|
||||||
try:
|
try:
|
||||||
if self.message_prefix:
|
if self.message_prefix:
|
||||||
|
@ -123,6 +122,15 @@ class PythonType:
|
||||||
return match
|
return match
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_args_parser(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def type_manifest(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def type_gencode(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Command:
|
class Command:
|
||||||
def __init__(self, name, *args, **kwargs):
|
def __init__(self, name, *args, **kwargs):
|
||||||
|
@ -160,3 +168,23 @@ class Command:
|
||||||
|
|
||||||
def command(name, *args, **kwargs):
|
def command(name, *args, **kwargs):
|
||||||
return Command(name, *args, **kwargs)
|
return Command(name, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def get_pytype_class(cdist_type):
|
||||||
|
module_name = cdist_type.name
|
||||||
|
file_path = os.path.join(cdist_type.absolute_path, '__init__.py')
|
||||||
|
type_class = None
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
||||||
|
m = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(m)
|
||||||
|
classes = inspect.getmembers(m, inspect.isclass)
|
||||||
|
for _, cl in classes:
|
||||||
|
if cl != PythonType and issubclass(cl, PythonType):
|
||||||
|
if type_class:
|
||||||
|
raise cdist.Error(
|
||||||
|
"Only one python type class is supported, but at least"
|
||||||
|
" two found: {}".format((type_class, cl, )))
|
||||||
|
else:
|
||||||
|
type_class = cl
|
||||||
|
return type_class
|
||||||
|
|
|
@ -30,6 +30,9 @@ import cdist
|
||||||
from cdist import core
|
from cdist import core
|
||||||
from cdist import flock
|
from cdist import flock
|
||||||
from cdist.core.manifest import Manifest
|
from cdist.core.manifest import Manifest
|
||||||
|
import cdist.util.python_type_util as pytype_util
|
||||||
|
from cdist.core.pytypes import get_pytype_class
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
class MissingRequiredEnvironmentVariableError(cdist.Error):
|
class MissingRequiredEnvironmentVariableError(cdist.Error):
|
||||||
|
@ -100,7 +103,28 @@ class Emulator(object):
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Emulate type commands (i.e. __file and co)"""
|
"""Emulate type commands (i.e. __file and co)"""
|
||||||
|
|
||||||
self.commandline()
|
args_parser = None
|
||||||
|
if pytype_util.is_python_type(self.cdist_type):
|
||||||
|
type_class = get_pytype_class(self.cdist_type)
|
||||||
|
if type_class is not None:
|
||||||
|
# We only need to call parse_args so we need plain instance.
|
||||||
|
type_obj = type_class(env=None, cdist_object=None, local=None,
|
||||||
|
remote=None)
|
||||||
|
if (hasattr(type_obj, 'get_args_parser') and
|
||||||
|
inspect.ismethod(type_obj.get_args_parser)):
|
||||||
|
args_parser = type_obj.get_args_parser()
|
||||||
|
self.log.trace("Using python type argument parser")
|
||||||
|
print("Using python type argument parser")
|
||||||
|
if args_parser is None:
|
||||||
|
# Fallback to classic way.
|
||||||
|
args_parser = self.get_args_parser()
|
||||||
|
self.log.trace("Fallback to classic argument parser")
|
||||||
|
print("Fallback to classic argument parser")
|
||||||
|
else:
|
||||||
|
args_parser = self.get_args_parser()
|
||||||
|
self.log.trace("Using emulator classic argument parser")
|
||||||
|
print("Using emulator classic argument parser")
|
||||||
|
self.commandline(args_parser)
|
||||||
self.init_object()
|
self.init_object()
|
||||||
|
|
||||||
# locking for parallel execution
|
# locking for parallel execution
|
||||||
|
@ -131,9 +155,7 @@ class Emulator(object):
|
||||||
|
|
||||||
self.log = logging.getLogger(self.target_host[0])
|
self.log = logging.getLogger(self.target_host[0])
|
||||||
|
|
||||||
def commandline(self):
|
def get_args_parser(self):
|
||||||
"""Parse command line"""
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(add_help=False,
|
parser = argparse.ArgumentParser(add_help=False,
|
||||||
argument_default=argparse.SUPPRESS)
|
argument_default=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
@ -165,8 +187,10 @@ class Emulator(object):
|
||||||
# If not singleton support one positional parameter
|
# If not singleton support one positional parameter
|
||||||
if not self.cdist_type.is_singleton:
|
if not self.cdist_type.is_singleton:
|
||||||
parser.add_argument("object_id", nargs=1)
|
parser.add_argument("object_id", nargs=1)
|
||||||
|
return parser
|
||||||
|
|
||||||
# And finally parse/verify parameter
|
def commandline(self, parser):
|
||||||
|
"""Parse command line"""
|
||||||
self.args = parser.parse_args(self.argv[1:])
|
self.args = parser.parse_args(self.argv[1:])
|
||||||
self.log.trace('Args: %s' % self.args)
|
self.log.trace('Args: %s' % self.args)
|
||||||
|
|
||||||
|
|
|
@ -542,6 +542,9 @@ in shell, since this is the code that is directly executed at target host.
|
||||||
When writing python type you can extend **cdist.core.pytypes.PythonType** class.
|
When writing python type you can extend **cdist.core.pytypes.PythonType** class.
|
||||||
You need to implement the following methods:
|
You need to implement the following methods:
|
||||||
|
|
||||||
|
* **get_args_parser**: implementation should return **argparse.ArgumentParser** and if
|
||||||
|
it is undefined or returned None then cdist falls back to classic type parameter
|
||||||
|
definition and argument parsing
|
||||||
* **type_manifest**: implementation should yield **cdist.core.pytypes.<type-name>**
|
* **type_manifest**: implementation should yield **cdist.core.pytypes.<type-name>**
|
||||||
attribute function call result, or **yield from ()** if type does not use other types
|
attribute function call result, or **yield from ()** if type does not use other types
|
||||||
* **type_gencode**: implementation should return a string consisting of lines
|
* **type_gencode**: implementation should return a string consisting of lines
|
||||||
|
|
Loading…
Reference in a new issue