2008-08-08 16:46:23 -05:00
|
|
|
# Authors:
|
|
|
|
# Jason Gerard DeRose <jderose@redhat.com>
|
|
|
|
#
|
|
|
|
# Copyright (C) 2008 Red Hat
|
|
|
|
# see file 'COPYING' for use and warranty information
|
|
|
|
#
|
|
|
|
# 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; version 2 only
|
|
|
|
#
|
|
|
|
# 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, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
"""
|
2008-09-23 19:01:29 -05:00
|
|
|
Functionality for Command Line Interface.
|
2008-08-08 16:46:23 -05:00
|
|
|
"""
|
|
|
|
|
2008-08-11 14:35:57 -05:00
|
|
|
import re
|
2008-11-12 01:46:04 -06:00
|
|
|
import textwrap
|
2008-08-11 21:03:47 -05:00
|
|
|
import sys
|
2008-11-18 12:30:16 -06:00
|
|
|
import getpass
|
2008-08-26 19:25:33 -05:00
|
|
|
import code
|
2008-09-04 01:33:57 -05:00
|
|
|
import optparse
|
2008-11-03 16:38:05 -06:00
|
|
|
import socket
|
2008-12-16 20:00:39 -06:00
|
|
|
import fcntl
|
|
|
|
import termios
|
|
|
|
import struct
|
2010-02-01 12:59:29 -06:00
|
|
|
import base64
|
2008-10-17 15:55:03 -05:00
|
|
|
|
2008-09-23 19:01:29 -05:00
|
|
|
import frontend
|
2008-11-12 01:46:04 -06:00
|
|
|
import backend
|
2008-09-08 16:37:02 -05:00
|
|
|
import plugable
|
2008-10-31 19:17:08 -05:00
|
|
|
import util
|
2009-06-09 14:24:23 -05:00
|
|
|
from errors import PublicError, CommandError, HelpError, InternalError, NoSuchNamespaceError, ValidationError, NotFound
|
2008-11-12 01:46:04 -06:00
|
|
|
from constants import CLI_TAB
|
2009-11-05 09:15:47 -06:00
|
|
|
from parameters import Password, Bytes, File
|
2009-01-23 11:33:54 -06:00
|
|
|
from request import ugettext as _
|
2008-09-10 18:33:36 -05:00
|
|
|
|
|
|
|
|
2008-08-08 16:46:23 -05:00
|
|
|
def to_cli(name):
|
|
|
|
"""
|
|
|
|
Takes a Python identifier and transforms it into form suitable for the
|
|
|
|
Command Line Interface.
|
|
|
|
"""
|
|
|
|
assert isinstance(name, str)
|
|
|
|
return name.replace('_', '-')
|
|
|
|
|
|
|
|
|
|
|
|
def from_cli(cli_name):
|
|
|
|
"""
|
|
|
|
Takes a string from the Command Line Interface and transforms it into a
|
|
|
|
Python identifier.
|
|
|
|
"""
|
2008-08-13 20:09:11 -05:00
|
|
|
return str(cli_name).replace('-', '_')
|
2008-08-11 14:35:57 -05:00
|
|
|
|
|
|
|
|
2008-11-12 01:46:04 -06:00
|
|
|
class textui(backend.Backend):
|
2008-09-24 00:03:10 -05:00
|
|
|
"""
|
2008-11-12 01:46:04 -06:00
|
|
|
Backend plugin to nicely format output to stdout.
|
2008-09-24 00:03:10 -05:00
|
|
|
"""
|
|
|
|
|
2008-11-12 01:46:04 -06:00
|
|
|
def get_tty_width(self):
|
|
|
|
"""
|
|
|
|
Return the width (in characters) of output tty.
|
|
|
|
|
|
|
|
If stdout is not a tty, this method will return ``None``.
|
|
|
|
"""
|
2008-12-16 20:00:39 -06:00
|
|
|
# /usr/include/asm/termios.h says that struct winsize has four
|
|
|
|
# unsigned shorts, hence the HHHH
|
2008-11-12 01:46:04 -06:00
|
|
|
if sys.stdout.isatty():
|
2008-12-16 20:00:39 -06:00
|
|
|
try:
|
|
|
|
winsize = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ,
|
|
|
|
struct.pack('HHHH', 0, 0, 0, 0))
|
|
|
|
return struct.unpack('HHHH', winsize)[1]
|
|
|
|
except IOError:
|
|
|
|
return None
|
2008-11-12 01:46:04 -06:00
|
|
|
|
|
|
|
def max_col_width(self, rows, col=None):
|
|
|
|
"""
|
|
|
|
Return the max width (in characters) of a specified column.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> rows = [
|
|
|
|
... ('a', 'package'),
|
|
|
|
... ('an', 'egg'),
|
|
|
|
... ]
|
|
|
|
>>> ui.max_col_width(rows, col=0) # len('an')
|
|
|
|
2
|
|
|
|
>>> ui.max_col_width(rows, col=1) # len('package')
|
|
|
|
7
|
|
|
|
>>> ui.max_col_width(['a', 'cherry', 'py']) # len('cherry')
|
|
|
|
6
|
|
|
|
"""
|
|
|
|
if type(rows) not in (list, tuple):
|
|
|
|
raise TypeError(
|
|
|
|
'rows: need %r or %r; got %r' % (list, tuple, rows)
|
|
|
|
)
|
|
|
|
if len(rows) == 0:
|
|
|
|
return 0
|
|
|
|
if col is None:
|
|
|
|
return max(len(row) for row in rows)
|
|
|
|
return max(len(row[col]) for row in rows)
|
|
|
|
|
2008-11-19 04:31:29 -06:00
|
|
|
def __get_encoding(self, stream):
|
|
|
|
assert stream in (sys.stdin, sys.stdout)
|
|
|
|
if stream.encoding is None:
|
|
|
|
return 'UTF-8'
|
|
|
|
return stream.encoding
|
|
|
|
|
2009-01-23 10:55:38 -06:00
|
|
|
def decode(self, value):
|
2008-11-19 04:31:29 -06:00
|
|
|
"""
|
|
|
|
Decode text from stdin.
|
|
|
|
"""
|
2009-01-23 10:55:38 -06:00
|
|
|
if type(value) is str:
|
2009-01-22 18:03:48 -06:00
|
|
|
encoding = self.__get_encoding(sys.stdin)
|
2009-01-23 10:55:38 -06:00
|
|
|
return value.decode(encoding)
|
|
|
|
elif type(value) in (list, tuple):
|
|
|
|
return tuple(self.decode(v) for v in value)
|
|
|
|
return value
|
2008-11-19 04:31:29 -06:00
|
|
|
|
|
|
|
def encode(self, unicode_text):
|
|
|
|
"""
|
|
|
|
Encode text for output to stdout.
|
|
|
|
"""
|
|
|
|
assert type(unicode_text) is unicode
|
|
|
|
encoding = self.__get_encoding(sys.stdout)
|
|
|
|
return unicode_text.encode(encoding)
|
|
|
|
|
2008-11-14 14:33:42 -06:00
|
|
|
def choose_number(self, n, singular, plural=None):
|
|
|
|
if n == 1 or plural is None:
|
|
|
|
return singular % n
|
|
|
|
return plural % n
|
2008-11-12 01:46:04 -06:00
|
|
|
|
2010-02-01 12:59:29 -06:00
|
|
|
def encode_binary(self, value):
|
|
|
|
"""
|
|
|
|
Convert a binary value to base64. We know a value is binary
|
|
|
|
if it is a python str type, otherwise it is a plain string.
|
|
|
|
"""
|
|
|
|
if type(value) is str:
|
|
|
|
return base64.b64encode(value)
|
|
|
|
else:
|
|
|
|
return value
|
|
|
|
|
2008-11-14 14:33:42 -06:00
|
|
|
def print_plain(self, string):
|
|
|
|
"""
|
|
|
|
Print exactly like ``print`` statement would.
|
2008-11-12 01:46:04 -06:00
|
|
|
"""
|
2008-09-24 00:03:10 -05:00
|
|
|
print string
|
|
|
|
|
2008-11-12 01:46:04 -06:00
|
|
|
def print_line(self, text, width=None):
|
|
|
|
"""
|
|
|
|
Force printing on a single line, using ellipsis if needed.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_line('This line can fit!', width=18)
|
|
|
|
This line can fit!
|
|
|
|
>>> ui.print_line('This line wont quite fit!', width=18)
|
|
|
|
This line wont ...
|
|
|
|
|
|
|
|
The above example aside, you normally should not specify the
|
|
|
|
``width``. When you don't, it is automatically determined by calling
|
|
|
|
`textui.get_tty_width()`.
|
|
|
|
"""
|
|
|
|
if width is None:
|
|
|
|
width = self.get_tty_width()
|
|
|
|
if width is not None and width < len(text):
|
|
|
|
text = text[:width - 3] + '...'
|
|
|
|
print text
|
|
|
|
|
2008-11-14 14:33:42 -06:00
|
|
|
def print_paragraph(self, text, width=None):
|
|
|
|
"""
|
|
|
|
Print a paragraph, automatically word-wrapping to tty width.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> text = '''
|
|
|
|
... Python is a dynamic object-oriented programming language that can
|
|
|
|
... be used for many kinds of software development.
|
|
|
|
... '''
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_paragraph(text, width=45)
|
|
|
|
Python is a dynamic object-oriented
|
|
|
|
programming language that can be used for
|
|
|
|
many kinds of software development.
|
|
|
|
|
|
|
|
The above example aside, you normally should not specify the
|
|
|
|
``width``. When you don't, it is automatically determined by calling
|
|
|
|
`textui.get_tty_width()`.
|
|
|
|
|
|
|
|
The word-wrapping is done using the Python ``textwrap`` module. See:
|
|
|
|
|
|
|
|
http://docs.python.org/library/textwrap.html
|
|
|
|
"""
|
|
|
|
if width is None:
|
|
|
|
width = self.get_tty_width()
|
|
|
|
for line in textwrap.wrap(text.strip(), width):
|
|
|
|
print line
|
|
|
|
|
2008-11-12 01:46:04 -06:00
|
|
|
def print_indented(self, text, indent=1):
|
|
|
|
"""
|
|
|
|
Print at specified indentation level.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_indented('One indentation level.')
|
|
|
|
One indentation level.
|
|
|
|
>>> ui.print_indented('Two indentation levels.', indent=2)
|
|
|
|
Two indentation levels.
|
|
|
|
>>> ui.print_indented('No indentation.', indent=0)
|
|
|
|
No indentation.
|
|
|
|
"""
|
|
|
|
print (CLI_TAB * indent + text)
|
|
|
|
|
|
|
|
def print_keyval(self, rows, indent=1):
|
|
|
|
"""
|
|
|
|
Print (key = value) pairs, one pair per line.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> items = [
|
|
|
|
... ('in_server', True),
|
2010-02-12 15:34:21 -06:00
|
|
|
... ('mode', u'production'),
|
2008-11-12 01:46:04 -06:00
|
|
|
... ]
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_keyval(items)
|
|
|
|
in_server = True
|
2010-02-12 15:34:21 -06:00
|
|
|
mode = u'production'
|
2008-11-12 01:46:04 -06:00
|
|
|
>>> ui.print_keyval(items, indent=0)
|
|
|
|
in_server = True
|
2010-02-12 15:34:21 -06:00
|
|
|
mode = u'production'
|
2008-11-12 01:46:04 -06:00
|
|
|
|
|
|
|
Also see `textui.print_indented`.
|
|
|
|
"""
|
2008-11-14 23:03:31 -06:00
|
|
|
for (key, value) in rows:
|
2010-02-01 12:59:29 -06:00
|
|
|
self.print_indented('%s = %r' % (key, self.encode_binary(value)), indent)
|
2008-11-12 01:46:04 -06:00
|
|
|
|
2009-08-25 07:05:40 -05:00
|
|
|
def print_attribute(self, attr, value, indent=1, one_value_per_line=True):
|
2009-04-21 08:20:32 -05:00
|
|
|
"""
|
|
|
|
Print an ldap attribute.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> attr = 'dn'
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_attribute(attr, u'dc=example,dc=com')
|
|
|
|
dn: dc=example,dc=com
|
|
|
|
>>> attr = 'objectClass'
|
2009-08-27 08:55:19 -05:00
|
|
|
>>> ui.print_attribute(attr, [u'top', u'someClass'], one_value_per_line=False)
|
2009-08-04 01:21:26 -05:00
|
|
|
objectClass: top, someClass
|
2009-08-27 08:55:19 -05:00
|
|
|
>>> ui.print_attribute(attr, [u'top', u'someClass'])
|
|
|
|
objectClass: top
|
|
|
|
objectClass: someClass
|
2009-04-21 08:20:32 -05:00
|
|
|
"""
|
|
|
|
assert isinstance(attr, basestring)
|
|
|
|
if not isinstance(value, (list, tuple)):
|
2009-07-31 10:59:28 -05:00
|
|
|
# single-value attribute
|
2010-02-01 12:59:29 -06:00
|
|
|
self.print_indented('%s: %s' % (attr, self.encode_binary(value)), indent)
|
2009-07-31 10:59:28 -05:00
|
|
|
else:
|
|
|
|
# multi-value attribute
|
2009-08-25 07:05:40 -05:00
|
|
|
if one_value_per_line:
|
|
|
|
for v in value:
|
2010-02-01 12:59:29 -06:00
|
|
|
self.print_indented('%s: %s' % (attr, self.encode_binary(v)), indent)
|
2009-08-25 07:05:40 -05:00
|
|
|
else:
|
2010-02-01 12:59:29 -06:00
|
|
|
value = map(lambda v: self.encode_binary(v), value)
|
2009-08-25 07:05:40 -05:00
|
|
|
text = ', '.join(value)
|
|
|
|
line_len = self.get_tty_width()
|
2009-10-22 09:47:22 -05:00
|
|
|
if line_len and text:
|
2009-08-25 07:05:40 -05:00
|
|
|
s_indent = '%s%s' % (
|
|
|
|
CLI_TAB * indent, ' ' * (len(attr) + 2)
|
|
|
|
)
|
|
|
|
line_len -= len(s_indent)
|
|
|
|
text = textwrap.wrap(
|
|
|
|
text, line_len, break_long_words=False
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
text = [text]
|
|
|
|
self.print_indented('%s: %s' % (attr, text[0]), indent)
|
|
|
|
for line in text[1:]:
|
|
|
|
self.print_plain('%s%s' % (s_indent, line))
|
|
|
|
|
2010-01-12 09:33:16 -06:00
|
|
|
def print_entry1(self, entry, indent=1, attr_map={}, attr_order=['dn'],
|
2009-08-25 07:05:40 -05:00
|
|
|
one_value_per_line=True):
|
2008-11-17 16:27:08 -06:00
|
|
|
"""
|
|
|
|
Print an ldap entry dict.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> entry = dict(sn='Last', givenname='First', uid='flast')
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_entry(entry)
|
2009-05-12 19:47:19 -05:00
|
|
|
givenname: First
|
|
|
|
sn: Last
|
|
|
|
uid: flast
|
2008-11-17 16:27:08 -06:00
|
|
|
"""
|
2009-07-31 10:59:28 -05:00
|
|
|
assert isinstance(entry, dict)
|
|
|
|
assert isinstance(attr_map, dict)
|
|
|
|
assert isinstance(attr_order, (list, tuple))
|
|
|
|
|
|
|
|
def print_attr(a):
|
|
|
|
if attr in attr_map:
|
2009-08-25 07:05:40 -05:00
|
|
|
self.print_attribute(
|
|
|
|
attr_map[attr], entry[attr], indent, one_value_per_line
|
|
|
|
)
|
2009-07-31 10:59:28 -05:00
|
|
|
else:
|
2009-08-25 07:05:40 -05:00
|
|
|
self.print_attribute(
|
|
|
|
attr, entry[attr], indent, one_value_per_line
|
|
|
|
)
|
2009-07-31 10:59:28 -05:00
|
|
|
|
|
|
|
for attr in attr_order:
|
|
|
|
if attr in entry:
|
|
|
|
print_attr(attr)
|
|
|
|
del entry[attr]
|
|
|
|
for attr in sorted(entry):
|
|
|
|
print_attr(attr)
|
2008-11-17 16:27:08 -06:00
|
|
|
|
2009-12-09 10:09:53 -06:00
|
|
|
def print_entries(self, entries, params=None, format='%s: %s', indent=1):
|
|
|
|
assert isinstance(entries, (list, tuple))
|
|
|
|
first = True
|
|
|
|
for entry in entries:
|
|
|
|
if not first:
|
|
|
|
print ''
|
|
|
|
first = False
|
|
|
|
self.print_entry(entry, params, format, indent)
|
|
|
|
|
|
|
|
def print_entry(self, entry, params=None, format='%s: %s', indent=1):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
if isinstance(entry, (list, tuple)):
|
|
|
|
entry = dict(entry)
|
|
|
|
assert isinstance(entry, dict)
|
|
|
|
if params:
|
|
|
|
order = list(params)
|
|
|
|
labels = dict((p.name, p.label) for p in params())
|
|
|
|
else:
|
|
|
|
order = sorted(entry)
|
|
|
|
labels = dict((k, k) for k in order)
|
|
|
|
for key in order:
|
|
|
|
if key not in entry:
|
|
|
|
continue
|
|
|
|
label = labels[key]
|
|
|
|
value = entry[key]
|
|
|
|
if isinstance(value, (list, tuple)):
|
2010-02-01 12:59:29 -06:00
|
|
|
value = map(lambda v: self.encode_binary(v), value)
|
2009-12-09 10:09:53 -06:00
|
|
|
value = ', '.join(value)
|
2010-02-12 15:34:21 -06:00
|
|
|
if isinstance(value, dict):
|
|
|
|
self.print_indented(format % (label, ''), indent)
|
|
|
|
self.print_entry(value, params, indent=indent+1)
|
|
|
|
else:
|
|
|
|
self.print_indented(format % (label, value), indent)
|
2009-12-09 10:09:53 -06:00
|
|
|
|
|
|
|
|
2008-11-17 21:41:01 -06:00
|
|
|
def print_dashed(self, string, above=True, below=True, indent=0, dash='-'):
|
2008-11-14 14:33:42 -06:00
|
|
|
"""
|
|
|
|
Print a string with a dashed line above and/or below.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_dashed('Dashed above and below.')
|
|
|
|
-----------------------
|
|
|
|
Dashed above and below.
|
|
|
|
-----------------------
|
|
|
|
>>> ui.print_dashed('Only dashed below.', above=False)
|
|
|
|
Only dashed below.
|
|
|
|
------------------
|
|
|
|
>>> ui.print_dashed('Only dashed above.', below=False)
|
|
|
|
------------------
|
|
|
|
Only dashed above.
|
|
|
|
"""
|
2008-11-17 21:41:01 -06:00
|
|
|
assert isinstance(dash, basestring)
|
|
|
|
assert len(dash) == 1
|
|
|
|
dashes = dash * len(string)
|
2008-11-14 14:33:42 -06:00
|
|
|
if above:
|
2008-11-17 21:41:01 -06:00
|
|
|
self.print_indented(dashes, indent)
|
|
|
|
self.print_indented(string, indent)
|
2008-11-14 14:33:42 -06:00
|
|
|
if below:
|
2008-11-17 21:41:01 -06:00
|
|
|
self.print_indented(dashes, indent)
|
|
|
|
|
|
|
|
def print_h1(self, text):
|
|
|
|
"""
|
|
|
|
Print a primary header at indentation level 0.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_h1('A primary header')
|
|
|
|
================
|
|
|
|
A primary header
|
|
|
|
================
|
|
|
|
"""
|
|
|
|
self.print_dashed(text, indent=0, dash='=')
|
|
|
|
|
|
|
|
def print_h2(self, text):
|
|
|
|
"""
|
|
|
|
Print a secondary header at indentation level 1.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_h2('A secondary header')
|
|
|
|
------------------
|
|
|
|
A secondary header
|
|
|
|
------------------
|
|
|
|
"""
|
|
|
|
self.print_dashed(text, indent=1, dash='-')
|
2008-11-14 14:33:42 -06:00
|
|
|
|
|
|
|
def print_name(self, name):
|
|
|
|
"""
|
|
|
|
Print a command name.
|
|
|
|
|
|
|
|
The typical use for this is to mark the start of output from a
|
|
|
|
command. For example, a hypothetical ``show_status`` command would
|
|
|
|
output something like this:
|
|
|
|
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_name('show_status')
|
|
|
|
------------
|
|
|
|
show-status:
|
|
|
|
------------
|
|
|
|
"""
|
|
|
|
self.print_dashed('%s:' % to_cli(name))
|
|
|
|
|
2009-12-09 10:09:53 -06:00
|
|
|
def print_header(self, msg, output):
|
|
|
|
self.print_dashed(msg % output)
|
|
|
|
|
|
|
|
def print_summary(self, msg):
|
|
|
|
"""
|
|
|
|
Print a summary at the end of a comand's output.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_summary('Added user "jdoe"')
|
|
|
|
-----------------
|
|
|
|
Added user "jdoe"
|
|
|
|
-----------------
|
|
|
|
"""
|
|
|
|
self.print_dashed(msg)
|
|
|
|
|
2008-11-12 01:46:04 -06:00
|
|
|
def print_count(self, count, singular, plural=None):
|
|
|
|
"""
|
|
|
|
Print a summary count.
|
|
|
|
|
|
|
|
The typical use for this is to print the number of items returned
|
|
|
|
by a command, especially when this return count can vary. This
|
|
|
|
preferably should be used as a summary and should be the final text
|
|
|
|
a command outputs.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
>>> ui = textui()
|
|
|
|
>>> ui.print_count(1, '%d goose', '%d geese')
|
|
|
|
-------
|
|
|
|
1 goose
|
|
|
|
-------
|
|
|
|
>>> ui.print_count(['Don', 'Sue'], 'Found %d user', 'Found %d users')
|
|
|
|
-------------
|
|
|
|
Found 2 users
|
|
|
|
-------------
|
|
|
|
|
|
|
|
If ``count`` is not an integer, it must be a list or tuple, and then
|
|
|
|
``len(count)`` is used as the count.
|
|
|
|
"""
|
|
|
|
if type(count) is not int:
|
2009-12-09 10:09:53 -06:00
|
|
|
assert type(count) in (list, tuple, dict)
|
2008-11-12 01:46:04 -06:00
|
|
|
count = len(count)
|
|
|
|
self.print_dashed(
|
|
|
|
self.choose_number(count, singular, plural)
|
|
|
|
)
|
|
|
|
|
2009-01-23 11:33:54 -06:00
|
|
|
def print_error(self, text):
|
|
|
|
print ' ** %s **' % text
|
|
|
|
|
2008-11-14 00:54:34 -06:00
|
|
|
def prompt(self, label, default=None, get_values=None):
|
|
|
|
"""
|
|
|
|
Prompt user for input.
|
|
|
|
"""
|
2008-11-19 04:31:29 -06:00
|
|
|
# TODO: Add tab completion using readline
|
2008-11-14 00:54:34 -06:00
|
|
|
if default is None:
|
2008-11-19 04:31:29 -06:00
|
|
|
prompt = u'%s: ' % label
|
2008-11-14 00:54:34 -06:00
|
|
|
else:
|
2008-11-19 04:31:29 -06:00
|
|
|
prompt = u'%s [%s]: ' % (label, default)
|
|
|
|
return self.decode(
|
|
|
|
raw_input(self.encode(prompt))
|
|
|
|
)
|
2008-11-14 00:54:34 -06:00
|
|
|
|
2008-11-18 12:30:16 -06:00
|
|
|
def prompt_password(self, label):
|
|
|
|
"""
|
2009-05-26 15:49:13 -05:00
|
|
|
Prompt user for a password or read it in via stdin depending
|
|
|
|
on whether there is a tty or not.
|
2008-11-18 12:30:16 -06:00
|
|
|
"""
|
|
|
|
try:
|
2009-05-26 15:49:13 -05:00
|
|
|
if sys.stdin.isatty():
|
|
|
|
while True:
|
|
|
|
pw1 = getpass.getpass('%s: ' % label)
|
|
|
|
pw2 = getpass.getpass(
|
|
|
|
_('Enter %(label)s again to verify: ') % dict(label=label)
|
|
|
|
)
|
|
|
|
if pw1 == pw2:
|
|
|
|
return self.decode(pw1)
|
|
|
|
self.print_error( _('Passwords do not match!'))
|
|
|
|
else:
|
|
|
|
return self.decode(sys.stdin.readline().strip())
|
2008-11-18 12:30:16 -06:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
print ''
|
2009-01-23 11:33:54 -06:00
|
|
|
self.print_error(_('Cancelled.'))
|
2008-11-18 12:30:16 -06:00
|
|
|
|
2009-07-10 15:43:47 -05:00
|
|
|
def select_entry(self, entries, format, attrs, display_count=True):
|
|
|
|
"""
|
|
|
|
Display a list of lines in with formatting defined in ``format``.
|
|
|
|
``attrs`` is a list of attributes in the format.
|
|
|
|
|
|
|
|
Prompt user for a selection and return the value (index of
|
|
|
|
``entries`` -1).
|
|
|
|
|
|
|
|
If only one entry is provided then always return 0.
|
|
|
|
|
|
|
|
Return: 0..n for the index of the selected entry
|
|
|
|
-1 if all entries should be displayed
|
|
|
|
-2 to quit, no entries to be displayed
|
|
|
|
"""
|
|
|
|
if not self.env.interactive or not sys.stdout.isatty():
|
|
|
|
return -1
|
|
|
|
|
|
|
|
counter = len(entries)
|
2009-06-09 14:24:23 -05:00
|
|
|
if counter == 0:
|
|
|
|
raise NotFound(reason="No matching entries found")
|
|
|
|
|
2009-07-10 15:43:47 -05:00
|
|
|
i = 1
|
|
|
|
for e in entries:
|
|
|
|
# There is no guarantee that all attrs are in any given
|
|
|
|
# entry
|
|
|
|
d = {}
|
|
|
|
for a in attrs:
|
|
|
|
d[a] = e.get(a, '')
|
|
|
|
self.print_line("%d: %s" % (i, format % d))
|
|
|
|
i = i + 1
|
|
|
|
|
|
|
|
if display_count:
|
|
|
|
self.print_count(entries, 'Found %d match', 'Found %d matches')
|
|
|
|
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
resp = self.prompt("Choose one: (1 - %s), a for all, q to quit" % counter)
|
|
|
|
except EOFError:
|
|
|
|
return -2
|
|
|
|
|
|
|
|
if resp.lower() == "q":
|
|
|
|
return -2
|
|
|
|
if resp.lower() == "a":
|
|
|
|
return -1
|
|
|
|
try:
|
|
|
|
selection = int(resp) - 1
|
|
|
|
if (selection >= 0 and selection < counter):
|
|
|
|
break
|
|
|
|
except:
|
|
|
|
# fall through to the error msg
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.print_line("Please enter a number between 1 and %s" % counter)
|
|
|
|
|
|
|
|
self.print_line('')
|
|
|
|
return selection
|
2008-11-12 01:46:04 -06:00
|
|
|
|
2009-01-27 17:28:50 -06:00
|
|
|
class help(frontend.Command):
|
|
|
|
"""
|
|
|
|
Display help for a command or topic.
|
|
|
|
"""
|
2008-09-08 20:41:15 -05:00
|
|
|
|
2009-05-21 13:31:54 -05:00
|
|
|
takes_args = (Bytes('command?'),)
|
2008-09-08 20:41:15 -05:00
|
|
|
|
2009-12-09 10:09:53 -06:00
|
|
|
has_output = tuple()
|
|
|
|
|
2009-04-27 09:41:49 -05:00
|
|
|
_PLUGIN_BASE_MODULE = 'ipalib.plugins'
|
|
|
|
|
2009-08-04 03:41:11 -05:00
|
|
|
def _get_command_module(self, module):
|
2009-04-27 09:41:49 -05:00
|
|
|
"""
|
2009-08-04 03:41:11 -05:00
|
|
|
Return last part of ``module`` name, or ``None`` if module is this file.
|
|
|
|
|
|
|
|
For example:
|
2009-04-27 09:41:49 -05:00
|
|
|
"""
|
2009-08-04 03:41:11 -05:00
|
|
|
if module == __name__:
|
|
|
|
return
|
|
|
|
return module.split('.')[-1]
|
2009-04-27 09:41:49 -05:00
|
|
|
# get representation in the form of 'base_module.bare_module.command()'
|
|
|
|
r = repr(cmd_plugin_proxy)
|
|
|
|
# skip base module part and the following dot
|
|
|
|
start = r.find(self._PLUGIN_BASE_MODULE)
|
|
|
|
if start == -1:
|
|
|
|
# command module isn't a plugin module, it's a builtin
|
|
|
|
return None
|
|
|
|
start += len(self._PLUGIN_BASE_MODULE) + 1
|
|
|
|
# parse bare module name
|
|
|
|
end = r.find('.', start)
|
|
|
|
return r[start:end]
|
|
|
|
|
2009-01-27 17:28:50 -06:00
|
|
|
def finalize(self):
|
2009-04-27 09:41:49 -05:00
|
|
|
# {plugin_module: [mcl, commands]}
|
|
|
|
self._topics = {}
|
|
|
|
# [builtin_commands]
|
|
|
|
self._builtins = []
|
|
|
|
|
|
|
|
# build help topics
|
|
|
|
for c in self.Command():
|
2009-08-04 03:41:11 -05:00
|
|
|
topic = self._get_command_module(c.module)
|
2009-04-27 09:41:49 -05:00
|
|
|
if topic:
|
|
|
|
self._topics.setdefault(topic, [0]).append(c)
|
|
|
|
# compute maximum length of command in topic
|
|
|
|
mcl = max((self._topics[topic][0], len(c.name)))
|
|
|
|
self._topics[topic][0] = mcl
|
|
|
|
else:
|
|
|
|
self._builtins.append(c)
|
|
|
|
|
|
|
|
# compute maximum topic length
|
|
|
|
self._mtl = max(
|
2009-08-04 03:41:11 -05:00
|
|
|
len(s) for s in (self._topics.keys() + [c.name for c in self._builtins])
|
2009-01-27 17:28:50 -06:00
|
|
|
)
|
2009-04-27 09:41:49 -05:00
|
|
|
|
2009-01-27 17:28:50 -06:00
|
|
|
super(help, self).finalize()
|
|
|
|
|
|
|
|
def run(self, key):
|
2009-06-12 12:26:15 -05:00
|
|
|
name = from_cli(key)
|
|
|
|
if key is None or name == "topics":
|
2009-04-27 09:41:49 -05:00
|
|
|
self.print_topics()
|
2008-11-12 11:15:24 -06:00
|
|
|
return
|
2009-04-27 09:41:49 -05:00
|
|
|
if name in self._topics:
|
|
|
|
self.print_commands(name)
|
|
|
|
elif name in self.Command:
|
|
|
|
cmd = self.Command[name]
|
|
|
|
print 'Purpose: %s' % cmd.doc
|
|
|
|
self.Backend.cli.build_parser(cmd).print_help()
|
2009-06-12 12:26:15 -05:00
|
|
|
elif name == "commands":
|
|
|
|
mcl = max(len(s) for s in (self.Command))
|
|
|
|
for cname in self.Command:
|
|
|
|
cmd = self.Command[cname]
|
2009-11-19 08:53:39 -06:00
|
|
|
print '%s %s' % (to_cli(cmd.name).ljust(mcl), cmd.summary)
|
2009-04-27 09:41:49 -05:00
|
|
|
else:
|
|
|
|
raise HelpError(topic=name)
|
|
|
|
|
|
|
|
def print_topics(self):
|
|
|
|
topics = sorted(self._topics.keys())
|
|
|
|
|
|
|
|
print 'Usage: ipa [global-options] COMMAND ...'
|
|
|
|
print ''
|
|
|
|
print 'Built-in commands:'
|
|
|
|
for c in self._builtins:
|
2009-11-19 08:53:39 -06:00
|
|
|
print ' %s %s' % (to_cli(c.name).ljust(self._mtl), c.summary)
|
2009-04-27 09:41:49 -05:00
|
|
|
print ''
|
|
|
|
print 'Help topics:'
|
|
|
|
for t in topics:
|
|
|
|
m = '%s.%s' % (self._PLUGIN_BASE_MODULE, t)
|
|
|
|
doc = (sys.modules[m].__doc__ or '').strip().split('\n', 1)[0]
|
|
|
|
print ' %s %s' % (to_cli(t).ljust(self._mtl), doc)
|
2008-11-12 11:15:24 -06:00
|
|
|
print ''
|
2009-04-27 09:41:49 -05:00
|
|
|
print 'Try `ipa --help` for a list of global options.'
|
2008-11-12 11:15:24 -06:00
|
|
|
|
2009-04-27 09:41:49 -05:00
|
|
|
def print_commands(self, topic):
|
|
|
|
mcl = self._topics[topic][0]
|
|
|
|
commands = self._topics[topic][1:]
|
|
|
|
m = '%s.%s' % (self._PLUGIN_BASE_MODULE, topic)
|
|
|
|
doc = (sys.modules[m].__doc__ or '').strip()
|
|
|
|
|
|
|
|
print doc
|
|
|
|
print ''
|
|
|
|
print 'Related commands:'
|
|
|
|
for c in commands:
|
2009-11-19 08:53:39 -06:00
|
|
|
print ' %s %s' % (to_cli(c.name).ljust(mcl), c.summary)
|
2008-11-12 11:15:24 -06:00
|
|
|
|
2008-10-27 15:48:02 -05:00
|
|
|
|
2009-10-13 23:23:19 -05:00
|
|
|
class console(frontend.Command):
|
2008-11-12 01:46:04 -06:00
|
|
|
"""Start the IPA interactive Python console."""
|
2008-08-26 19:25:33 -05:00
|
|
|
|
2009-12-09 10:09:53 -06:00
|
|
|
has_output = tuple()
|
|
|
|
|
2008-09-23 22:10:35 -05:00
|
|
|
def run(self):
|
2008-08-26 19:25:33 -05:00
|
|
|
code.interact(
|
2008-09-04 00:18:14 -05:00
|
|
|
'(Custom IPA interactive Python console)',
|
2008-08-26 19:25:33 -05:00
|
|
|
local=dict(api=self.api)
|
|
|
|
)
|
|
|
|
|
2008-09-23 22:10:35 -05:00
|
|
|
|
2009-10-13 23:23:19 -05:00
|
|
|
class show_api(frontend.Command):
|
2008-09-24 00:35:40 -05:00
|
|
|
'Show attributes on dynamic API object'
|
|
|
|
|
|
|
|
takes_args = ('namespaces*',)
|
|
|
|
|
|
|
|
def run(self, namespaces):
|
|
|
|
if namespaces is None:
|
|
|
|
names = tuple(self.api)
|
|
|
|
else:
|
|
|
|
for name in namespaces:
|
|
|
|
if name not in self.api:
|
2009-04-20 12:58:26 -05:00
|
|
|
raise NoSuchNamespaceError(name=name)
|
2008-09-24 00:35:40 -05:00
|
|
|
names = namespaces
|
|
|
|
lines = self.__traverse(names)
|
2008-09-08 16:37:02 -05:00
|
|
|
ml = max(len(l[1]) for l in lines)
|
2008-12-17 18:21:25 -06:00
|
|
|
self.Backend.textui.print_name('run')
|
2008-09-24 00:35:40 -05:00
|
|
|
first = True
|
2008-09-08 16:37:02 -05:00
|
|
|
for line in lines:
|
2008-09-24 00:35:40 -05:00
|
|
|
if line[0] == 0 and not first:
|
2008-09-08 16:37:02 -05:00
|
|
|
print ''
|
2008-09-24 00:35:40 -05:00
|
|
|
if first:
|
|
|
|
first = False
|
2008-09-08 16:37:02 -05:00
|
|
|
print '%s%s %r' % (
|
|
|
|
' ' * line[0],
|
|
|
|
line[1].ljust(ml),
|
|
|
|
line[2],
|
|
|
|
)
|
2008-09-24 00:35:40 -05:00
|
|
|
if len(lines) == 1:
|
|
|
|
s = '1 attribute shown.'
|
|
|
|
else:
|
|
|
|
s = '%d attributes show.' % len(lines)
|
2008-12-17 18:21:25 -06:00
|
|
|
self.Backend.textui.print_dashed(s)
|
2008-09-24 00:35:40 -05:00
|
|
|
|
|
|
|
def __traverse(self, names):
|
2008-09-08 16:37:02 -05:00
|
|
|
lines = []
|
2008-09-24 00:35:40 -05:00
|
|
|
for name in names:
|
2008-09-08 16:37:02 -05:00
|
|
|
namespace = self.api[name]
|
2008-09-24 00:35:40 -05:00
|
|
|
self.__traverse_namespace('%s' % name, namespace, lines)
|
2008-09-08 16:37:02 -05:00
|
|
|
return lines
|
|
|
|
|
|
|
|
def __traverse_namespace(self, name, namespace, lines, tab=0):
|
|
|
|
lines.append((tab, name, namespace))
|
|
|
|
for member_name in namespace:
|
|
|
|
member = namespace[member_name]
|
|
|
|
lines.append((tab + 1, member_name, member))
|
|
|
|
if not hasattr(member, '__iter__'):
|
|
|
|
continue
|
|
|
|
for n in member:
|
|
|
|
attr = member[n]
|
2008-09-08 16:44:53 -05:00
|
|
|
if isinstance(attr, plugable.NameSpace) and len(attr) > 0:
|
2008-09-08 16:37:02 -05:00
|
|
|
self.__traverse_namespace(n, attr, lines, tab + 2)
|
|
|
|
|
2008-08-26 19:25:33 -05:00
|
|
|
|
2008-09-23 23:44:52 -05:00
|
|
|
cli_application_commands = (
|
|
|
|
help,
|
|
|
|
console,
|
2008-09-24 00:35:40 -05:00
|
|
|
show_api,
|
2008-09-23 23:44:52 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2009-01-27 17:28:50 -06:00
|
|
|
class Collector(object):
|
2009-01-28 14:05:26 -06:00
|
|
|
def __init__(self):
|
2009-01-27 17:28:50 -06:00
|
|
|
object.__setattr__(self, '_Collector__options', {})
|
2008-09-04 01:33:57 -05:00
|
|
|
|
|
|
|
def __setattr__(self, name, value):
|
2009-01-28 14:05:26 -06:00
|
|
|
if name in self.__options:
|
|
|
|
v = self.__options[name]
|
|
|
|
if type(v) is tuple:
|
|
|
|
value = v + (value,)
|
|
|
|
else:
|
|
|
|
value = (v, value)
|
|
|
|
self.__options[name] = value
|
2008-09-04 01:33:57 -05:00
|
|
|
object.__setattr__(self, name, value)
|
|
|
|
|
|
|
|
def __todict__(self):
|
2009-01-27 17:28:50 -06:00
|
|
|
return dict(self.__options)
|
2008-09-04 01:33:57 -05:00
|
|
|
|
|
|
|
|
2009-01-28 14:05:26 -06:00
|
|
|
class cli(backend.Executioner):
|
2008-10-27 20:21:49 -05:00
|
|
|
"""
|
2009-01-28 14:05:26 -06:00
|
|
|
Backend plugin for executing from command line interface.
|
2008-10-27 20:21:49 -05:00
|
|
|
"""
|
|
|
|
|
2009-01-28 14:05:26 -06:00
|
|
|
def run(self, argv):
|
|
|
|
if len(argv) == 0:
|
|
|
|
self.Command.help()
|
2008-11-14 00:29:35 -06:00
|
|
|
return
|
2009-01-28 17:12:49 -06:00
|
|
|
self.create_context()
|
2009-01-28 14:05:26 -06:00
|
|
|
(key, argv) = (argv[0], argv[1:])
|
2009-01-28 17:12:49 -06:00
|
|
|
name = from_cli(key)
|
|
|
|
if name not in self.Command:
|
|
|
|
raise CommandError(name=key)
|
|
|
|
cmd = self.Command[name]
|
2009-01-28 14:05:26 -06:00
|
|
|
kw = self.parse(cmd, argv)
|
|
|
|
if self.env.interactive:
|
2008-11-18 14:43:43 -06:00
|
|
|
self.prompt_interactively(cmd, kw)
|
2009-11-05 09:15:47 -06:00
|
|
|
self.load_files(cmd, kw)
|
2009-07-10 15:43:47 -05:00
|
|
|
try:
|
|
|
|
result = self.execute(name, **kw)
|
|
|
|
if callable(cmd.output_for_cli):
|
|
|
|
for param in cmd.params():
|
|
|
|
if param.password and param.name in kw:
|
|
|
|
del kw[param.name]
|
|
|
|
(args, options) = cmd.params_2_args_options(**kw)
|
2009-06-09 14:24:23 -05:00
|
|
|
rv = cmd.output_for_cli(self.api.Backend.textui, result, *args, **options)
|
|
|
|
if rv:
|
|
|
|
return rv
|
|
|
|
else:
|
|
|
|
return 0
|
2009-07-10 15:43:47 -05:00
|
|
|
finally:
|
|
|
|
self.destroy_context()
|
2008-09-04 02:18:26 -05:00
|
|
|
|
2009-01-28 14:05:26 -06:00
|
|
|
def parse(self, cmd, argv):
|
2008-09-04 01:33:57 -05:00
|
|
|
parser = self.build_parser(cmd)
|
2009-01-28 14:05:26 -06:00
|
|
|
(collector, args) = parser.parse_args(argv, Collector())
|
|
|
|
options = collector.__todict__()
|
2009-01-22 18:03:48 -06:00
|
|
|
kw = cmd.args_options_2_params(*args, **options)
|
2009-01-23 11:33:54 -06:00
|
|
|
return dict(self.parse_iter(cmd, kw))
|
2009-01-22 18:03:48 -06:00
|
|
|
|
2009-01-28 14:05:26 -06:00
|
|
|
# FIXME: Probably move decoding to Command, use same method regardless of
|
|
|
|
# request source:
|
2009-01-23 11:33:54 -06:00
|
|
|
def parse_iter(self, cmd, kw):
|
|
|
|
"""
|
|
|
|
Decode param values if appropriate.
|
|
|
|
"""
|
2009-01-22 18:03:48 -06:00
|
|
|
for (key, value) in kw.iteritems():
|
|
|
|
param = cmd.params[key]
|
|
|
|
if isinstance(param, Bytes):
|
|
|
|
yield (key, value)
|
|
|
|
else:
|
2009-01-28 14:05:26 -06:00
|
|
|
yield (key, self.Backend.textui.decode(value))
|
2008-09-04 01:33:57 -05:00
|
|
|
|
|
|
|
def build_parser(self, cmd):
|
|
|
|
parser = optparse.OptionParser(
|
2009-01-28 14:05:26 -06:00
|
|
|
usage=' '.join(self.usage_iter(cmd))
|
2008-09-04 01:33:57 -05:00
|
|
|
)
|
2008-09-10 10:31:34 -05:00
|
|
|
for option in cmd.options():
|
2008-11-18 12:30:16 -06:00
|
|
|
kw = dict(
|
2008-10-13 21:31:10 -05:00
|
|
|
dest=option.name,
|
2008-09-04 01:33:57 -05:00
|
|
|
help=option.doc,
|
|
|
|
)
|
2009-01-28 14:05:26 -06:00
|
|
|
if option.password and self.env.interactive:
|
2008-11-18 12:30:16 -06:00
|
|
|
kw['action'] = 'store_true'
|
2009-11-26 08:53:07 -06:00
|
|
|
elif option.type is bool and option.autofill:
|
2008-11-17 19:50:30 -06:00
|
|
|
if option.default is True:
|
2008-11-18 12:30:16 -06:00
|
|
|
kw['action'] = 'store_false'
|
2008-11-17 19:50:30 -06:00
|
|
|
else:
|
2008-11-18 12:30:16 -06:00
|
|
|
kw['action'] = 'store_true'
|
|
|
|
else:
|
2009-01-16 12:07:21 -06:00
|
|
|
kw['metavar'] = metavar=option.__class__.__name__.upper()
|
2009-06-10 14:07:24 -05:00
|
|
|
if option.cli_short_name:
|
|
|
|
o = optparse.make_option('-%s' % option.cli_short_name, '--%s' % to_cli(option.cli_name), **kw)
|
|
|
|
else:
|
|
|
|
o = optparse.make_option('--%s' % to_cli(option.cli_name), **kw)
|
2008-10-21 08:31:06 -05:00
|
|
|
parser.add_option(o)
|
2008-09-04 01:33:57 -05:00
|
|
|
return parser
|
|
|
|
|
2009-01-28 14:05:26 -06:00
|
|
|
def usage_iter(self, cmd):
|
2008-10-03 15:13:50 -05:00
|
|
|
yield 'Usage: %%prog [global-options] %s' % to_cli(cmd.name)
|
2008-09-09 21:02:26 -05:00
|
|
|
for arg in cmd.args():
|
2009-01-22 20:40:02 -06:00
|
|
|
if arg.password:
|
2008-11-18 14:43:43 -06:00
|
|
|
continue
|
2008-10-13 21:31:10 -05:00
|
|
|
name = to_cli(arg.cli_name).upper()
|
2008-09-08 20:41:15 -05:00
|
|
|
if arg.multivalue:
|
|
|
|
name = '%s...' % name
|
|
|
|
if arg.required:
|
|
|
|
yield name
|
|
|
|
else:
|
|
|
|
yield '[%s]' % name
|
|
|
|
|
2009-01-28 14:05:26 -06:00
|
|
|
def prompt_interactively(self, cmd, kw):
|
2009-01-27 17:28:50 -06:00
|
|
|
"""
|
|
|
|
Interactively prompt for missing or invalid values.
|
|
|
|
|
|
|
|
By default this method will only prompt for *required* Param that
|
|
|
|
have a missing or invalid value. However, if
|
2009-01-28 14:05:26 -06:00
|
|
|
``self.env.prompt_all`` is ``True``, this method will prompt for any
|
|
|
|
params that have a missing values, even if the param is optional.
|
2009-01-27 17:28:50 -06:00
|
|
|
"""
|
|
|
|
for param in cmd.params():
|
2009-02-26 16:27:06 -06:00
|
|
|
if param.password and (
|
|
|
|
kw.get(param.name, False) is True or param.name in cmd.args
|
|
|
|
):
|
|
|
|
kw[param.name] = \
|
|
|
|
self.Backend.textui.prompt_password(param.cli_name)
|
|
|
|
elif param.name not in kw:
|
|
|
|
if param.autofill:
|
|
|
|
kw[param.name] = param.get_default(**kw)
|
|
|
|
elif param.required or self.env.prompt_all:
|
|
|
|
default = param.get_default(**kw)
|
|
|
|
error = None
|
|
|
|
while True:
|
|
|
|
if error is not None:
|
2009-12-09 10:09:53 -06:00
|
|
|
print '>>> %s: %s' % (param.label, error)
|
|
|
|
raw = self.Backend.textui.prompt(param.label, default)
|
2009-02-26 16:27:06 -06:00
|
|
|
try:
|
|
|
|
value = param(raw, **kw)
|
|
|
|
if value is not None:
|
|
|
|
kw[param.name] = value
|
|
|
|
break
|
2009-04-20 12:58:26 -05:00
|
|
|
except ValidationError, e:
|
2009-02-26 16:27:06 -06:00
|
|
|
error = e.error
|
2009-01-27 17:28:50 -06:00
|
|
|
|
2009-11-05 09:15:47 -06:00
|
|
|
def load_files(self, cmd, kw):
|
|
|
|
"""
|
|
|
|
Load files from File parameters.
|
|
|
|
|
|
|
|
This has to be done after all required parameters have been read
|
|
|
|
(i.e. after prompt_interactively has or would have been called)
|
|
|
|
AND before they are passed to the command. This is because:
|
|
|
|
1) we need to be sure no more files are going to be added
|
|
|
|
2) we load files from the machine where the command was executed
|
|
|
|
3) the webUI will use a different way of loading files
|
|
|
|
"""
|
|
|
|
for p in cmd.params():
|
|
|
|
if isinstance(p, File):
|
|
|
|
if p.name in kw:
|
|
|
|
try:
|
|
|
|
f = open(kw[p.name], 'r')
|
|
|
|
raw = f.read()
|
|
|
|
f.close()
|
|
|
|
except IOError, e:
|
|
|
|
raise ValidationError(
|
|
|
|
name=to_cli(p.cli_name),
|
|
|
|
error='%s: %s:' % (kw[p.name], e[1])
|
|
|
|
)
|
|
|
|
elif p.stdin_if_missing:
|
|
|
|
try:
|
|
|
|
raw = sys.stdin.read()
|
|
|
|
except IOError, e:
|
2009-11-12 16:34:19 -06:00
|
|
|
raise ValidationError(
|
2009-11-05 09:15:47 -06:00
|
|
|
name=to_cli(p.cli_name), error=e[1]
|
|
|
|
)
|
|
|
|
kw[p.name] = self.Backend.textui.decode(raw)
|
|
|
|
|
2009-01-27 17:28:50 -06:00
|
|
|
|
|
|
|
cli_plugins = (
|
|
|
|
cli,
|
|
|
|
textui,
|
2009-01-28 14:05:26 -06:00
|
|
|
console,
|
2009-01-27 17:28:50 -06:00
|
|
|
help,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def run(api):
|
2009-01-28 14:05:26 -06:00
|
|
|
error = None
|
2009-01-27 17:28:50 -06:00
|
|
|
try:
|
2009-10-13 12:28:00 -05:00
|
|
|
(options, argv) = api.bootstrap_with_global_options(context='cli')
|
2009-01-27 17:28:50 -06:00
|
|
|
for klass in cli_plugins:
|
|
|
|
api.register(klass)
|
|
|
|
api.load_plugins()
|
|
|
|
api.finalize()
|
2009-06-09 14:24:23 -05:00
|
|
|
sys.exit(api.Backend.cli.run(argv))
|
2009-01-27 17:28:50 -06:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
print ''
|
|
|
|
api.log.info('operation aborted')
|
|
|
|
except PublicError, e:
|
|
|
|
error = e
|
2009-04-28 16:46:23 -05:00
|
|
|
except StandardError, e:
|
2009-01-27 17:28:50 -06:00
|
|
|
api.log.exception('%s: %s', e.__class__.__name__, str(e))
|
|
|
|
error = InternalError()
|
2009-01-28 14:05:26 -06:00
|
|
|
if error is not None:
|
|
|
|
assert isinstance(error, PublicError)
|
|
|
|
api.log.error(error.strerror)
|
2009-06-09 14:24:23 -05:00
|
|
|
sys.exit(error.rval)
|