Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (14.5 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
            "-r", "--runtime",
109
            dest="printout_runtime",
110
            action="store_true",
111
            default=False,
112
            help=("Append runtime values in printout.")),
113
        make_option(
114
            "-p", "--printout",
115
            dest="printout",
116
            action="store_true",
117
            default=False,
118
            help=("Create a printout of settings as comment blocks.")),
119
        make_option(
120
            "-f", "--printout-files",
121
            dest="printout_files",
122
            action="store",
123
            metavar="SETTINGS-DIRECTORY",
124
            help=("Create a printout of settings grouped "
125
                  "in files by category.")),
126
    )
127

    
128
    def mk_filter_all(self, param):
129
        if param != '':
130
            m = "Invalid filter parameter '{0}'".format(param)
131
            raise AssertionError(m)
132

    
133
        def filter_all(setting):
134
            return True
135
        return filter_all
136

    
137
    def mk_filter_category(self, category):
138
        if category not in Setting.Catalogs['categories']:
139
            m = "Unknown category '{0}'".format(category)
140
            raise CommandError(m)
141

    
142
        def filter_category(setting):
143
            return setting.category == category
144
        return filter_category
145

    
146
    def mk_filter_type(self, setting_type):
147
        if setting_type not in Setting.Catalogs['types']:
148
            m = "Unknown type '{0}'".format(setting_type)
149
            raise CommandError(m)
150

    
151
        def filter_type(setting):
152
            return setting.setting_type == setting_type
153
        return filter_type
154

    
155
    def mk_filter_hidden(self, hidden):
156
        if hidden != '':
157
            m = "Invalid hidden filter parameter '{0}'".format(hidden)
158
            raise AssertionError(m)
159

    
160
        def filter_hidden(setting):
161
            return not setting.export
162
        return filter_hidden
163

    
164
    def mk_filter_configured(self, configured):
165
        if configured != '':
166
            m = "Invalid configured filter parameter '{0}'".format(configured)
167
            raise AssertionError(m)
168

    
169
        def filter_configured(setting):
170
            return setting.configured_value is not NoValue
171
        return filter_configured
172

    
173
    def mk_filter_setting(self, setting_name):
174
        if not Setting.is_valid_setting_name(setting_name):
175
            m = "Invalid setting name '{0}'".format(setting_name)
176
            raise AssertionError(m)
177

    
178
        def filter_setting(setting):
179
            return setting.setting_name == setting_name
180
        return filter_setting
181

    
182
    def mk_negate(self, filter_method):
183
        def negated_filter(*args, **kwargs):
184
            return not filter_method(*args, **kwargs)
185
        return negated_filter
186

    
187
    _mk_filters = {
188
        'all': 'mk_filter_all',
189
        'category': 'mk_filter_category',
190
        'type': 'mk_filter_type',
191
        'configured': 'mk_filter_configured',
192
        'hidden': 'mk_filter_hidden',
193
        'setting': 'mk_filter_setting',
194
    }
195

    
196
    def parse_selection_filters(self, string):
197
        filters = []
198
        for term in string.split(','):
199
            key, sep, value = term.partition('=')
200
            if key.startswith('!'):
201
                negate = True
202
                key = key[1:]
203
            else:
204
                negate = False
205

    
206
            if key not in self._mk_filters:
207
                if not Setting.is_valid_setting_name(key):
208
                    return None
209
                value = key
210
                key = 'setting'
211

    
212
            mk_filter_method = getattr(self, self._mk_filters[key])
213
            if not mk_filter_method:
214
                m = "Unknown filter '{0}'".format(key)
215
                raise CommandError(m)
216

    
217
            filter_method = mk_filter_method(value)
218
            if negate:
219
                filter_method = self.mk_negate(filter_method)
220
            filters.append(filter_method)
221
        return filters
222

    
223
    def sort_lexical(display_settings):
224
        return sorted(display_settings.iteritems())
225

    
226
    def sort_source(display_settings):
227
        registry = Setting.Catalogs['registry']
228
        sortable = []
229
        for name, setting in display_settings.iteritems():
230
            sortable.append((registry[name], name, setting))
231
        sortable.sort()
232
        return [(t[1], t[2]) for t in sortable]
233

    
234
    def sort_category_lexical(display_settings):
235
        sortable = []
236
        for name, setting in display_settings.iteritems():
237
            sortable.append((setting.category, name, setting))
238
        sortable.sort()
239
        return [(t[1], t[2]) for t in sortable]
240

    
241
    def sort_category_source(display_settings):
242
        registry = Setting.Catalogs['registry']
243
        sortable = []
244
        for name, setting in display_settings.iteritems():
245
            sortable.append((setting.category, registry[name], setting))
246
        sortable.sort()
247
        return [(t[1], t[2]) for t in sortable]
248

    
249
    sort_methods = {
250
        'lexical': sort_lexical,
251
        'source': sort_source,
252
        'category-lexical': sort_category_lexical,
253
        'category-source': sort_category_source,
254
    }
255

    
256
    def display_console(self, display_settings_list, options):
257
        for name, setting in display_settings_list:
258
            format_str = "{name} = {value}"
259
            flags = []
260

    
261
            if setting.runtime_value == setting.default_value:
262
                flags.append('default')
263

    
264
            if setting.configured_value is not NoValue:
265
                flags.append('configured')
266
                if setting.runtime_value != setting.configured_value:
267
                    flags.append('runtime')
268

    
269
            value = setting.runtime_value
270
            default_value = setting.default_value
271
            example_value = setting.example_value
272
            dependencies = setting.dependencies
273
            description = setting.description
274
            sep = " # "
275
            eol = ""
276

    
277
            if options['display_multiline']:
278
                value = pformat(value)
279
                default_value = pformat(default_value)
280
                example_value = pformat(example_value)
281
                dependencies = pformat(dependencies)
282
                sep = "\n  # "
283
                description = (sep + '  ').join(wrap(description, 70))
284
                eol = "\n"
285

    
286
            format_args = {'name': name,
287
                           'value': value,
288
                           'example': example_value,
289
                           'default': default_value,
290
                           'description': description,
291
                           'dependencies': dependencies,
292
                           'serial': setting.serial,
293
                           'configured_depth': setting.configured_depth,
294
                           'configured_source': setting.configured_source,
295
                           'configure_callback': setting.configure_callback,
296
                           'flags': ','.join(flags)}
297

    
298
            sep = "\n  # " if options['display_multiline'] else " # "
299
            eol = "\n" if options['display_multiline'] else ""
300

    
301
            if options['display_details'] or options['display_defaults']:
302
                format_str += sep + "Default: {default}"
303

    
304
            if options['display_details'] or options['display_status']:
305
                format_str += sep + "Flags: {flags}"
306

    
307
            if options['display_details']:
308
                format_str += sep + "Description: {description}"
309
                format_str += sep + "Dependencies: {dependencies}"
310
                format_str += sep + "Depth: {configured_depth}"
311
                format_str += sep + "Serial: {serial}"
312
                format_str += sep + "Callback: {configure_callback}"
313
                format_str += sep + "Source: {configured_source}"
314

    
315
            line = format_str.format(**format_args) + eol
316
            print line
317

    
318
    def display_printout(self, display_settings_list, runtime=False):
319
        for name, setting in display_settings_list:
320
            comment = setting.present_as_comment(runtime=runtime) + '\n'
321
            print comment
322

    
323
    def printout_files(self, display_settings_list, path, runtime=False):
324
        if not isdir(path):
325
            m = "Cannot find directory '{path}'".format(path=path)
326
            raise CommandError(m)
327

    
328
        category_depths = {}
329
        for name, setting in Setting.Catalogs['settings'].iteritems():
330
            category = setting.category
331
            if (category not in category_depths or
332
                    setting.configured_depth > category_depths[category]):
333
                category_depths[category] = setting.configured_depth
334

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

    
353
    def handle(self, *args, **options):
354
        if args:
355
            raise CommandError("This command takes no arguments. Only options")
356

    
357
        selection_strings = options["selection_strings"]
358
        if not selection_strings:
359
            selection_strings = ['configured']
360

    
361
        and_filters = [self.parse_selection_filters(s)
362
                       for s in selection_strings]
363

    
364
        settings_dict = Setting.Catalogs['settings']
365
        display_settings = {}
366
        for name, setting in settings_dict.items():
367
            if all(any(or_filter(setting) for or_filter in and_filter)
368
                   for and_filter in and_filters):
369
                display_settings[name] = setting
370

    
371
        sort_order = options['sort_order']
372
        if sort_order not in self.sort_methods:
373
            m = "Unknown sort method '{0}'".format(sort_order)
374
            raise CommandError(m)
375

    
376
        sort_method = self.sort_methods[sort_order]
377
        display_settings_list = sort_method(display_settings)
378

    
379
        if options['printout']:
380
            self.display_printout(display_settings_list,
381
                                  options['printout_runtime'])
382
        elif options['printout_files']:
383
            self.printout_files(display_settings_list,
384
                                options['printout_files'],
385
                                options['printout_runtime'])
386
        else:
387
            self.display_console(display_settings_list, options)