Statistics
| Branch: | Tag: | Revision:

root / snf-webproject / synnefo / webproject / management / commands / settings.py @ d63a86d6

History | View | Annotate | Download (14.2 kB)

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

    
30
from synnefo.settings.setup import Setting
31
from django.core.management.base import BaseCommand, CommandError
32
from optparse import make_option
33
from os.path import isdir
34
from pprint import pformat
35
from textwrap import wrap
36

    
37
NoValue = Setting.NoValue
38
available_categories = sorted(Setting.Catalogs['categories'].keys())
39
available_types = sorted(Setting.Catalogs['types'].keys())
40

    
41

    
42
class Command(BaseCommand):
43
    help = """Display synnefo settings
44

45
    Example:
46
    settings --select type=mandatory,configured
47
    settings --select hidden --select type=default,
48
    """
49

    
50
    option_list = BaseCommand.option_list + (
51
        make_option(
52
            "-s", "--select",
53
            type='string',
54
            dest="selection_strings",
55
            action="append",
56
            metavar='[!]selection,...',
57
            help=("List settings that match any comma-separated criteria:"
58
                  "  all\n"
59
                  "  type=<setting type>\n"
60
                  "  category=<setting category>\n"
61
                  "  hidden\n"
62
                  "  configured\n"
63
                  "  <SETTING_NAME>\n"
64
                  "\n"
65
                  "Available types: {types}\n"
66
                  "Available categories: {categories}\n"
67
                  "\n"
68
                  "Multiple --select options yield the intersection "
69
                  "of their results\n"
70
                  "Prepending '!' negates the selection criterion\n"
71
                  ).format(types=available_types,
72
                           categories=available_categories)),
73
        make_option(
74
            "-o", "--sort-order",
75
            type='string',
76
            dest="sort_order",
77
            action="store",
78
            default='lexical',
79
            help=("Order settings. Available orderings: "
80
                  "['lexical', 'source', "
81
                  "'category-lexical', 'category-source']")),
82
        make_option(
83
            "-d", "--defaults",
84
            dest="display_defaults",
85
            action="store_true",
86
            default=False,
87
            help=("Include setting default value.")),
88

    
89
        make_option(
90
            "-t", "--status",
91
            dest="display_status",
92
            action="store_true",
93
            default=False,
94
            help=("Display internal setting status.")),
95
        make_option(
96
            "-1", "--oneline",
97
            dest="display_multiline",
98
            action="store_false",
99
            default=True,
100
            help=("Display setting in multiple lines")),
101
        make_option(
102
            "-l", "--details",
103
            dest="display_details",
104
            action="store_true",
105
            default=False,
106
            help=("Display full setting details")),
107
        make_option(
108
            "-p", "--printout",
109
            dest="printout",
110
            action="store_true",
111
            default=False,
112
            help=("Create a printout of settings as comment blocks.")),
113
        make_option(
114
            "-f", "--printout-files",
115
            dest="printout_files",
116
            action="store",
117
            metavar="SETTINGS-DIRECTORY",
118
            help=("Create a printout of settings grouped "
119
                  "in files by category.")),
120
    )
121

    
122
    def mk_filter_all(self, param):
123
        if param != '':
124
            m = "Invalid filter parameter '{0}'".format(param)
125
            raise AssertionError(m)
126

    
127
        def filter_all(setting):
128
            return True
129
        return filter_all
130

    
131
    def mk_filter_category(self, category):
132
        if category not in Setting.Catalogs['categories']:
133
            m = "Unknown category '{0}'".format(category)
134
            raise CommandError(m)
135

    
136
        def filter_category(setting):
137
            return setting.category == category
138
        return filter_category
139

    
140
    def mk_filter_type(self, setting_type):
141
        if setting_type not in Setting.Catalogs['types']:
142
            m = "Unknown type '{0}'".format(setting_type)
143
            raise CommandError(m)
144

    
145
        def filter_type(setting):
146
            return setting.setting_type == setting_type
147
        return filter_type
148

    
149
    def mk_filter_hidden(self, hidden):
150
        if hidden != '':
151
            m = "Invalid hidden filter parameter '{0}'".format(hidden)
152
            raise AssertionError(m)
153

    
154
        def filter_hidden(setting):
155
            return not setting.export
156
        return filter_hidden
157

    
158
    def mk_filter_configured(self, configured):
159
        if configured != '':
160
            m = "Invalid configured filter parameter '{0}'".format(configured)
161
            raise AssertionError(m)
162

    
163
        def filter_configured(setting):
164
            return setting.configured_value is not NoValue
165
        return filter_configured
166

    
167
    def mk_filter_setting(self, setting_name):
168
        if not Setting.is_valid_setting_name(setting_name):
169
            m = "Invalid setting name '{0}'".format(setting_name)
170
            raise AssertionError(m)
171

    
172
        def filter_setting(setting):
173
            return setting.setting_name == setting_name
174
        return filter_setting
175

    
176
    def mk_negate(self, filter_method):
177
        def negated_filter(*args, **kwargs):
178
            return not filter_method(*args, **kwargs)
179
        return negated_filter
180

    
181
    _mk_filters = {
182
        'all': 'mk_filter_all',
183
        'category': 'mk_filter_category',
184
        'type': 'mk_filter_type',
185
        'configured': 'mk_filter_configured',
186
        'hidden': 'mk_filter_hidden',
187
        'setting': 'mk_filter_setting',
188
    }
189

    
190
    def parse_selection_filters(self, string):
191
        filters = []
192
        for term in string.split(','):
193
            key, sep, value = term.partition('=')
194
            if key.startswith('!'):
195
                negate = True
196
                key = key[1:]
197
            else:
198
                negate = False
199

    
200
            if key not in self._mk_filters:
201
                if not Setting.is_valid_setting_name(key):
202
                    return None
203
                value = key
204
                key = 'setting'
205

    
206
            mk_filter_method = getattr(self, self._mk_filters[key])
207
            if not mk_filter_method:
208
                m = "Unknown filter '{0}'".format(key)
209
                raise CommandError(m)
210

    
211
            filter_method = mk_filter_method(value)
212
            if negate:
213
                filter_method = self.mk_negate(filter_method)
214
            filters.append(filter_method)
215
        return filters
216

    
217
    def sort_lexical(display_settings):
218
        return sorted(display_settings.iteritems())
219

    
220
    def sort_source(display_settings):
221
        registry = Setting.Catalogs['registry']
222
        sortable = []
223
        for name, setting in display_settings.iteritems():
224
            sortable.append((registry[name], name, setting))
225
        sortable.sort()
226
        return [(t[1], t[2]) for t in sortable]
227

    
228
    def sort_category_lexical(display_settings):
229
        sortable = []
230
        for name, setting in display_settings.iteritems():
231
            sortable.append((setting.category, name, setting))
232
        sortable.sort()
233
        return [(t[1], t[2]) for t in sortable]
234

    
235
    def sort_category_source(display_settings):
236
        registry = Setting.Catalogs['registry']
237
        sortable = []
238
        for name, setting in display_settings.iteritems():
239
            sortable.append((setting.category, registry[name], setting))
240
        sortable.sort()
241
        return [(t[1], t[2]) for t in sortable]
242

    
243
    sort_methods = {
244
        'lexical': sort_lexical,
245
        'source': sort_source,
246
        'category-lexical': sort_category_lexical,
247
        'category-source': sort_category_source,
248
    }
249

    
250
    def display_console(self, display_settings_list, options):
251
        for name, setting in display_settings_list:
252
            format_str = "{name} = {value}"
253
            flags = []
254

    
255
            if setting.runtime_value == setting.default_value:
256
                flags.append('default')
257

    
258
            if setting.configured_value is not NoValue:
259
                flags.append('configured')
260
                if setting.runtime_value != setting.configured_value:
261
                    flags.append('runtime')
262

    
263
            value = setting.runtime_value
264
            default_value = setting.default_value
265
            example_value = setting.example_value
266
            dependencies = setting.dependencies
267
            description = setting.description
268
            sep = " # "
269
            eol = ""
270

    
271
            if options['display_multiline']:
272
                value = pformat(value)
273
                default_value = pformat(default_value)
274
                example_value = pformat(example_value)
275
                dependencies = pformat(dependencies)
276
                sep = "\n  # "
277
                description = (sep + '  ').join(wrap(description, 70))
278
                eol = "\n"
279

    
280
            format_args = {'name': name,
281
                           'value': value,
282
                           'example': example_value,
283
                           'default': default_value,
284
                           'description': description,
285
                           'dependencies': dependencies,
286
                           'serial': setting.serial,
287
                           'configured_depth': setting.configured_depth,
288
                           'configured_source': setting.configured_source,
289
                           'configure_callback': setting.configure_callback,
290
                           'flags': ','.join(flags)}
291

    
292
            sep = "\n  # " if options['display_multiline'] else " # "
293
            eol = "\n" if options['display_multiline'] else ""
294

    
295
            if options['display_details'] or options['display_defaults']:
296
                format_str += sep + "Default: {default}"
297

    
298
            if options['display_details'] or options['display_status']:
299
                format_str += sep + "Flags: {flags}"
300

    
301
            if options['display_details']:
302
                format_str += sep + "Description: {description}"
303
                format_str += sep + "Dependencies: {dependencies}"
304
                format_str += sep + "Depth: {configured_depth}"
305
                format_str += sep + "Serial: {serial}"
306
                format_str += sep + "Callback: {configure_callback}"
307
                format_str += sep + "Source: {configured_source}"
308

    
309
            line = format_str.format(**format_args) + eol
310
            print line
311

    
312
    def display_printout(self, display_settings_list):
313
        for name, setting in display_settings_list:
314
            comment = setting.present_as_comment() + '\n'
315
            print comment
316

    
317
    def printout_files(self, display_settings_list, path):
318
        if not isdir(path):
319
            m = "Cannot find directory '{path}'".format(path=path)
320
            raise CommandError(m)
321

    
322
        category_depths = {}
323
        for name, setting in Setting.Catalogs['settings'].iteritems():
324
            category = setting.category
325
            if (category not in category_depths or
326
                    setting.configured_depth > category_depths[category]):
327
                category_depths[category] = setting.configured_depth
328

    
329
        old_filepath = None
330
        conffile = None
331
        filepath = None
332
        for name, setting in display_settings_list:
333
            category = setting.category
334
            category_depth = 10 * category_depths[category]
335
            filepath = '{path}/{depth}-{category}.conf'
336
            filepath = filepath.format(path=path,
337
                                       depth=category_depth,
338
                                       category=category)
339
            if filepath != old_filepath:
340
                if conffile:
341
                    conffile.close()
342
                conffile = open(filepath, "a")
343
                old_filepath = filepath
344
            conffile.write(setting.present_as_comment())
345
            conffile.write('\n')
346

    
347
    def handle(self, *args, **options):
348
        if args:
349
            raise CommandError("This command takes no arguments. Only options")
350

    
351
        selection_strings = options["selection_strings"]
352
        if not selection_strings:
353
            selection_strings = ['configured']
354

    
355
        and_filters = [self.parse_selection_filters(s)
356
                       for s in selection_strings]
357

    
358
        settings_dict = Setting.Catalogs['settings']
359
        display_settings = {}
360
        for name, setting in settings_dict.items():
361
            if all(any(or_filter(setting) for or_filter in and_filter)
362
                   for and_filter in and_filters):
363
                display_settings[name] = setting
364

    
365
        sort_order = options['sort_order']
366
        if sort_order not in self.sort_methods:
367
            m = "Unknown sort method '{0}'".format(sort_order)
368
            raise CommandError(m)
369

    
370
        sort_method = self.sort_methods[sort_order]
371
        display_settings_list = sort_method(display_settings)
372

    
373
        if options['printout']:
374
            self.display_printout(display_settings_list)
375
        elif options['printout_files']:
376
            self.printout_files(display_settings_list,
377
                                options['printout_files'])
378
        else:
379
            self.display_console(display_settings_list, options)