~lutostag/ubuntu/trusty/maas/1.5.4+keystone

« back to all changes in this revision

Viewing changes to src/maasserver/provisioning.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2012-03-27 14:49:56 UTC
  • mto: (20.1.1 quantal) (1.2.1)
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: package-import@ubuntu.com-20120327144956-z5stunhc83bnnwsi
Tags: upstream-0.1+bzr363+dfsg
ImportĀ upstreamĀ versionĀ 0.1+bzr363+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
__metaclass__ = type
12
12
__all__ = [
13
13
    'get_provisioning_api_proxy',
 
14
    'ProvisioningProxy',
14
15
    ]
15
16
 
16
17
from logging import getLogger
25
26
    post_save,
26
27
    )
27
28
from django.dispatch import receiver
28
 
from maasserver.exceptions import MissingProfileException
 
29
from maasserver.exceptions import MAASAPIException
29
30
from maasserver.models import (
30
31
    Config,
31
32
    MACAddress,
33
34
    )
34
35
from provisioningserver.enum import PSERV_FAULT
35
36
 
 
37
# Presentation templates for various provisioning faults.
 
38
PRESENTATIONS = {
 
39
    PSERV_FAULT.NO_COBBLER: """
 
40
        The provisioning server was unable to reach the Cobbler service:
 
41
        %(fault_string)s
 
42
 
 
43
        Check pserv.log, and restart MaaS if needed.
 
44
        """,
 
45
    PSERV_FAULT.COBBLER_AUTH_FAILED: """
 
46
        The provisioning server failed to authenticate with the Cobbler
 
47
        service: %(fault_string)s.
 
48
 
 
49
        This may mean that Cobbler's authentication configuration has
 
50
        changed.  Check /var/log/cobbler/ and pserv.log.
 
51
        """,
 
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.
 
56
        """,
 
57
    PSERV_FAULT.NO_SUCH_PROFILE: """
 
58
        System profile does not exist: %(fault_string)s.
 
59
 
 
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.
 
63
        """,
 
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
 
67
 
 
68
        If the error message is not clear, you may need to check the
 
69
        Cobbler logs in /var/log/cobbler/ or pserv.log.
 
70
        """,
 
71
    8002: """
 
72
        Unable to reach provisioning server (%(fault_string)s).
 
73
 
 
74
        Check pserv.log and your PSERV_URL setting, and restart MaaS if
 
75
        needed.
 
76
        """,
 
77
}
 
78
 
 
79
 
 
80
def present_user_friendly_fault(fault):
 
81
    """Return a more user-friendly exception to represent `fault`.
 
82
 
 
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.
 
90
    """
 
91
    params = {
 
92
        'fault_code': fault.faultCode,
 
93
        'fault_string': fault.faultString,
 
94
    }
 
95
    user_friendly_text = PRESENTATIONS.get(fault.faultCode)
 
96
    if user_friendly_text is None:
 
97
        return None
 
98
    else:
 
99
        return MAASAPIException(dedent(
 
100
            user_friendly_text.lstrip('\n') % params))
 
101
 
 
102
 
 
103
class ProvisioningCaller:
 
104
    """Wrapper for an XMLRPC call.
 
105
 
 
106
    Runs xmlrpc exceptions through `present_user_friendly_fault` for better
 
107
    presentation to the user.
 
108
    """
 
109
 
 
110
    def __init__(self, method):
 
111
        self.method = method
 
112
 
 
113
    def __call__(self, *args, **kwargs):
 
114
        try:
 
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:
 
119
                raise
 
120
            else:
 
121
                raise friendly_fault
 
122
 
 
123
 
 
124
class ProvisioningProxy:
 
125
    """Proxy for calling the provisioning service.
 
126
 
 
127
    This wraps an XMLRPC :class:`ServerProxy`, but translates exceptions
 
128
    coming in from, or across, the xmlrpc mechanism into more helpful ones
 
129
    for the user.
 
130
    """
 
131
 
 
132
    def __init__(self, xmlrpc_proxy):
 
133
        self.proxy = xmlrpc_proxy
 
134
 
 
135
    def patch(self, method, replacement):
 
136
        setattr(self.proxy, method, replacement)
 
137
 
 
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.
 
143
            return attribute
 
144
        else:
 
145
            # This attribute is callable.  Wrap it in a caller.
 
146
            return ProvisioningCaller(attribute)
 
147
 
36
148
 
37
149
def get_provisioning_api_proxy():
38
150
    """Return a proxy to the Provisioning API.
44
156
    if settings.USE_REAL_PSERV:
45
157
        # Use a real provisioning server.  This requires PSERV_URL to be
46
158
        # set.
47
 
        return xmlrpclib.ServerProxy(
 
159
        xmlrpc_proxy = xmlrpclib.ServerProxy(
48
160
            settings.PSERV_URL, allow_none=True, use_datetime=True)
49
161
    else:
50
162
        # Create a fake.  The code that provides the testing fake is not
59
171
                "USE_REAL_PSERV to False, on an installed MAAS.  "
60
172
                "Don't do either.")
61
173
            raise
62
 
        return get_fake_provisioning_api_proxy()
 
174
        xmlrpc_proxy = get_fake_provisioning_api_proxy()
 
175
 
 
176
    return ProvisioningProxy(xmlrpc_proxy)
63
177
 
64
178
 
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:
 
183
        path = "metadata/"
 
184
    else:
 
185
        path = "%s/metadata/" % settings.FORCE_SCRIPT_NAME
 
186
    return urljoin(maas_url, path)
68
187
 
69
188
 
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)
126
 
    try:
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
134
 
                it manually.
135
 
                """ % profile).lstrip('\n'))
136
 
        else:
137
 
            raise
 
245
    papi.add_node(instance.system_id, profile, power_type, metadata)
138
246
 
139
247
 
140
248
def set_node_mac_addresses(node):