/usr/lib/python3/dist-packages/provisioningserver/rpc/utils.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 | # Copyright 2015-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Utilities for the provisioning server."""
__all__ = [
"create_node",
"commission_node",
]
import json
import re
from provisioningserver.logger.log import get_maas_logger
from provisioningserver.rpc import getRegionClient
from provisioningserver.rpc.exceptions import (
CommissionNodeFailed,
NoConnectionsAvailable,
NodeAlreadyExists,
)
from provisioningserver.rpc.region import CreateNode
from provisioningserver.utils.twisted import (
asynchronous,
pause,
retries,
)
from twisted.internet import reactor
from twisted.internet.defer import (
inlineCallbacks,
returnValue,
)
from twisted.protocols.amp import (
UnhandledCommand,
UnknownRemoteError,
)
maaslog = get_maas_logger("region")
def coerce_to_valid_hostname(hostname):
"""Given a server name that may contain spaces and special characters,
attempts to derive a valid hostname.
:param hostname: the specified (possibly invalid) hostname
:return: the resulting string, or None if the hostname could not be coerced
"""
hostname = hostname.lower()
hostname = re.sub(r'[^a-z0-9-]+', '-', hostname)
hostname = hostname.strip('-')
if hostname == '' or len(hostname) > 64:
return None
return hostname
@asynchronous
@inlineCallbacks
def create_node(
macs, arch, power_type, power_parameters, domain=None, hostname=None):
"""Create a Node on the region and return its system_id.
:param macs: A list of MAC addresses belonging to the node.
:param arch: The node's architecture, in the form 'arch/subarch'.
:param power_type: The node's power type as a string.
:param power_parameters: The power parameters for the node, as a
dict.
:param domain: The domain the node should join.
"""
if hostname is not None:
hostname = coerce_to_valid_hostname(hostname)
for elapsed, remaining, wait in retries(15, 5, reactor):
try:
client = getRegionClient()
break
except NoConnectionsAvailable:
yield pause(wait, reactor)
else:
maaslog.error(
"Can't create node, no RPC connection to region.")
return
# De-dupe the MAC addresses we pass. We sort here to avoid test
# failures.
macs = sorted(set(macs))
try:
response = yield client(
CreateNode,
architecture=arch,
power_type=power_type,
power_parameters=json.dumps(power_parameters),
mac_addresses=macs,
hostname=hostname, domain=domain)
except NodeAlreadyExists:
# The node already exists on the region, so we log the error and
# give up.
maaslog.error(
"A node with one of the mac addresses in %s already exists.",
macs)
returnValue(None)
except UnhandledCommand:
# The region hasn't been upgraded to support this method
# yet, so give up.
maaslog.error(
"Unable to create node on region: Region does not "
"support the CreateNode RPC method.")
returnValue(None)
except UnknownRemoteError as e:
# This happens, for example, if a ValidationError occurs on the region.
# (In particular, we see this if the hostname is a duplicate.)
# We should probably create specific exceptions for these, so we can
# act on them appropriately.
maaslog.error(
"Unknown error while creating node %s: %s (see regiond.log)",
macs, e.description)
returnValue(None)
else:
returnValue(response['system_id'])
@asynchronous
@inlineCallbacks
def commission_node(system_id, user):
"""Commission a Node on the region.
:param system_id: system_id of node to commission.
:param user: user for the node.
"""
# Avoid circular dependencies.
from provisioningserver.rpc.region import CommissionNode
for elapsed, remaining, wait in retries(15, 5, reactor):
try:
client = getRegionClient()
break
except NoConnectionsAvailable:
yield pause(wait, reactor)
else:
maaslog.error(
"Can't commission node, no RPC connection to region.")
return
try:
yield client(
CommissionNode,
system_id=system_id,
user=user)
except CommissionNodeFailed as e:
# The node cannot be commissioned, give up.
maaslog.error(
"Could not commission with system_id %s because %s.",
system_id, e.args[0])
except UnhandledCommand:
# The region hasn't been upgraded to support this method
# yet, so give up.
maaslog.error(
"Unable to commission node on region: Region does not "
"support the CommissionNode RPC method.")
finally:
returnValue(None)
|