summaryrefslogtreecommitdiff
path: root/test/sanity/validate-modules/schema.py
blob: 008e53bc06957cade23e5c1d5f4897fcaf7bd925 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 Matt Martz <matt@sivel.net>
# Copyright (C) 2015 Rackspace US, Inc.
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

from voluptuous import PREVENT_EXTRA, Any, Required, Schema
from ansible.module_utils.six import string_types
list_string_types = list(string_types)

suboption_schema = Schema(
    {
        Required('description'): Any(list_string_types, *string_types),
        'required': bool,
        'choices': list,
        'aliases': Any(list, *string_types),
        'version_added': Any(float, *string_types),
        'default': Any(None, float, int, bool, list, dict, *string_types),
        # Note: Types are strings, not literal bools, such as True or False
        'type': Any(None, "bool")
    },
    extra=PREVENT_EXTRA
)

# This generates list of dicts with keys from string_types and suboption_schema value
# for example in Python 3: {str: suboption_schema}
list_dict_suboption_schema = [{str_type: suboption_schema} for str_type in string_types]

option_schema = Schema(
    {
        Required('description'): Any(list_string_types, *string_types),
        'required': bool,
        'choices': list,
        'aliases': Any(list, *string_types),
        'version_added': Any(float, *string_types),
        'default': Any(None, float, int, bool, list, dict, *string_types),
        'suboptions': Any(None, *list_dict_suboption_schema),
        # Note: Types are strings, not literal bools, such as True or False
        'type': Any(None, "bool")
    },
    extra=PREVENT_EXTRA
)

# This generates list of dicts with keys from string_types and option_schema value
# for example in Python 3: {str: option_schema}
list_dict_option_schema = [{str_type: option_schema} for str_type in string_types]


def return_schema(data):

    return_schema_dict = {
        Required('description'): Any(list, *string_types),
        Required('returned'): Any(*string_types),
        Required('type'): Any('string', 'list', 'boolean', 'dict', 'complex', 'bool', 'float', 'int', 'dictionary', 'str'),
        'sample': Any(None, list, dict, int, float, *string_types),
        'example': Any(None, list, dict, int, float, *string_types)
    }
    if isinstance(data, dict):
        if 'type' in data and (data['type'] == 'complex'):
            # This will just check if the schema has a 'contains' value.
            # It won't recursively validate the contents of the 'contains' field
            additional_schema = {
                Required('contains'): Any(dict, list, *string_types)
            }
            return_schema_dict.update(additional_schema)

    return Schema(
        return_schema_dict,
        extra=PREVENT_EXTRA
    )

def doc_schema(module_name):
    if module_name.startswith('_'):
        module_name = module_name[1:]
    return Schema(
        {
            Required('module'): module_name,
            'deprecated': Any(*string_types),
            Required('short_description'): Any(*string_types),
            Required('description'): Any(list_string_types, *string_types),
            Required('version_added'): Any(float, *string_types),
            Required('author'): Any(None, list_string_types, *string_types),
            'notes': Any(None, list_string_types),
            'requirements': list_string_types,
            'todo': Any(None, list_string_types, *string_types),
            'options': Any(None, *list_dict_option_schema),
            'extends_documentation_fragment': Any(list_string_types, *string_types)
        },
        extra=PREVENT_EXTRA
    )


def metadata_schema(deprecated):
    valid_status = Any('stableinterface', 'preview', 'deprecated', 'removed')
    if deprecated:
        valid_status = Any('deprecated')

    return Schema(
        {
            Required('status'): [valid_status],
            Required('metadata_version'): '1.0',
            Required('supported_by'): Any('core', 'community', 'curated')
        }
    )



# Things to add soon
####################
# 1) Recursively validate `type: complex` fields
#    This will improve documentation, though require fair amount of module tidyup

# Possible Future Enhancements
##############################

# 1) Don't allow empty options for choices, aliases, etc
# 2) If type: bool ensure choices isn't set - perhaps use Exclusive
# 3) both version_added should be quoted floats
# 4) Use Recursive Schema: https://github.com/alecthomas/voluptuous/issues/128 though don't allow two layers

#  Tool that takes JSON and generates RETURN skeleton (needs to support complex structures)