~ubuntu-branches/ubuntu/utopic/maas/utopic-updates

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez, Jeroen Vermeulen, Andres Rodriguez, Jason Hobbs, Raphaël Badin, Louis Bouchard, Gavin Panella
  • Date: 2014-08-21 19:36:30 UTC
  • mfrom: (1.3.1)
  • Revision ID: package-import@ubuntu.com-20140821193630-kertpu5hd8yyss8h
Tags: 1.7.0~beta7+bzr3266-0ubuntu1
* New Upstream Snapshot, Beta 7 bzr3266

[ Jeroen Vermeulen ]
* debian/extras/99-maas-sudoers
  debian/maas-dhcp.postinst
  debian/rules
  - Add second DHCP server instance for IPv6.
* debian/maas-region-controller-min.install
  debian/maas-region-controller-min.lintian-overrides
  - Install deployment user-data: maas_configure_interfaces.py script.
* debian/maas-cluster-controller.links
  debian/maas-cluster-controller.install
  debian/maas-cluster-controller.postinst
  - Reflect Celery removal changes made in trunk r3067.
  - Don't install celeryconfig_cluster.py any longer. 
  - Don't install maas_local_celeryconfig_cluster.py any longer.
  - Don't symlink maas_local_celeryconfig_cluster.py from /etc to /usr.
  - Don't insert UUID into maas_local_celeryconfig_cluster.py.

[ Andres Rodriguez ]
* debian/maas-region-controller-min.postrm: Cleanup lefover files.
* debian/maas-dhcp.postrm: Clean leftover configs.
* Provide new maas-proxy package that replaces the usage of
  squid-deb-proxy:
  - debian/control: New maas-proxy package that replaces the usage
    of squid-deb-proxy; Drop depends on squid-deb-proxy.
  - Add upstrart job.
  - Ensure squid3 is stopped as maas-proxy uses a caching proxy.
* Remove Celery references to cluster controller:
  - Rename upstart job from maas-pserv to maas-cluster; rename
    maas-cluster-celery to maas-cluster-register. Ensure services
    are stopped on upgrade.
  - debian/maintscript: Cleanup config files.
  - Remove all references to the MAAS celery daemon and config
    files as we don't use it like that anymore
* Move some entries in debian/maintscript to
  debian/maas-cluster-controller.maintscript
* Remove usage of txlongpoll and rabbitmq-server. Handle upgrades
  to ensure these are removed correctly.

[ Jason Hobbs ]
* debian/maas-region-controller-min.install: Install
  maas-generate-winrm-cert script.

[ Raphaël Badin ]
* debian/extras/maas-region-admin: Bypass django-admin as it prints
  spurious messages to stdout (LP: #1365130).

[Louis Bouchard]
* debian/maas-cluster-controller.postinst:
  - Exclude /var/log/maas/rsyslog when changing ownership
    (LP: #1346703)

[Gavin Panella]
* debian/maas-cluster-controller.maas-clusterd.upstart:
  - Don't start-up the cluster controller unless a shared-secret has
    been installed.
* debian/maas-cluster-controller.maas-cluster-register.upstart: Drop.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
    "compose_URL",
17
17
    "create_node",
18
18
    "filter_dict",
 
19
    "flatten",
 
20
    "get_cluster_config",
19
21
    "import_settings",
20
22
    "locate_config",
21
23
    "parse_key_value_file",
24
26
    "write_custom_config_section",
25
27
    ]
26
28
 
 
29
from collections import Iterable
 
30
from itertools import (
 
31
    chain,
 
32
    imap,
 
33
    )
27
34
import os
28
35
from pipes import quote
29
36
import re
36
43
    )
37
44
from warnings import warn
38
45
 
39
 
from apiclient.maas_client import (
40
 
    MAASClient,
41
 
    MAASDispatcher,
42
 
    MAASOAuth,
43
 
    )
44
46
import bson
45
 
from provisioningserver.auth import get_recorded_api_credentials
46
 
from provisioningserver.cluster_config import get_maas_url
 
47
from provisioningserver.cluster_config import get_cluster_uuid
 
48
from provisioningserver.logger.log import get_maas_logger
 
49
from provisioningserver.rpc import getRegionClient
 
50
from provisioningserver.rpc.exceptions import (
 
51
    NoConnectionsAvailable,
 
52
    NodeAlreadyExists,
 
53
    )
 
54
from provisioningserver.utils.twisted import (
 
55
    pause,
 
56
    retries,
 
57
    )
47
58
import simplejson as json
48
59
import tempita
 
60
from twisted.internet import reactor
 
61
from twisted.internet.defer import (
 
62
    inlineCallbacks,
 
63
    returnValue,
 
64
    )
 
65
from twisted.protocols.amp import UnhandledCommand
 
66
 
 
67
 
 
68
maaslog = get_maas_logger("utils")
49
69
 
50
70
 
51
71
def node_exists(macs, url, client):
66
86
    return len(content) > 0
67
87
 
68
88
 
 
89
@inlineCallbacks
69
90
def create_node(macs, arch, power_type, power_parameters):
70
 
    api_credentials = get_recorded_api_credentials()
71
 
    if api_credentials is None:
72
 
        raise Exception('Not creating node: no API key yet.')
73
 
    client = MAASClient(
74
 
        MAASOAuth(*api_credentials), MAASDispatcher(),
75
 
        get_maas_url())
 
91
    """Create a Node on the region and return its system_id.
76
92
 
77
 
    data = {
78
 
        'architecture': arch,
79
 
        'power_type': power_type,
80
 
        'power_parameters': json.dumps(power_parameters),
81
 
        'mac_addresses': macs,
82
 
        'autodetect_nodegroup': 'true'
83
 
    }
84
 
    url = '/api/1.0/nodes/'
85
 
    if node_exists(macs, url, client):
 
93
    :param macs: A list of MAC addresses belonging to the node.
 
94
    :param arch: The node's architecture, in the form 'arch/subarch'.
 
95
    :param power_type: The node's power type as a string.
 
96
    :param power_parameters: The power parameters for the node, as a
 
97
        dict.
 
98
    """
 
99
    # Avoid circular dependencies.
 
100
    from provisioningserver.rpc.region import CreateNode
 
101
    for elapsed, remaining, wait in retries(15, 5, reactor):
 
102
        try:
 
103
            client = getRegionClient()
 
104
            break
 
105
        except NoConnectionsAvailable:
 
106
            yield pause(wait, reactor)
 
107
    else:
 
108
        maaslog.error(
 
109
            "Can't create node, no RPC connection to region.")
86
110
        return
87
 
    return client.post(url, 'new', **data)
 
111
 
 
112
    # De-dupe the MAC addresses we pass. We sort here to avoid test
 
113
    # failures.
 
114
    macs = sorted(set(macs))
 
115
    try:
 
116
        response = yield client(
 
117
            CreateNode,
 
118
            cluster_uuid=get_cluster_uuid(),
 
119
            architecture=arch,
 
120
            power_type=power_type,
 
121
            power_parameters=json.dumps(power_parameters),
 
122
            mac_addresses=macs)
 
123
    except NodeAlreadyExists:
 
124
        # The node already exists on the region, so we log the error and
 
125
        # give up.
 
126
        maaslog.error(
 
127
            "A node with one of the mac addressess in %s already exists.",
 
128
            macs)
 
129
        returnValue(None)
 
130
    except UnhandledCommand:
 
131
        # The region hasn't been upgraded to support this method
 
132
        # yet, so give up.
 
133
        maaslog.error(
 
134
            "Unable to create node on region: Region does not "
 
135
            "support the CreateNode RPC method.")
 
136
        returnValue(None)
 
137
    else:
 
138
        returnValue(response['system_id'])
88
139
 
89
140
 
90
141
def locate_config(*path):
110
161
    return os.path.abspath(os.path.join(config_dir, *path))
111
162
 
112
163
 
 
164
setting_expression = r"""
 
165
    ^([A-Z0-9_]+)    # Variable name is all caps, alphanumeric and _.
 
166
    =                # Assignment operator.
 
167
    (?:"|\')?        # Optional leading single or double quote.
 
168
    (.*)             # Value
 
169
    (?:"|\')?        # Optional trailing single or double quote.
 
170
    """
 
171
 
 
172
 
 
173
def get_cluster_config(config_path):
 
174
    contents = open(config_path).read()
 
175
 
 
176
    results = re.findall(
 
177
        setting_expression, contents, re.MULTILINE | re.VERBOSE)
 
178
 
 
179
    return dict(results)
 
180
 
 
181
 
113
182
def find_settings(whence):
114
183
    """Return settings from `whence`, which is assumed to be a module."""
115
184
    # XXX 2012-10-11 JeroenVermeulen, bug=1065456: Put this in a shared
143
212
    }
144
213
 
145
214
 
 
215
def dict_depth(d, depth=0):
 
216
    """Returns the max depth of a dictionary."""
 
217
    if not isinstance(d, dict) or not d:
 
218
        return depth
 
219
    return max(dict_depth(v, depth + 1) for _, v in d.iteritems())
 
220
 
 
221
 
146
222
def split_lines(input, separator):
147
223
    """Split each item from `input` into a key/value pair."""
148
224
    return (line.split(separator, 1) for line in input if line.strip() != '')
345
421
    else:
346
422
        message = "%s; %s" % (message, alternative)
347
423
    warn(message, DeprecationWarning, 1)
 
424
 
 
425
 
 
426
def flatten(*things):
 
427
    """Recursively flatten iterable parts of `things`.
 
428
 
 
429
    For example::
 
430
 
 
431
      >>> sorted(flatten([1, 2, {3, 4, (5, 6)}]))
 
432
      [1, 2, 3, 4, 5, 6]
 
433
 
 
434
    :returns: An iterator.
 
435
    """
 
436
    def _flatten(things):
 
437
        if isinstance(things, basestring):
 
438
            # String-like objects are treated as leaves; iterating through a
 
439
            # string yields more strings, each of which is also iterable, and
 
440
            # so on, until the heat-death of the universe.
 
441
            return iter((things,))
 
442
        elif isinstance(things, Iterable):
 
443
            # Recurse and merge in order to flatten nested structures.
 
444
            return chain.from_iterable(imap(_flatten, things))
 
445
        else:
 
446
            # This is a leaf; return an single-item iterator so that it can be
 
447
            # chained with any others.
 
448
            return iter((things,))
 
449
 
 
450
    return _flatten(things)