/usr/lib/python3/dist-packages/provisioningserver/drivers/__init__.py is in python3-maas-provisioningserver 2.0.0~beta3+bzr4941-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | # Copyright 2014-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Hardware Drivers."""
__all__ = [
"Architecture",
"ArchitectureRegistry",
"BootResource",
]
from abc import (
ABCMeta,
abstractmethod,
)
from jsonschema import validate
from provisioningserver.power.schema import JSON_POWER_TYPE_PARAMETERS
from provisioningserver.utils import typed
from provisioningserver.utils.registry import Registry
# JSON schema representing the Django choices format as JSON; an array of
# 2-item arrays.
CHOICE_FIELD_SCHEMA = {
'type': 'array',
'items': {
'title': "Setting parameter field choice",
'type': 'array',
'minItems': 2,
'maxItems': 2,
'uniqueItems': True,
'items': {
'type': 'string',
}
},
}
# JSON schema for what a settings field should look like.
SETTING_PARAMETER_FIELD_SCHEMA = {
'title': "Setting parameter field",
'type': 'object',
'properties': {
'name': {
'type': 'string',
},
'field_type': {
'type': 'string',
},
'label': {
'type': 'string',
},
'required': {
'type': 'boolean',
},
'choices': CHOICE_FIELD_SCHEMA,
'default': {
'type': 'string',
},
},
'required': ['field_type', 'label', 'required'],
}
# JSON schema for what group of setting parameters should look like.
JSON_SETTING_SCHEMA = {
'title': "Setting parameters set",
'type': 'object',
'properties': {
'name': {
'type': 'string',
},
'description': {
'type': 'string',
},
'fields': {
'type': 'array',
'items': SETTING_PARAMETER_FIELD_SCHEMA,
},
},
'required': ['name', 'description', 'fields'],
}
def make_setting_field(
name, label, field_type=None, choices=None, default=None,
required=False):
"""Helper function for building a JSON setting parameters field.
:param name: The name of the field.
:type name: string
:param label: The label to be presented to the user for this field.
:type label: string
:param field_type: The type of field to create. Can be one of
(string, choice, mac_address). Defaults to string.
:type field_type: string.
:param choices: The collection of choices to present to the user.
Needs to be structured as a list of lists, otherwise
make_setting_field() will raise a ValidationError.
:type list:
:param default: The default value for the field.
:type default: string
:param required: Whether or not a value for the field is required.
:type required: boolean
"""
if field_type not in ('string', 'mac_address', 'choice'):
field_type = 'string'
if choices is None:
choices = []
validate(choices, CHOICE_FIELD_SCHEMA)
if default is None:
default = ""
field = {
'name': name,
'label': label,
'required': required,
'field_type': field_type,
'choices': choices,
'default': default,
}
return field
def validate_settings(setting_fields):
"""Helper that validates that the fields adhere to the JSON schema."""
validate(setting_fields, JSON_SETTING_SCHEMA)
def gen_power_types():
from provisioningserver.drivers.power import power_drivers_by_name
for power_type in JSON_POWER_TYPE_PARAMETERS:
driver = power_drivers_by_name.get(power_type['name'])
if driver is not None:
power_type['missing_packages'] = driver.detect_missing_packages()
yield power_type
class Architecture:
def __init__(self, name, description, pxealiases=None,
kernel_options=None):
"""Represents an architecture in the driver context.
:param name: The architecture name as used in MAAS.
arch/subarch or just arch.
:param description: The human-readable description for the
architecture.
:param pxealiases: The optional list of names used if the
hardware uses a different name when requesting its bootloader.
:param kernel_options: The optional list of kernel options for this
architecture. Anything supplied here supplements the options
provided by MAAS core.
"""
if pxealiases is None:
pxealiases = ()
self.name = name
self.description = description
self.pxealiases = pxealiases
self.kernel_options = kernel_options
class BootResource(metaclass=ABCMeta):
"""Abstraction of ephemerals and pxe resources required for a hardware
driver.
This resource is responsible for importing and reporting on
what is potentially available in relation to a cluster controller.
"""
def __init__(self, name):
self.name = name
@abstractmethod
def import_resources(self, at_location, filter=None):
"""Import the specified resources.
:param at_location: URL to a Simplestreams index or a local path
to a directory containing boot resources.
:param filter: A simplestreams filter.
e.g. "release=trusty label=beta-2 arch=amd64"
This is ignored if the location is a local path, all resources
at the location will be imported.
TBD: How to provide progress information.
"""
@abstractmethod
def describe_resources(self, at_location):
"""Enumerate all the boot resources.
:param at_location: URL to a Simplestreams index or a local path
to a directory containing boot resources.
:return: a list of dictionaries describing the available resources,
which will need to be imported so the driver can use them.
[
{
"release": "trusty",
"arch": "amd64",
"label": "beta-2",
"size": 12344556,
}
,
]
"""
class HardwareDiscoverContext(metaclass=ABCMeta):
@abstractmethod
def startDiscovery(self):
"""TBD"""
@abstractmethod
def stopDiscovery(self):
"""TBD"""
class ArchitectureRegistry(Registry):
"""Registry for architecture classes."""
@classmethod
@typed
def get_by_pxealias(cls, alias: str):
for _, arch in cls:
if alias in arch.pxealiases:
return arch
return None
class BootResourceRegistry(Registry):
"""Registry for boot resource classes."""
builtin_architectures = [
Architecture(name="i386/generic", description="i386"),
Architecture(name="amd64/generic", description="amd64"),
Architecture(
name="arm64/generic", description="arm64/generic",
pxealiases=["arm"]),
Architecture(
name="arm64/xgene-uboot", description="arm64/xgene-uboot",
pxealiases=["arm"]),
Architecture(
name="arm64/xgene-uboot-mustang",
description="arm64/xgene-uboot-mustang", pxealiases=["arm"]),
Architecture(
name="armhf/highbank", description="armhf/highbank",
pxealiases=["arm"], kernel_options=["console=ttyAMA0"]),
Architecture(
name="armhf/generic", description="armhf/generic",
pxealiases=["arm"], kernel_options=["console=ttyAMA0"]),
Architecture(
name="armhf/keystone", description="armhf/keystone",
pxealiases=["arm"]),
# PPC64EL needs a rootdelay for PowerNV. The disk controller
# in the hardware, takes a little bit longer to come up then
# the initrd wants to wait. Set this to 60 seconds, just to
# give the booting machine enough time. This doesn't slow down
# the booting process, it just increases the timeout.
Architecture(
name="ppc64el/generic", description="ppc64el",
kernel_options=['rootdelay=60']),
]
for arch in builtin_architectures:
ArchitectureRegistry.register_item(arch.name, arch)
|