2009-12-28 01:04:33 +01:00
# Unix SMB/CIFS implementation.
2012-09-25 22:34:36 +02:00
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009-2012
2011-05-19 16:17:07 -04:00
# Copyright (C) Theresa Halloran <theresahalloran@gmail.com> 2011
2009-12-28 01:04:33 +01:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
2018-07-30 18:21:38 +12:00
import optparse
import samba
2018-04-19 14:15:25 +12:00
from samba import colour
2019-06-21 13:12:01 +12:00
from samba . getopt import SambaOption
2018-08-21 12:08:59 +12:00
from samba . logger import get_samba_logger
2010-11-29 14:11:57 +11:00
from ldb import LdbError
2018-07-30 18:21:38 +12:00
import sys
import traceback
2011-10-13 23:08:32 +02:00
import textwrap
2009-12-28 13:53:18 +01:00
2018-07-30 18:20:39 +12:00
2019-06-21 13:12:01 +12:00
class Option ( SambaOption ) :
2019-03-12 10:25:40 +01:00
SUPPRESS_HELP = optparse . SUPPRESS_HELP
2009-12-28 13:53:18 +01:00
pass
2011-11-02 10:33:12 -04:00
# This help formatter does text wrapping and preserves newlines
2018-07-30 18:20:39 +12:00
2011-11-02 10:33:12 -04:00
class PlainHelpFormatter ( optparse . IndentedHelpFormatter ) :
2018-07-30 18:19:05 +12:00
def format_description ( self , description = " " ) :
2018-07-30 18:13:57 +12:00
desc_width = self . width - self . current_indent
2018-07-30 18:18:25 +12:00
indent = " " * self . current_indent
2018-07-30 18:13:57 +12:00
paragraphs = description . split ( ' \n ' )
wrapped_paragraphs = [
textwrap . fill ( p ,
2018-07-30 18:16:12 +12:00
desc_width ,
initial_indent = indent ,
subsequent_indent = indent )
2018-07-30 18:13:57 +12:00
for p in paragraphs ]
result = " \n " . join ( wrapped_paragraphs ) + " \n "
return result
2011-11-02 10:33:12 -04:00
2012-10-08 12:45:20 +02:00
def format_epilog ( self , epilog ) :
if epilog :
return " \n " + epilog + " \n "
else :
return " "
2009-12-28 13:53:18 +01:00
2018-07-30 18:20:39 +12:00
2009-12-28 01:04:33 +01:00
class Command ( object ) :
2011-07-18 18:34:45 -04:00
""" A samba-tool command. """
2011-10-13 23:08:32 +02:00
def _get_short_description ( self ) :
2009-12-28 16:05:04 +01:00
return self . __doc__ . splitlines ( ) [ 0 ] . rstrip ( " \n " )
2009-12-28 13:53:18 +01:00
2011-10-13 23:08:32 +02:00
short_description = property ( _get_short_description )
def _get_full_description ( self ) :
lines = self . __doc__ . split ( " \n " )
return lines [ 0 ] + " \n " + textwrap . dedent ( " \n " . join ( lines [ 1 : ] ) )
2009-12-28 13:53:18 +01:00
2011-10-13 23:08:32 +02:00
full_description = property ( _get_full_description )
2011-10-13 23:16:58 +02:00
def _get_name ( self ) :
name = self . __class__ . __name__
if name . startswith ( " cmd_ " ) :
return name [ 4 : ]
return name
name = property ( _get_name )
2011-10-13 23:08:32 +02:00
# synopsis must be defined in all subclasses in order to provide the
# command usage
synopsis = None
2011-07-28 14:21:40 -04:00
takes_args = [ ]
takes_options = [ ]
2012-02-06 16:33:38 +01:00
takes_optiongroups = { }
2011-11-02 16:39:47 +01:00
2012-09-25 22:34:36 +02:00
hidden = False
2012-09-10 14:02:19 +02:00
raw_argv = None
raw_args = None
raw_kwargs = None
2011-11-02 16:39:47 +01:00
def __init__ ( self , outf = sys . stdout , errf = sys . stderr ) :
self . outf = outf
self . errf = errf
2009-12-28 01:04:33 +01:00
2018-10-26 20:20:55 +13:00
def usage ( self , prog = None ) :
2011-10-13 23:27:22 +02:00
parser , _ = self . _create_parser ( prog )
2009-12-28 16:48:07 +01:00
parser . print_usage ( )
2009-12-28 01:21:27 +01:00
2010-11-29 14:11:57 +11:00
def show_command_error ( self , e ) :
''' display a command error '''
if isinstance ( e , CommandError ) :
( etype , evalue , etraceback ) = e . exception_info
inner_exception = e . inner_exception
message = e . message
force_traceback = False
else :
( etype , evalue , etraceback ) = sys . exc_info ( )
inner_exception = e
message = " uncaught exception "
force_traceback = True
if isinstance ( inner_exception , LdbError ) :
2018-05-04 11:28:46 +01:00
( ldb_ecode , ldb_emsg ) = inner_exception . args
2011-10-12 23:19:12 +02:00
self . errf . write ( " ERROR(ldb): %s - %s \n " % ( message , ldb_emsg ) )
2010-11-29 14:11:57 +11:00
elif isinstance ( inner_exception , AssertionError ) :
2011-10-12 23:19:12 +02:00
self . errf . write ( " ERROR(assert): %s \n " % message )
2010-11-29 14:11:57 +11:00
force_traceback = True
elif isinstance ( inner_exception , RuntimeError ) :
2011-10-12 23:19:12 +02:00
self . errf . write ( " ERROR(runtime): %s - %s \n " % ( message , evalue ) )
2010-11-29 14:11:57 +11:00
elif type ( inner_exception ) is Exception :
2011-10-12 23:19:12 +02:00
self . errf . write ( " ERROR(exception): %s - %s \n " % ( message , evalue ) )
2010-11-29 14:11:57 +11:00
force_traceback = True
elif inner_exception is None :
2011-10-12 23:19:12 +02:00
self . errf . write ( " ERROR: %s \n " % ( message ) )
2010-11-29 14:11:57 +11:00
else :
2011-10-12 23:19:12 +02:00
self . errf . write ( " ERROR( %s ): %s - %s \n " % ( str ( etype ) , message , evalue ) )
2010-11-29 14:11:57 +11:00
force_traceback = True
if force_traceback or samba . get_debug_level ( ) > = 3 :
2016-11-28 14:30:43 +13:00
traceback . print_tb ( etraceback , file = self . errf )
2010-11-29 14:11:57 +11:00
2018-10-26 20:20:55 +13:00
def _create_parser ( self , prog = None , epilog = None ) :
2011-10-13 23:27:22 +02:00
parser = optparse . OptionParser (
usage = self . synopsis ,
description = self . full_description ,
2011-11-02 10:33:12 -04:00
formatter = PlainHelpFormatter ( ) ,
2018-07-30 18:19:05 +12:00
prog = prog , epilog = epilog )
2009-12-28 16:48:07 +01:00
parser . add_options ( self . takes_options )
optiongroups = { }
2018-10-17 18:06:34 +01:00
for name in sorted ( self . takes_optiongroups . keys ( ) ) :
optiongroup = self . takes_optiongroups [ name ]
2009-12-28 16:48:07 +01:00
optiongroups [ name ] = optiongroup ( parser )
parser . add_option_group ( optiongroups [ name ] )
return parser , optiongroups
2009-12-28 16:05:04 +01:00
def message ( self , text ) :
2018-07-30 18:18:25 +12:00
self . outf . write ( text + " \n " )
2009-12-28 13:53:18 +01:00
def _run ( self , * argv ) :
2011-10-13 23:27:22 +02:00
parser , optiongroups = self . _create_parser ( argv [ 0 ] )
2009-12-28 16:48:07 +01:00
opts , args = parser . parse_args ( list ( argv ) )
# Filter out options from option groups
2009-12-28 21:07:25 +01:00
args = args [ 1 : ]
2009-12-28 16:48:07 +01:00
kwargs = dict ( opts . __dict__ )
for option_group in parser . option_groups :
for option in option_group . option_list :
2010-04-09 02:37:20 +02:00
if option . dest is not None :
del kwargs [ option . dest ]
2009-12-28 16:48:07 +01:00
kwargs . update ( optiongroups )
2011-07-18 16:48:03 -04:00
# Check for a min a max number of allowed arguments, whenever possible
# The suffix "?" means zero or one occurence
# The suffix "+" means at least one occurence
2016-02-25 12:42:09 +13:00
# The suffix "*" means zero or more occurences
2009-12-30 20:40:11 +01:00
min_args = 0
max_args = 0
2011-07-18 16:48:03 -04:00
undetermined_max_args = False
2009-12-30 19:53:05 +01:00
for i , arg in enumerate ( self . takes_args ) :
2016-02-25 12:42:09 +13:00
if arg [ - 1 ] != " ? " and arg [ - 1 ] != " * " :
2018-07-30 18:13:57 +12:00
min_args + = 1
2016-02-25 12:42:09 +13:00
if arg [ - 1 ] == " + " or arg [ - 1 ] == " * " :
2018-07-30 18:13:57 +12:00
undetermined_max_args = True
2011-07-18 16:48:03 -04:00
else :
2018-07-30 18:13:57 +12:00
max_args + = 1
2012-09-27 09:30:47 -07:00
if ( len ( args ) < min_args ) or ( not undetermined_max_args and len ( args ) > max_args ) :
2011-07-18 16:48:03 -04:00
parser . print_usage ( )
2009-12-28 16:48:07 +01:00
return - 1
2011-07-18 16:48:03 -04:00
2012-09-10 14:02:19 +02:00
self . raw_argv = list ( argv )
self . raw_args = args
self . raw_kwargs = kwargs
2009-12-28 20:37:48 +01:00
try :
return self . run ( * args , * * kwargs )
2018-02-14 10:07:23 +13:00
except Exception as e :
2010-11-29 14:11:57 +11:00
self . show_command_error ( e )
2009-12-28 20:37:48 +01:00
return - 1
2009-12-28 13:53:18 +01:00
2009-12-28 01:04:33 +01:00
def run ( self ) :
2017-02-18 08:47:12 +13:00
""" Run the command. This should be overridden by all subclasses. """
2009-12-28 01:04:33 +01:00
raise NotImplementedError ( self . run )
2018-08-21 12:08:59 +12:00
def get_logger ( self , name = " " , verbose = False , quiet = False , * * kwargs ) :
2011-10-12 23:21:52 +02:00
""" Get a logger object. """
2018-08-21 12:08:59 +12:00
return get_samba_logger (
name = name or self . name , stream = self . errf ,
verbose = verbose , quiet = quiet ,
* * kwargs )
2009-12-28 01:04:33 +01:00
2018-04-19 14:15:25 +12:00
def apply_colour_choice ( self , requested ) :
""" Heuristics to work out whether the user wants colour output, from a
- - color = yes | no | auto option . This alters the ANSI 16 bit colour
" constants " in the colour module to be either real colours or empty
strings .
"""
requested = requested . lower ( )
if requested == ' no ' :
colour . switch_colour_off ( )
elif requested == ' yes ' :
colour . switch_colour_on ( )
elif requested == ' auto ' :
if ( hasattr ( self . outf , ' isatty ' ) and self . outf . isatty ( ) ) :
colour . switch_colour_on ( )
else :
colour . switch_colour_off ( )
else :
raise CommandError ( " Unknown --color option: %s "
" please choose from yes, no, auto " )
2011-07-18 11:30:23 -04:00
2009-12-28 13:53:18 +01:00
class SuperCommand ( Command ) :
2011-07-18 18:34:45 -04:00
""" A samba-tool command with subcommands. """
2009-12-28 13:53:18 +01:00
2011-10-13 23:47:45 +02:00
synopsis = " % prog <subcommand> "
2009-12-28 13:53:18 +01:00
subcommands = { }
2009-12-30 21:06:21 +01:00
def _run ( self , myname , subcommand = None , * args ) :
2010-11-28 10:52:09 +11:00
if subcommand in self . subcommands :
2011-10-13 23:27:22 +02:00
return self . subcommands [ subcommand ] . _run (
" %s %s " % ( myname , subcommand ) , * args )
2020-04-28 17:09:56 +02:00
elif subcommand not in [ ' --help ' , ' help ' , None ] :
print ( " %s : no such subcommand: %s \n " % ( myname , subcommand ) )
args = [ ]
2011-10-13 23:08:32 +02:00
2017-08-11 16:39:33 +12:00
if subcommand == ' help ' :
# pass the request down
if len ( args ) > 0 :
sub = self . subcommands . get ( args [ 0 ] )
if isinstance ( sub , SuperCommand ) :
return sub . _run ( " %s %s " % ( myname , args [ 0 ] ) , ' help ' ,
* args [ 1 : ] )
elif sub is not None :
return sub . _run ( " %s %s " % ( myname , args [ 0 ] ) , ' --help ' ,
* args [ 1 : ] )
subcommand = ' --help '
2012-10-08 12:47:47 +02:00
epilog = " \n Available subcommands: \n "
2018-10-15 16:23:07 +01:00
subcmds = list ( self . subcommands . keys ( ) )
2011-07-21 12:15:33 +10:00
subcmds . sort ( )
2011-10-13 00:36:44 +02:00
max_length = max ( [ len ( c ) for c in subcmds ] )
2012-09-25 22:34:36 +02:00
for cmd_name in subcmds :
cmd = self . subcommands [ cmd_name ]
if not cmd . hidden :
2012-10-08 12:47:47 +02:00
epilog + = " %*s - %s \n " % (
- max_length , cmd_name , cmd . short_description )
epilog + = " For more help on a specific subcommand, please type: %s <subcommand> (-h|--help) \n " % myname
parser , optiongroups = self . _create_parser ( myname , epilog = epilog )
args_list = list ( args )
if subcommand :
args_list . insert ( 0 , subcommand )
opts , args = parser . parse_args ( args_list )
parser . print_help ( )
return - 1
2009-12-30 21:06:21 +01:00
2009-12-28 13:53:18 +01:00
2009-12-28 16:05:04 +01:00
class CommandError ( Exception ) :
2011-10-13 23:27:22 +02:00
""" An exception class for samba-tool Command errors. """
2010-11-29 14:11:57 +11:00
def __init__ ( self , message , inner_exception = None ) :
self . message = message
self . inner_exception = inner_exception
self . exception_info = sys . exc_info ( )
2018-01-24 19:14:53 +01:00
def __repr__ ( self ) :
return " CommandError( %s ) " % self . message