backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / operator / command.py
1 from collections import namedtuple
2 import json
3 import logging
4 import pprint
5 import re
6 import six
7
8 (STATUS_OK, STATUS_ERROR) = range(2)
9
10 CommandsResponse = namedtuple('CommandsResponse', ['status', 'value'])
11
12 LOG = logging.getLogger('bgpspeaker.operator.command')
13
14
15 def default_help_formatter(quick_helps):
16     """Apply default formatting for help messages
17
18         :param quick_helps: list of tuples containing help info
19      """
20     ret = ''
21     for line in quick_helps:
22         cmd_path, param_hlp, cmd_hlp = line
23         ret += ' '.join(cmd_path) + ' '
24         if param_hlp:
25             ret += param_hlp + ' '
26         ret += '- ' + cmd_hlp + '\n'
27     return ret
28
29
30 class Command(object):
31     """Command class is used as a node in tree of commands.
32
33     Each command can do some action or have some sub-commands, just like in IOS
34     Command with it's sub-commands form tree.
35     Each command can have one or more parameters. Parameters have to be
36     distinguishable from sub-commands.
37         One can inject dependency into command Cmd(api=my_object).
38     This dependency will be injected to every sub-command. And can be used
39     to interact with model/data etc.
40         Example of path in command tree `show count all`.
41     """
42
43     help_msg = ''
44     param_help_msg = ''
45     command = ''
46     cli_resp_line_template = '{0}: {1}\n'
47
48     def __init__(self, api=None, parent=None,
49                  help_formatter=default_help_formatter,
50                  resp_formatter_name='cli'):
51         """:param api: object which is saved as self.api
52                  and re-injected to every sub-command. You can use it to
53                  manipulate your model from inside Commands'
54            :param parent: parent command instance.
55            :param help_formatter: function used to format
56                 output of '?'command. Is re-injected to every
57                 sub-command as well.
58            :param resp_formatter_name: used to select function to format
59                 output of _action. cli_resp_formatter and json_resp_formatter
60                 are defined by default, but you can define your own formatters.
61                 If you use custom formatter(not cli nor json) remember to
62                 implement it for every sub-command.
63         """
64
65         self.resp_formatter_name = resp_formatter_name
66
67         if hasattr(self, resp_formatter_name + '_resp_formatter'):
68             self.resp_formatter = \
69                 getattr(self, resp_formatter_name + '_resp_formatter')
70         else:
71             self.resp_formatter = self.cli_resp_formatter
72
73         self.api = api
74         self.parent_cmd = parent
75         self.help_formatter = help_formatter
76         if not hasattr(self, 'subcommands'):
77             self.subcommands = {}
78
79     def __call__(self, params):
80         """You run command by calling it.
81
82         :param params: As params you give list of subcommand names
83             and params to final subcommand. Kind of like in
84             cisco ios cli, ie. show int eth1 / 1, where show is command,
85             int subcommand and eth1 / 1 is param for subcommand.
86         :return: returns tuple of CommandsResponse and class of
87             sub - command on which _action was called. (last sub - command)
88             CommandsResponse.status is action status,
89             and CommandsResponse.value is formatted response.
90         """
91         if len(params) == 0:
92             return self._action_wrapper([])
93
94         first_param = params[0]
95
96         if first_param == '?':
97             return self.question_mark()
98
99         if first_param in self.subcommands:
100             return self._instantiate_subcommand(first_param)(params[1:])
101
102         return self._action_wrapper(params)
103
104     @classmethod
105     def cli_resp_formatter(cls, resp):
106         """Override this method to provide custom formatting of cli response.
107         """
108         if not resp.value:
109             return ''
110
111         if resp.status == STATUS_OK:
112
113             if type(resp.value) in (str, bool, int, float, six.text_type):
114                 return str(resp.value)
115
116             ret = ''
117             val = resp.value
118             if not isinstance(val, list):
119                 val = [val]
120             for line in val:
121                 for k, v in line.items():
122                     if isinstance(v, dict):
123                         ret += cls.cli_resp_line_template.format(
124                             k, '\n' + pprint.pformat(v)
125                         )
126                     else:
127                         ret += cls.cli_resp_line_template.format(k, v)
128             return ret
129         else:
130             return "Error: {0}".format(resp.value)
131
132     @classmethod
133     def json_resp_formatter(cls, resp):
134         """Override this method to provide custom formatting of json response.
135         """
136         return json.dumps(resp.value)
137
138     @classmethod
139     def dict_resp_formatter(cls, resp):
140         return resp.value
141
142     def _action_wrapper(self, params):
143         filter_params = []
144         if '|' in params:
145             ind = params.index('|')
146             new_params = params[:ind]
147             filter_params = params[ind:]
148             params = new_params
149
150         action_resp = self.action(params)
151         if len(filter_params) > 1:
152             # we don't pass '|' around so filter_params[1:]
153             action_resp = self.filter_resp(action_resp, filter_params[1:])
154         action_resp = CommandsResponse(
155             action_resp.status,
156             self.resp_formatter(action_resp)
157         )
158         return action_resp, self.__class__
159
160     def action(self, params):
161         """Override this method to define what command should do.
162
163         :param params: list of text parameters applied to this command.
164         :return: returns CommandsResponse instance.
165                  CommandsResponse.status can be STATUS_OK or STATUS_ERROR
166                  CommandsResponse.value should be dict or str
167         """
168         return CommandsResponse(STATUS_ERROR, 'Not implemented')
169
170     def filter_resp(self, action_resp, filter_params):
171         """Filter response of action. Used to make printed results more
172         specific
173
174         :param action_resp: named tuple (CommandsResponse)
175             containing response from action.
176         :param filter_params: params used after '|' specific for given filter
177         :return: filtered response.
178         """
179         if action_resp.status == STATUS_OK:
180             try:
181                 return CommandsResponse(
182                     STATUS_OK,
183                     TextFilter.filter(action_resp.value, filter_params)
184                 )
185             except FilterError as e:
186                 return CommandsResponse(STATUS_ERROR, str(e))
187         else:
188             return action_resp
189
190     def question_mark(self):
191         """Shows help for this command and it's sub-commands.
192         """
193         ret = []
194         if self.param_help_msg or len(self.subcommands) == 0:
195             ret.append(self._quick_help())
196
197         if len(self.subcommands) > 0:
198             for k, _ in sorted(self.subcommands.items()):
199                 command_path, param_help, cmd_help = \
200                     self._instantiate_subcommand(k)._quick_help(nested=True)
201                 if command_path or param_help or cmd_help:
202                     ret.append((command_path, param_help, cmd_help))
203
204         return (
205             CommandsResponse(STATUS_OK, self.help_formatter(ret)),
206             self.__class__
207         )
208
209     def _quick_help(self, nested=False):
210         """:param nested: True if help is requested directly for this command
211                     and False when help is requested for a list of possible
212                     completions.
213         """
214         if nested:
215             return self.command_path(), None, self.help_msg
216         else:
217             return self.command_path(), self.param_help_msg, self.help_msg
218
219     def command_path(self):
220         if self.parent_cmd:
221             return self.parent_cmd.command_path() + [self.command]
222         else:
223             return [self.command]
224
225     def _instantiate_subcommand(self, key):
226         return self.subcommands[key](
227             api=self.api,
228             parent=self,
229             help_formatter=self.help_formatter,
230             resp_formatter_name=self.resp_formatter_name
231         )
232
233
234 class TextFilter(object):
235
236     @classmethod
237     def filter(cls, action_resp_value, filter_params):
238         try:
239             action, expected_value = filter_params
240         except ValueError:
241             raise FilterError('Wrong number of filter parameters')
242         if action == 'regexp':
243
244             if isinstance(action_resp_value, list):
245                 resp = list(action_resp_value)
246                 iterator = enumerate(action_resp_value)
247             else:
248                 resp = dict(action_resp_value)
249                 iterator = iter(action_resp_value.items())
250
251             remove = []
252
253             for key, value in iterator:
254                 if not re.search(expected_value, str(value)):
255                     remove.append(key)
256
257             if isinstance(resp, list):
258                 resp = [resp[key] for key, value in enumerate(resp)
259                         if key not in remove]
260             else:
261                 resp = dict([(key, value)
262                              for key, value in resp.items()
263                              if key not in remove])
264
265             return resp
266         else:
267             raise FilterError('Unknown filter')
268
269
270 class FilterError(Exception):
271     pass