~lamont/maas/bug-1614584

« back to all changes in this revision

Viewing changes to src/provisioningserver/drivers/__init__.py

  • Committer: MAAS Lander
  • Author(s): Blake Rouse
  • Date: 2016-11-30 15:12:47 UTC
  • mfrom: (5570.1.7 reorg-power-drivers)
  • Revision ID: maas_lander-20161130151247-bkx57gqe5auighsd
[r=newell-jensen][bug=][author=blake-rouse] Remove the src/provisioningserver/power/schema.py. Place the schema for each driver in the driver class. This makes it easier to add drivers without requiring code in two places.

Remove all the diskless code, as that used some of the old schema stuff and it was never used.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright 2014-2016 Canonical Ltd.  This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
3
 
4
 
"""Hardware Drivers."""
 
4
"""Drivers."""
5
5
 
6
6
__all__ = [
7
7
    "Architecture",
8
8
    "ArchitectureRegistry",
9
 
    "BootResource",
10
9
    ]
11
10
 
12
 
from abc import (
13
 
    ABCMeta,
14
 
    abstractmethod,
15
 
)
16
 
 
17
11
from jsonschema import validate
18
 
from provisioningserver.power.schema import JSON_POWER_TYPE_PARAMETERS
19
12
from provisioningserver.utils import typed
20
13
from provisioningserver.utils.registry import Registry
21
14
 
 
15
 
 
16
class IP_EXTRACTOR_PATTERNS:
 
17
    """Commonly used patterns IP extractor patterns."""
 
18
 
 
19
    # Use the entire string as the value.
 
20
    IDENTITY = '^(?P<address>.+?)$'
 
21
 
 
22
    # The typical URL pattern. Extracts address field as the value.
 
23
    # The given URL has an address component that is one of:
 
24
    # (1) an IPv6 IP address surrounded by []
 
25
    # (2) an IPv4 IP address (no [])
 
26
    # (3) a name
 
27
    # (4) the empty string.
 
28
    # Cases 2 and 3 are processed in the regex by excluding all []/: from the
 
29
    # allowed values.  The need to verify the [] around the IPv6 IP address
 
30
    # introduces a goodly amount of complexity due to looking forward/backward
 
31
    # to determine if [] is ok/expected.
 
32
    # The resulting address is simply the IP address (v6 or v4), or a hostname.
 
33
    URL = (r'^'
 
34
           r'((?P<schema>.+?)://)?'
 
35
           r'((?P<user>.+?)(:(?P<password>.*?))?@)?'
 
36
 
 
37
           r'(?:\[(?=[0-9a-fA-F]*:[0-9a-fA-F.:]+\]))?'
 
38
           r'(?P<address>(?:(?:[^\[\]/:]*(?!\]))|'
 
39
           r'(?:(?<=\[)[0-9a-fA-F:.]+(?=\]))))\]?'
 
40
 
 
41
           r'(:(?P<port>\d+?))?'
 
42
           r'(?P<path>/.*?)?'
 
43
           r'(?P<query>[?].*?)?'
 
44
           r'$'
 
45
           )
 
46
 
 
47
 
 
48
# Python REGEX pattern for extracting IP address from parameter field.
 
49
# The field_name tells the extractor which power_parameter field to use.
 
50
# Name the address field 'address' in your Python regex pattern.
 
51
# The pattern will be used as in 're.match(pattern, field_value)'.
 
52
IP_EXTRACTOR_SCHEMA = {
 
53
    'title': "IP Extractor Configuration",
 
54
    'type': 'object',
 
55
    'properties': {
 
56
        'field_name': {
 
57
            'type': 'string',
 
58
        },
 
59
        'pattern': {
 
60
            'type': 'string',
 
61
        },
 
62
    },
 
63
    "dependencies": {
 
64
        "field_name": ["pattern"],
 
65
        "pattern": ["field_name"]
 
66
    },
 
67
}
 
68
 
22
69
# JSON schema representing the Django choices format as JSON; an array of
23
70
# 2-item arrays.
24
71
CHOICE_FIELD_SCHEMA = {
52
99
        'required': {
53
100
            'type': 'boolean',
54
101
        },
 
102
        # 'bmc' or 'node': Whether value lives on bmc (global) or node/device.
 
103
        'scope': {
 
104
            'type': 'string',
 
105
        },
55
106
        'choices': CHOICE_FIELD_SCHEMA,
56
107
        'default': {
57
108
            'type': 'string',
76
127
            'type': 'array',
77
128
            'items': SETTING_PARAMETER_FIELD_SCHEMA,
78
129
        },
 
130
        'ip_extractor': IP_EXTRACTOR_SCHEMA,
 
131
        'queryable': {
 
132
            'type': 'boolean',
 
133
        },
 
134
        'missing_packages': {
 
135
            'type': 'array',
 
136
            'items': {
 
137
                'type': 'string',
 
138
            },
 
139
        },
79
140
    },
80
141
    'required': ['name', 'description', 'fields'],
81
142
}
82
143
 
83
144
 
 
145
def make_ip_extractor(field_name, pattern=IP_EXTRACTOR_PATTERNS.IDENTITY):
 
146
    return {
 
147
        'field_name': field_name,
 
148
        'pattern': pattern,
 
149
    }
 
150
 
 
151
 
 
152
class SETTING_SCOPE:
 
153
    BMC = "bmc"
 
154
    NODE = "node"
 
155
 
 
156
 
84
157
def make_setting_field(
85
158
        name, label, field_type=None, choices=None, default=None,
86
 
        required=False):
 
159
        required=False, scope=SETTING_SCOPE.BMC):
87
160
    """Helper function for building a JSON setting parameters field.
88
161
 
89
162
    :param name: The name of the field.
101
174
    :type default: string
102
175
    :param required: Whether or not a value for the field is required.
103
176
    :type required: boolean
 
177
    :param scope: 'bmc' or 'node' - Whether value is bmc or node specific.
 
178
        Defaults to 'bmc'.
 
179
    :type scope: string
104
180
    """
105
 
    if field_type not in ('string', 'mac_address', 'choice'):
 
181
    if field_type not in ('string', 'mac_address', 'choice', 'password'):
106
182
        field_type = 'string'
107
183
    if choices is None:
108
184
        choices = []
109
185
    validate(choices, CHOICE_FIELD_SCHEMA)
110
186
    if default is None:
111
187
        default = ""
112
 
    field = {
 
188
    if scope not in (SETTING_SCOPE.BMC, SETTING_SCOPE.NODE):
 
189
        scope = SETTING_SCOPE.BMC
 
190
    return {
113
191
        'name': name,
114
192
        'label': label,
115
193
        'required': required,
116
194
        'field_type': field_type,
117
195
        'choices': choices,
118
196
        'default': default,
 
197
        'scope': scope,
119
198
    }
120
 
    return field
121
 
 
122
 
 
123
 
def validate_settings(setting_fields):
124
 
    """Helper that validates that the fields adhere to the JSON schema."""
125
 
    validate(setting_fields, JSON_SETTING_SCHEMA)
126
 
 
127
 
 
128
 
def gen_power_types():
129
 
    from provisioningserver.drivers.power import power_drivers_by_name
130
 
    for power_type in JSON_POWER_TYPE_PARAMETERS:
131
 
        driver = power_drivers_by_name.get(power_type['name'])
132
 
        if driver is not None:
133
 
            power_type['missing_packages'] = driver.detect_missing_packages()
134
 
        yield power_type
135
199
 
136
200
 
137
201
class Architecture:
158
222
        self.kernel_options = kernel_options
159
223
 
160
224
 
161
 
class BootResource(metaclass=ABCMeta):
162
 
    """Abstraction of ephemerals and pxe resources required for a hardware
163
 
    driver.
164
 
 
165
 
    This resource is responsible for importing and reporting on
166
 
    what is potentially available in relation to a cluster controller.
167
 
    """
168
 
 
169
 
    def __init__(self, name):
170
 
        self.name = name
171
 
 
172
 
    @abstractmethod
173
 
    def import_resources(self, at_location, filter=None):
174
 
        """Import the specified resources.
175
 
 
176
 
        :param at_location: URL to a Simplestreams index or a local path
177
 
            to a directory containing boot resources.
178
 
        :param filter: A simplestreams filter.
179
 
            e.g. "release=trusty label=beta-2 arch=amd64"
180
 
            This is ignored if the location is a local path, all resources
181
 
            at the location will be imported.
182
 
        TBD: How to provide progress information.
183
 
        """
184
 
 
185
 
    @abstractmethod
186
 
    def describe_resources(self, at_location):
187
 
        """Enumerate all the boot resources.
188
 
 
189
 
        :param at_location: URL to a Simplestreams index or a local path
190
 
            to a directory containing boot resources.
191
 
 
192
 
        :return: a list of dictionaries describing the available resources,
193
 
            which will need to be imported so the driver can use them.
194
 
        [
195
 
            {
196
 
                "release": "trusty",
197
 
                "arch": "amd64",
198
 
                "label": "beta-2",
199
 
                "size": 12344556,
200
 
            }
201
 
            ,
202
 
        ]
203
 
        """
204
 
 
205
 
 
206
 
class HardwareDiscoverContext(metaclass=ABCMeta):
207
 
 
208
 
    @abstractmethod
209
 
    def startDiscovery(self):
210
 
        """TBD"""
211
 
 
212
 
    @abstractmethod
213
 
    def stopDiscovery(self):
214
 
        """TBD"""
215
 
 
216
 
 
217
225
class ArchitectureRegistry(Registry):
218
226
    """Registry for architecture classes."""
219
227
 
226
234
        return None
227
235
 
228
236
 
229
 
class BootResourceRegistry(Registry):
230
 
    """Registry for boot resource classes."""
231
 
 
232
 
 
233
237
builtin_architectures = [
234
238
    Architecture(name="i386/generic", description="i386"),
235
239
    Architecture(name="amd64/generic", description="amd64"),