Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / command_shell.py @ c270fe96

History | View | Annotate | Download (6.4 kB)

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

    
34
from cmd import Cmd
35
from new import instancemethod
36
from os import popen
37
from argparse import ArgumentParser
38
from kamaki.cli import _update_parser, _exec_cmd
39
from .errors import CLIError
40
from .argument import _arguments
41
from .utils import magenta, print_dict
42
from sys import stdout
43
from .history import History
44

    
45
def _fix_arguments():
46
        _arguments.pop('version', None)
47
        _arguments.pop('options', None)
48
        _arguments.pop('history', None)
49

    
50
class Shell(Cmd):
51
        """Kamaki interactive shell"""
52
        _prefix = '['
53
        _suffix = ']:'
54
        cmd_tree = None
55
        _history = None
56
        undoc_header='interactive shell commands:'
57

    
58
        def greet(self, version):
59
                print('kamaki v%s - Interactive Shell\n\t(exit or ^D to exit)\n'%version)
60
        def set_prompt(self, new_prompt):
61
                self.prompt = '[%s]:'%new_prompt
62

    
63
        def do_exit(self, line):
64
                print('')
65
                return True
66

    
67
        def do_shell(self, line):
68
                output = popen(line).read()
69
                print(output)
70

    
71
        @property 
72
        def path(self):
73
                if self._cmd:
74
                        return _cmd.path
75
                return ''
76

    
77
        @classmethod
78
        def _register_method(self, method, name):
79
                self.__dict__[name]=method
80
        @classmethod
81
        def _unregister_method(self, name):
82
                try:
83
                        self.__dict__.pop(name)
84
                except KeyError:
85
                        pass
86
        def _roll_command(self, cmd_path):
87
                for subname in self.cmd_tree.get_subnames(cmd_path):
88
                        self._unregister_method('do_%s'%subname)
89
                        self._unregister_method('complete_%s'%subname)
90
                        self._unregister_method('help_%s'%subname)
91

    
92
        @classmethod 
93
        def _backup(self):
94
                return dict(self.__dict__)
95
        @classmethod
96
        def _restore(self, oldcontext):
97
                self.__dict__= oldcontext
98

    
99
        def _push_in_command(self, cmd_path):
100
                cmd = self.cmd_tree.get_command(cmd_path)
101
                _cmd_tree = self.cmd_tree
102
                _history = self._history
103

    
104
                def do_method(self, line):
105
                        """ Template for all cmd.Cmd methods of the form do_<cmd name>
106
                                Parse cmd + args and decide to execute or change context
107
                                <cmd> <term> <term> <args> is always parsed to the most specific cmd path
108
                                even if cmd_term_term is not a terminal path
109
                        """
110
                        if _history:
111
                                _history.add(' '.join([cmd.path.replace('_',' '), line]))
112
                        subcmd, cmd_args = cmd.parse_out(line.split())
113
                        active_terms = [cmd.name]+subcmd.path.split('_')[len(cmd.path.split('_')):]
114
                        subname = '_'.join(active_terms)
115
                        cmd_parser = ArgumentParser(subname, add_help=False)
116
                        cmd_parser.description = subcmd.help
117

    
118
                        #exec command or change context
119
                        if subcmd.is_command:#exec command
120
                                cls = subcmd.get_class()
121
                                instance = cls(dict(_arguments))
122
                                cmd_parser.prog= cmd_parser.prog.replace('_', ' ')+' '+cls.syntax
123
                                _update_parser(cmd_parser, instance.arguments)
124
                                if '-h' in cmd_args or '--help' in cmd_args:
125
                                        cmd_parser.print_help()
126
                                        return
127
                                parsed, unparsed = cmd_parser.parse_known_args(cmd_args)
128

    
129
                                for name, arg in instance.arguments.items():
130
                                        arg.value = getattr(parsed, name, arg.default)
131
                                _exec_cmd(instance, unparsed, cmd_parser.print_help)
132
                        elif ('-h' in cmd_args or '--help' in cmd_args) \
133
                        or len(cmd_args):#print options
134
                                print('%s: %s'%(subname, subcmd.help))
135
                                options = {}
136
                                for sub in subcmd.get_subcommands():
137
                                        options[sub.name] = sub.help
138
                                print_dict(options)
139
                        else:#change context
140
                                new_context = self
141
                                backup_context = self._backup()
142
                                old_prompt = self.prompt
143
                                new_context._roll_command(cmd.parent_path)
144
                                new_context.set_prompt(subcmd.path.replace('_',' '))
145
                                newcmds = [subcmd for subcmd in subcmd.get_subcommands()]
146
                                for subcmd in newcmds:
147
                                        new_context._push_in_command(subcmd.path)
148
                                new_context.cmdloop()
149
                                self.prompt = old_prompt
150
                                #when new context is over, roll back to the old one
151
                                self._restore(backup_context)
152
                self._register_method(do_method, 'do_%s'%cmd.name)
153

    
154
                def help_method(self):
155
                        print('%s (%s -h for more options)'%(cmd.help, cmd.name))
156
                self._register_method(help_method, 'help_%s'%cmd.name)
157

    
158
                def complete_method(self, text, line, begidx, endidx):
159
                        subcmd, cmd_args = cmd.parse_out(line.split()[1:])
160
                        if subcmd.is_command:
161
                                cls = subcmd.get_class()
162
                                instance = cls(dict(_arguments))
163
                                empty, sep, subname = subcmd.path.partition(cmd.path)
164
                                cmd_name = '%s %s'%(cmd.name,subname.replace('_',' '))
165
                                print('\n%s\nSyntax:\t%s %s'%(cls.description,cmd_name,cls.syntax))
166
                                cmd_args={}
167
                                for arg in instance.arguments.values():
168
                                        cmd_args[','.join(arg.parsed_name)]=arg.help
169
                                print_dict(cmd_args, ident=14)
170
                                stdout.write('%s %s'%(self.prompt,line))
171
                        return subcmd.get_subnames()
172
                self._register_method(complete_method, 'complete_%s'%cmd.name)
173

    
174
        @property 
175
        def doc_header(self):
176
                hdr = self.prompt.partition(self._prefix)[2].partition(self._suffix)[0].strip()
177
                return '%s commands:'%hdr
178

    
179
        def run(self, path=''):
180
                self._history = History(_arguments['config'].get('history', 'file'))
181
                if len(path):
182
                        cmd = self.cmd_tree.get_command(path)
183
                        intro = cmd.path.replace('_', ' ')
184
                else:
185
                        intro = self.cmd_tree.name
186

    
187
                for subcmd in self.cmd_tree.get_subcommands(path):
188
                        self._push_in_command(subcmd.path)
189

    
190
                self.set_prompt(intro)
191
                self.cmdloop()