/usr/lib/python2.7/dist-packages/maasserver/utils/__init__.py is in python-django-maas 1.5+bzr2252-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 | # Copyright 2012 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Utilities."""
from __future__ import (
absolute_import,
print_function,
unicode_literals,
)
str = None
__metaclass__ = type
__all__ = [
'absolute_reverse',
'build_absolute_uri',
'find_nodegroup',
'get_db_state',
'get_local_cluster_UUID',
'ignore_unused',
'map_enum',
'strip_domain',
'synchronised',
]
import errno
from functools import wraps
import re
from urllib import urlencode
from urlparse import urljoin
from django.conf import settings
from django.core.urlresolvers import reverse
from maasserver.enum import NODEGROUPINTERFACE_MANAGEMENT
from maasserver.exceptions import NodeGroupMisconfiguration
from maasserver.utils.orm import get_one
def get_db_state(instance, field_name):
"""Get the persisted state of a given field for a given model instance.
:param instance: The model instance to consider.
:type instance: :class:`django.db.models.Model`
:param field_name: The name of the field to return.
:type field_name: unicode
"""
obj = get_one(instance.__class__.objects.filter(pk=instance.pk))
if obj is None:
return None
else:
return getattr(obj, field_name)
def ignore_unused(*args):
"""Suppress warnings about unused variables.
This function does nothing. Use it whenever you have deliberately
unused symbols: pass them to this function and lint checkers will no
longer consider them unused.
"""
def map_enum(enum_class):
"""Map out an enumeration class as a "NAME: value" dict."""
# Filter out anything that starts with '_', which covers private and
# special methods. We can make this smarter later if we start using
# a smarter enumeration base class etc. Or if we switch to a proper
# enum mechanism, this function will act as a marker for pieces of
# code that should be updated.
return {
key: value
for key, value in vars(enum_class).items()
if not key.startswith('_')
}
def absolute_reverse(view_name, query=None, base_url=None, *args, **kwargs):
"""Return the absolute URL (i.e. including the URL scheme specifier and
the network location of the MAAS server). Internally this method simply
calls Django's 'reverse' method and prefixes the result of that call with
the configured DEFAULT_MAAS_URL.
:param view_name: Django's view function name/reference or URL pattern
name for which to compute the absolute URL.
:param query: Optional query argument which will be passed down to
urllib.urlencode. The result of that call will be appended to the
resulting url.
:param base_url: Optional url used as base. If None is provided, then
settings.DEFAULT_MAAS_URL will be used.
:param args: Positional arguments for Django's 'reverse' method.
:param kwargs: Named arguments for Django's 'reverse' method.
"""
if not base_url:
base_url = settings.DEFAULT_MAAS_URL
url = urljoin(base_url, reverse(view_name, *args, **kwargs))
if query is not None:
url += '?%s' % urlencode(query, doseq=True)
return url
def build_absolute_uri(request, path):
"""Return absolute URI corresponding to given absolute path.
:param request: An http request to the API. This is needed in order to
figure out how the client is used to addressing
the API on the network.
:param path: The absolute http path to a given resource.
:return: Full, absolute URI to the resource, taking its networking
portion from `request` but the rest from `path`.
"""
scheme = "https" if request.is_secure() else "http"
return "%s://%s%s" % (scheme, request.get_host(), path)
def strip_domain(hostname):
"""Return `hostname` with the domain part removed."""
return hostname.split('.', 1)[0]
def get_local_cluster_UUID():
"""Return the UUID of the local cluster (or None if it cannot be found)."""
try:
cluster_config = open(settings.LOCAL_CLUSTER_CONFIG).read()
match = re.search(
"CLUSTER_UUID=(?P<quote>[\"']?)([^\"']+)(?P=quote)",
cluster_config)
if match is not None:
return match.groups()[1]
else:
return None
except IOError as error:
if error.errno == errno.ENOENT:
# Cluster config file is not present.
return None
else:
# Anything else is an error.
raise
def find_nodegroup(request):
"""Find the nodegroup whose subnet contains the requester's address.
There may be multiple matching nodegroups, but this endeavours to choose
the most appropriate.
:raises `maasserver.exceptions.NodeGroupMisconfiguration`: When more than
one nodegroup claims to manage the requester's network.
"""
# Circular imports.
from maasserver.models import NodeGroup
ip_address = request.META['REMOTE_ADDR']
if ip_address is None:
return None
else:
# Fetch nodegroups with interfaces in the requester's network,
# preferring those with managed networks first. The `NodeGroup`
# objects returned are annotated with the `management` field of the
# matching `NodeGroupInterface`. See https://docs.djangoproject.com
# /en/dev/topics/db/sql/#adding-annotations for this curious feature
# of Django's ORM.
query = NodeGroup.objects.raw("""
SELECT
ng.*,
ngi.management
FROM
maasserver_nodegroup AS ng,
maasserver_nodegroupinterface AS ngi
WHERE
ng.id = ngi.nodegroup_id
AND
(inet %s & ngi.subnet_mask) = (ngi.ip & ngi.subnet_mask)
ORDER BY
ngi.management DESC,
ng.id ASC
""", [ip_address])
nodegroups = list(query)
if len(nodegroups) == 0:
return None
elif len(nodegroups) == 1:
return nodegroups[0]
else:
# There are multiple matching nodegroups. Only zero or one may
# have a managed interface, otherwise it is a misconfiguration.
unmanaged = NODEGROUPINTERFACE_MANAGEMENT.UNMANAGED
nodegroups_with_managed_interfaces = {
nodegroup.id for nodegroup in nodegroups
if nodegroup.management != unmanaged
}
if len(nodegroups_with_managed_interfaces) > 1:
raise NodeGroupMisconfiguration(
"Multiple clusters on the same network; only "
"one cluster may manage the network of which "
"%s is a member." % ip_address)
return nodegroups[0]
def synchronised(lock):
"""Decorator to synchronise a call against a given lock.
Note: if the function being wrapped is a generator, the lock will
*not* be held for the lifetime of the generator; to this decorator,
it looks like the wrapped function has returned.
"""
def synchronise(func):
@wraps(func)
def call_with_lock(*args, **kwargs):
with lock:
return func(*args, **kwargs)
return call_with_lock
return synchronise
|