moduleinfo.py 8.97 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Copyright (C) 2009  Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

16
17
"""This module holds classes representing modules, commands and
   parameters for use in bindctl"""
18

19
20
import textwrap

21
22
23
try:
    from collections import OrderedDict
except ImportError:
24
    from bindctl.mycollections import OrderedDict
25
26
27
28
29
30
31
32
33
34

# Define value type
STRING_TYPE = "string"
LIST_TYPE = "list"
INT_TYPE = "int"

MODULE_NODE_NAME = 'module'
COMMAND_NODE_NAME = 'command'
PARAM_NODE_NAME = 'param'

35
36
37
# this is used to align the descriptions in help output
CONST_BINDCTL_HELP_INDENT_WIDTH=12

38
39

class ParamInfo:
40
    """One parameter of one command.
41
42
43
44
    Each command parameter has five attributes:
    parameter name, parameter type, parameter value,
    parameter description and paramter's spec(got from
    module spec file). 
45
46
    """
    def __init__(self, name, desc = '', type = STRING_TYPE, 
47
48
                 optional = False, value = '', default_value = '', 
                 param_spec = None):
49
50
51
52
53
54
        self.name = name
        self.type = type
        self.value = value
        self.default_value = default_value                           
        self.desc = desc
        self.is_optional = optional
55
        self.param_spec = param_spec
56
57
58
59
    
    def __str__(self):        
        return str("\t%s <type: %s> \t(%s)" % (self.name, self.type, self.desc))

60
61
62
63
64
65
    def get_name(self):
        return "%s <type: %s>" % (self.name, self.type)

    def get_desc(self):
        return self.desc

66
class CommandInfo:
67
68
    """One command which is provided by one bind10 module, it has zero
       or more parameters
69
70
    """

71
    def __init__(self, name, desc = ""):
72
73
74
75
76
77
78
79
80
81
82
        self.name = name
        self.desc = desc
        self.params = OrderedDict()        
        # Set default parameter "help"
        self.add_param(ParamInfo("help", 
                                  desc = "Get help for command",
                                  optional = True))
                
    def __str__(self):
        return str("%s \t(%s)" % (self.name, self.desc))

83
84
85
86
87
88
    def get_name(self):
        return self.name

    def get_desc(self):
        return self.desc;
    
89
    def add_param(self, paraminfo):
90
        """Add a ParamInfo object to this CommandInfo"""
91
92
93
94
        self.params[paraminfo.name] = paraminfo
        

    def has_param_with_name(self, param_name):
95
        """Returns true if the parameter with param_name exists"""
96
97
98
99
        return param_name in self.params
        

    def get_param_with_name(self, param_name):
100
101
        """Returns the ParamInfo with the given name. Raises a
           KeyError if it doesn't exist"""
102
103
104
105
        return self.params[param_name]
        

    def get_params(self):
106
        """Returns a list of all ParamInfo objects for this CommandInfo"""
107
108
109
110
        return list(self.params.values())
        

    def get_param_names(self):
111
        """Returns a list of the names of all parameters for this command"""
112
113
114
115
        return list(self.params.keys())
        
        
    def get_mandatory_param_names(self):
116
117
        """Returns a list of the names of all mandatory parameters for
           this command"""
118
119
120
121
        all_names = self.params.keys()
        return [name for name in all_names 
                if not self.params[name].is_optional]        
        
122
    def get_param_name_by_position(self, pos, param_count):
123
        """
124
125
126
127
128
129
        Find a proper parameter name for the position 'pos':
        If param_count is equal to the count of mandatory parameters of command,
        and there is some optional parameter, find the first mandatory parameter 
        from the position 'pos' to the end. Else, return the name on position pos.
        (This function will be changed if bindctl command line syntax is changed
        in the future. )
130
        """
131
132
        if type(pos) != int:
            raise KeyError(str(pos) + " is not an integer")
133

134
        else:
135
136
137
138
            params = self.params.copy()
            del params['help']
            count = len(params)
            if (pos >= count):
139
                raise KeyError(str(pos) + " out of range")
140
141
142
143
144
145
146
147
148
149

            mandatory_count = len(self.get_mandatory_param_names())
            param_names = list(params.keys())
            if (param_count == mandatory_count) and (param_count < count):
                while pos < count:
                    if not params[param_names[pos]].is_optional:
                        return param_names[pos]
                    pos += 1
                
                raise KeyError(str(pos) + "parameters have error")
150
            else:
151
                return param_names[pos]
152
153


154
    def command_help(self):
155
        """Prints the help info for this command to stdout"""
156
157
158
159
160
161
162
        print("Command ", self)
        print("\t\thelp (Get help for command)")
                
        params = self.params.copy()
        del params["help"]

        if len(params) == 0:
163
            print("No parameters for the command")
164
165
            return
        
166
        print("\nMandatory parameters:")
167
168
169
        mandatory_infos = []
        for info in params.values():            
            if not info.is_optional:
170
                print("    %s" % info.get_name())
171
                print(textwrap.fill(info.get_desc(),
172
173
                      initial_indent="        ",
                      subsequent_indent="        ",
174
                      width=70))
175
176
177
178
179
                mandatory_infos.append(info)

        optional_infos = [info for info in params.values() 
                          if info not in mandatory_infos]
        if len(optional_infos) > 0:
180
            print("\nOptional parameters:")      
181
            for info in optional_infos:
182
                print("    %s" % info.get_name())
183
                print(textwrap.fill(info.get_desc(),
184
185
                      initial_indent="        ",
                      subsequent_indent="        ",
186
                      width=70))
187
188
189
190


class ModuleInfo:
    """Define the information of one module, include module name, 
191
    module supporting commands.
192
193
    """    
    
194
    def __init__(self, name, desc = ""):
195
196
197
198
        self.name = name
        self.desc = desc
        self.commands = OrderedDict()         
        self.add_command(CommandInfo(name = "help", 
199
                                     desc = "Get help for module"))
200
201
202
        
    def __str__(self):
        return str("%s \t%s" % (self.name, self.desc))
203
204
205
206
207
208
209

    def get_name(self):
        return self.name

    def get_desc(self):
        return self.desc

210
    def add_command(self, command_info):
211
        """Add a CommandInfo to this ModuleInfo."""
212
213
214
        self.commands[command_info.name] = command_info
        
    def has_command_with_name(self, command_name):
215
        """Returns true if this module has a command with the given name."""
216
217
218
        return command_name in self.commands
        
    def get_command_with_name(self, command_name):
219
220
        """Returns the CommandInfo for the command with the given name.
           Raises a KeyError if not found"""
221
222
223
        return self.commands[command_name]
        
    def get_commands(self):
224
        """Returns a list of all CommandInfo objects for this module."""
225
226
227
        return list(self.commands.values())
        
    def get_command_names(self):
228
        """Returns a list of the names of all commands for this module."""
229
230
231
        return list(self.commands.keys())

    def module_help(self):
232
        """Prints the help info for this module to stdout"""
233
        print("Module ", self, "\nAvailable commands:")
234
235
        for k in self.commands.values():
            n = k.get_name()
236
237
            if len(n) >= CONST_BINDCTL_HELP_INDENT_WIDTH:
                print("    %s" % n)
238
                print(textwrap.fill(k.get_desc(),
239
240
241
                      initial_indent="            ",
                      subsequent_indent="    " +
                      " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
242
243
                      width=70))
            else:
244
245
246
247
248
249
250
251
                print(textwrap.fill("%s%s%s" %
                    (k.get_name(),
                     " "*(CONST_BINDCTL_HELP_INDENT_WIDTH - len(k.get_name())),
                     k.get_desc()),
                    initial_indent="    ",
                    subsequent_indent="    " +
                    " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
                    width=70))
252
253
            
    def command_help(self, command):
254
255
        """Prints the help info for the command with the given name.
           Raises KeyError if not found"""
256
        self.commands[command].command_help()    
257