Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / history.py @ eb46e9a1

History | View | Annotate | Download (7.3 kB)

1
#!/usr/bin/env python
2

    
3
# Copyright 2012-2013 GRNET S.A. All rights reserved.
4
#
5
# Redistribution and use in source and binary forms, with or
6
# without modification, are permitted provided that the following
7
# conditions are met:
8
#
9
#   1. Redistributions of source code must retain the above
10
#      copyright notice, this list of conditions and the following
11
#      disclaimer.
12
#
13
#   2. Redistributions in binary form must reproduce the above
14
#      copyright notice, this list of conditions and the following
15
#      disclaimer in the documentation and/or other materials
16
#      provided with the distribution.
17
#
18
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
# POSSIBILITY OF SUCH DAMAGE.
30
#
31
# The views and conclusions contained in the software and
32
# documentation are those of the authors and should not be
33
# interpreted as representing official policies, either expressed
34
# or implied, of GRNET S.A.
35

    
36
from kamaki.cli.command_tree import CommandTree
37
from kamaki.cli.argument import IntArgument, ValueArgument
38
from kamaki.cli.argument import ArgumentParseManager
39
from kamaki.cli.history import History
40
from kamaki.cli import command
41
from kamaki.cli.commands import _command_init, errors
42
from kamaki.cli import exec_cmd, print_error_message
43
from kamaki.cli.errors import CLIError, raiseCLIError
44
from kamaki.cli.utils import split_input
45
from kamaki.clients import ClientError
46

    
47

    
48
history_cmds = CommandTree('history', 'Kamaki command history')
49
_commands = [history_cmds]
50

    
51

    
52
def _get_num_list(num_str):
53
    if num_str.startswith('-'):
54
        num1, sep, num2 = num_str[1:].partition('-')
55
        num1 = '-%s' % num1
56
    else:
57
        num1, sep, num2 = num_str.partition('-')
58
    (num1, num2) = (num1.strip(), num2.strip())
59
    try:
60
        num1 = (-int(num1[1:])) if num1.startswith('-') else int(num1)
61
    except ValueError as e:
62
        raiseCLIError(e, 'Invalid id %s' % num1)
63
    if sep:
64
        try:
65
            num2 = (-int(num2[1:])) if num2.startswith('-') else int(num2)
66
            num2 += 1 if num2 > 0 else 0
67
        except ValueError as e:
68
            raiseCLIError(e, 'Invalid id %s' % num2)
69
    else:
70
        num2 = (1 + num1) if num1 else 0
71
    return [i for i in range(num1, num2)]
72

    
73

    
74
class _init_history(_command_init):
75
    @errors.generic.all
76
    @errors.history.init
77
    def _run(self):
78
        self.history = History(self.config.get_global('history_file'))
79

    
80
    def main(self):
81
        self._run()
82

    
83

    
84
@command(history_cmds)
85
class history_show(_init_history):
86
    """Show intersession command history
87
    ---
88
    - With no parameters : pick all commands in history records
89
    - With:
90
    .   1.  <order-id> : pick the <order-id>th command
91
    .   2.  <order-id-1>-<order-id-2> : pick all commands ordered in the range
92
    .       [<order-id-1> - <order-id-2>]
93
    .   - the above can be mixed and repeated freely, separated by spaces
94
    .       e.g. pick 2 4-7 -3
95
    .   - Use negative integers to count from the end of the list, e.g.:
96
    .       -2 means : the command before the last one
97
    .       -2-5 means : last 2 commands + the first 5
98
    .       -5--2 means : the last 5 commands except the last 2
99
    """
100

    
101
    arguments = dict(
102
        limit=IntArgument(
103
            'number of lines to show',
104
            ('-n', '--numner'),
105
            default=0),
106
        match=ValueArgument('show lines that match given terms', '--match')
107
    )
108

    
109
    @errors.generic.all
110
    def _run(self, *cmd_ids):
111
        ret = self.history.get(match_terms=self['match'], limit=self['limit'])
112

    
113
        if not cmd_ids:
114
            print(''.join(ret))
115
            return
116

    
117
        num_list = []
118
        for num_str in cmd_ids:
119
            num_list += _get_num_list(num_str)
120

    
121
        for cmd_id in num_list:
122
            try:
123
                cur_id = int(cmd_id)
124
                if cur_id:
125
                    print(ret[cur_id - (1 if cur_id > 0 else 0)][:-1])
126
            except IndexError as e2:
127
                raiseCLIError(e2, 'Command id out of 1-%s range' % len(ret))
128

    
129
    def main(self, *cmd_ids):
130
        super(self.__class__, self)._run()
131
        self._run(*cmd_ids)
132

    
133

    
134
@command(history_cmds)
135
class history_clean(_init_history):
136
    """Clean up history (permanent)"""
137

    
138
    @errors.generic.all
139
    def _run(self):
140
        self.history.clean()
141

    
142
    def main(self):
143
        super(self.__class__, self)._run()
144
        self._run()
145

    
146

    
147
@command(history_cmds)
148
class history_run(_init_history):
149
    """Run previously executed command(s)
150
    Use with:
151
    .   1.  <order-id> : pick the <order-id>th command
152
    .   2.  <order-id-1>-<order-id-2> : pick all commands ordered in the range
153
    .       [<order-id-1> - <order-id-2>]
154
    .   - Use negative integers to count from the end of the list, e.g.:
155
    .       -2 means : the command before the last one
156
    .       -2-5 means : last 2 commands + the first 5
157
    .       -5--2 mean
158
    .   - to find order ids for commands try   /history show.
159
    """
160

    
161
    _cmd_tree = None
162

    
163
    def __init__(self, arguments={}, auth_base=None, cmd_tree=None):
164
        super(self.__class__, self).__init__(arguments, auth_base=auth_base)
165
        self._cmd_tree = cmd_tree
166

    
167
    @errors.generic.all
168
    def _run_from_line(self, line):
169
        terms = split_input(line)
170
        cmd, args = self._cmd_tree.find_best_match(terms)
171
        if not cmd.is_command:
172
            return
173
        try:
174
            instance = cmd.cmd_class(
175
                self.arguments,
176
                auth_base=getattr(self, 'auth_base', None))
177
            instance.config = self.config
178
            prs = ArgumentParseManager(
179
                cmd.path.split(),
180
                dict(instance.arguments))
181
            prs.syntax = '%s %s' % (
182
                cmd.path.replace('_', ' '),
183
                cmd.cmd_class.syntax)
184
            prs.parse(args)
185
            exec_cmd(instance, prs.unparsed, prs.parser.print_help)
186
        except (CLIError, ClientError) as err:
187
            print_error_message(err)
188
        except Exception as e:
189
            print('Execution of [ %s ] failed' % line)
190
            print('\t%s' % e)
191

    
192
    @errors.generic.all
193
    @errors.history._get_cmd_ids
194
    def _get_cmd_ids(self, cmd_ids):
195
        cmd_id_list = []
196
        for cmd_str in cmd_ids:
197
            cmd_id_list += _get_num_list(cmd_str)
198
        return cmd_id_list
199

    
200
    @errors.generic.all
201
    def _run(self, *command_ids):
202
        cmd_list = self._get_cmd_ids(command_ids)
203
        for cmd_id in cmd_list:
204
            r = self.history.retrieve(cmd_id)
205
            try:
206
                print('< %s >' % r[:-1])
207
            except (TypeError, KeyError):
208
                continue
209
            if self._cmd_tree:
210
                r = r[len('kamaki '):-1] if r.startswith('kamaki ') else r[:-1]
211
                self._run_from_line(r)
212

    
213
    def main(self, *command_ids):
214
        super(self.__class__, self)._run()
215
        self._run(*command_ids)