34
35
from provisioningserver.enum import PSERV_FAULT
37
# Presentation templates for various provisioning faults.
39
PSERV_FAULT.NO_COBBLER: """
40
The provisioning server was unable to reach the Cobbler service:
43
Check pserv.log, and restart MaaS if needed.
45
PSERV_FAULT.COBBLER_AUTH_FAILED: """
46
The provisioning server failed to authenticate with the Cobbler
47
service: %(fault_string)s.
49
This may mean that Cobbler's authentication configuration has
50
changed. Check /var/log/cobbler/ and pserv.log.
52
PSERV_FAULT.COBBLER_AUTH_ERROR: """
53
The Cobbler server no longer accepts the provisioning server's
54
authentication token. This should not happen; it may indicate
55
that the server is under unsustainable load.
57
PSERV_FAULT.NO_SUCH_PROFILE: """
58
System profile does not exist: %(fault_string)s.
60
Has the maas-import-isos script been run? This will run
61
automatically from time to time, but if it is failing, an
62
administrator may need to run it manually.
64
PSERV_FAULT.GENERIC_COBBLER_ERROR: """
65
The provisioning service encountered a problem with the Cobbler
66
server, fault code %(fault_code)s: %(fault_string)s
68
If the error message is not clear, you may need to check the
69
Cobbler logs in /var/log/cobbler/ or pserv.log.
72
Unable to reach provisioning server (%(fault_string)s).
74
Check pserv.log and your PSERV_URL setting, and restart MaaS if
80
def present_user_friendly_fault(fault):
81
"""Return a more user-friendly exception to represent `fault`.
83
:param fault: An exception raised by, or received across, xmlrpc.
84
:type fault: :class:`xmlrpclib.Fault`
85
:return: A more user-friendly exception, if one can be produced.
86
Otherwise, this returns None and the original exception should be
87
re-raised. (This is left to the caller in order to minimize
88
erosion of the backtrace).
89
:rtype: :class:`MAASAPIException`, or None.
92
'fault_code': fault.faultCode,
93
'fault_string': fault.faultString,
95
user_friendly_text = PRESENTATIONS.get(fault.faultCode)
96
if user_friendly_text is None:
99
return MAASAPIException(dedent(
100
user_friendly_text.lstrip('\n') % params))
103
class ProvisioningCaller:
104
"""Wrapper for an XMLRPC call.
106
Runs xmlrpc exceptions through `present_user_friendly_fault` for better
107
presentation to the user.
110
def __init__(self, method):
113
def __call__(self, *args, **kwargs):
115
return self.method(*args, **kwargs)
116
except xmlrpclib.Fault as e:
117
friendly_fault = present_user_friendly_fault(e)
118
if friendly_fault is None:
124
class ProvisioningProxy:
125
"""Proxy for calling the provisioning service.
127
This wraps an XMLRPC :class:`ServerProxy`, but translates exceptions
128
coming in from, or across, the xmlrpc mechanism into more helpful ones
132
def __init__(self, xmlrpc_proxy):
133
self.proxy = xmlrpc_proxy
135
def patch(self, method, replacement):
136
setattr(self.proxy, method, replacement)
138
def __getattr__(self, attribute_name):
139
"""Return a wrapped version of the requested method."""
140
attribute = getattr(self.proxy, attribute_name)
141
if getattr(attribute, '__call__', None) is None:
142
# This is a regular attribute. Return it as-is.
145
# This attribute is callable. Wrap it in a caller.
146
return ProvisioningCaller(attribute)
37
149
def get_provisioning_api_proxy():
38
150
"""Return a proxy to the Provisioning API.
59
171
"USE_REAL_PSERV to False, on an installed MAAS. "
60
172
"Don't do either.")
62
return get_fake_provisioning_api_proxy()
174
xmlrpc_proxy = get_fake_provisioning_api_proxy()
176
return ProvisioningProxy(xmlrpc_proxy)
65
179
def get_metadata_server_url():
66
180
"""Return the URL where nodes can reach the metadata service."""
67
return urljoin(Config.objects.get_config('maas_url'), "metadata/")
181
maas_url = Config.objects.get_config('maas_url')
182
if settings.FORCE_SCRIPT_NAME is None:
185
path = "%s/metadata/" % settings.FORCE_SCRIPT_NAME
186
return urljoin(maas_url, path)
70
189
def compose_metadata(node):
123
242
profile = select_profile_for_node(instance, papi)
124
243
power_type = instance.get_effective_power_type()
125
244
metadata = compose_metadata(instance)
127
papi.add_node(instance.system_id, profile, power_type, metadata)
128
except xmlrpclib.Fault as e:
129
if e.faultCode == PSERV_FAULT.NO_SUCH_PROFILE:
130
raise MissingProfileException(dedent("""
131
System profile %s does not exist. Has the maas-import-isos
132
script been run? This will run automatically from time to
133
time, but if it is failing, an administrator may need to run
135
""" % profile).lstrip('\n'))
245
papi.add_node(instance.system_id, profile, power_type, metadata)
140
248
def set_node_mac_addresses(node):