/usr/lib/python3/dist-packages/provisioningserver/refresh/__init__.py is in python3-maas-provisioningserver 2.4.0~beta2-6865-gec43e47e6-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 212 213 214 215 216 217 | # Copyright 2016-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Functionality to refresh rack controller hardware and networking details."""
import os
import socket
import stat
from subprocess import (
DEVNULL,
PIPE,
Popen,
TimeoutExpired,
)
import tempfile
from provisioningserver.logger import get_maas_logger
from provisioningserver.refresh.maas_api_helper import (
capture_script_output,
MD_VERSION,
signal,
SignalException,
)
from provisioningserver.refresh.node_info_scripts import NODE_INFO_SCRIPTS
from provisioningserver.utils.shell import (
call_and_check,
ExternalProcessError,
)
from provisioningserver.utils.twisted import synchronous
from provisioningserver.utils.version import get_maas_version
maaslog = get_maas_logger("refresh")
def get_architecture():
"""Get the architecture of the running system."""
try:
stdout = call_and_check('archdetect').decode('utf-8')
except ExternalProcessError:
return ''
arch, subarch = stdout.strip().split('/')
if arch in ['i386', 'amd64', 'arm64', 'ppc64el']:
subarch = 'generic'
return '%s/%s' % (arch, subarch)
def get_os_release():
"""Parse the contents of /etc/os-release into a dictionary."""
def full_strip(value):
return value.strip().strip('\'"')
os_release = {}
with open('/etc/os-release') as f:
for line in f:
key, value = line.split('=')
os_release[full_strip(key)] = full_strip(value)
return os_release
@synchronous
def get_sys_info():
"""Return basic system information in a dictionary."""
os_release = get_os_release()
if 'ID' in os_release:
osystem = os_release['ID']
elif 'NAME' in os_release:
osystem = os_release['NAME']
else:
osystem = ''
if 'UBUNTU_CODENAME' in os_release:
distro_series = os_release['UBUNTU_CODENAME']
elif 'VERSION_ID' in os_release:
distro_series = os_release['VERSION_ID']
else:
distro_series = ''
result = {
'hostname': socket.gethostname().split('.')[0],
'architecture': get_architecture(),
'osystem': osystem,
'distro_series': distro_series,
'maas_version': get_maas_version(),
# In MAAS 2.3+, the NetworksMonitoringService is solely responsible for
# interface update, because interface updates need to include beaconing
# data. The key and empty dictionary are necessary for backward
# compatibility; the region will ignore the empty dictionary in
# _process_sys_info().
'interfaces': {}
}
return result
def signal_wrapper(*args, **kwargs):
"""Wrapper to capture and log any SignalException from signal."""
try:
signal(*args, **kwargs)
except SignalException as e:
maaslog.error("Error during controller refresh: %s" % e.error)
@synchronous
def refresh(system_id, consumer_key, token_key, token_secret, maas_url=None):
"""Run all builtin commissioning scripts and report results to region."""
maaslog.info(
"Refreshing rack controller hardware information.")
if maas_url is None:
maas_url = 'http://127.0.0.1:5240/MAAS'
url = "%s/metadata/%s/" % (maas_url, MD_VERSION)
creds = {
'consumer_key': consumer_key,
'token_key': token_key,
'token_secret': token_secret,
'consumer_secret': '',
}
scripts = {
name: config
for name, config in NODE_INFO_SCRIPTS.items()
if config['run_on_controller']
}
with tempfile.TemporaryDirectory(prefix='maas-commission-') as tmpdir:
failed_scripts = runscripts(scripts, url, creds, tmpdir=tmpdir)
if len(failed_scripts) == 0:
signal_wrapper(
url, creds, 'OK', 'Finished refreshing %s' % system_id)
else:
signal_wrapper(
url, creds, 'FAILED', 'Failed refreshing %s' % system_id)
def runscripts(scripts, url, creds, tmpdir):
total_scripts = len(scripts)
current_script = 1
failed_scripts = []
out_dir = os.path.join(tmpdir, 'out')
os.makedirs(out_dir)
for script_name in sorted(scripts.keys()):
builtin_script = scripts[script_name]
signal_wrapper(
url, creds, 'WORKING', 'Starting %s [%d/%d]' %
(script_name, current_script, total_scripts))
# Write script to /tmp and set it executable
script_path = os.path.join(tmpdir, script_name)
with open(script_path, 'wb') as f:
f.write(builtin_script['content'])
st = os.stat(script_path)
os.chmod(script_path, st.st_mode | stat.S_IEXEC)
combined_path = os.path.join(out_dir, script_name)
stdout_name = '%s.out' % script_name
stdout_path = os.path.join(out_dir, stdout_name)
stderr_name = '%s.err' % script_name
stderr_path = os.path.join(out_dir, stderr_name)
timeout = builtin_script.get('timeout')
if timeout is None:
timeout_seconds = None
else:
timeout_seconds = timeout.seconds
try:
proc = Popen(script_path, stdin=DEVNULL, stdout=PIPE, stderr=PIPE)
capture_script_output(
proc, combined_path, stdout_path, stderr_path, timeout_seconds)
except OSError as e:
if isinstance(e.errno, int) and e.errno != 0:
exit_status = e.errno
else:
# 2 is the return code bash gives when it can't execute.
exit_status = 2
result = str(e).encode()
if result == b'':
result = b'Unable to execute script'
files = {
script_name: result,
stderr_name: result,
}
signal_wrapper(
url, creds, 'WORKING', files=files, exit_status=exit_status,
error='Failed to execute %s [%d/%d]: %d' % (
script_name, current_script, total_scripts, exit_status))
failed_scripts.append(script_name)
except TimeoutExpired:
files = {
script_name: open(combined_path, 'rb').read(),
stdout_name: open(stdout_path, 'rb').read(),
stderr_name: open(stderr_path, 'rb').read(),
}
signal_wrapper(
url, creds, 'TIMEDOUT', files=files,
error='Timeout(%s) expired on %s [%d/%d]' % (
str(timeout), script_name, current_script, total_scripts))
failed_scripts.append(script_name)
else:
files = {
script_name: open(combined_path, 'rb').read(),
stdout_name: open(stdout_path, 'rb').read(),
stderr_name: open(stderr_path, 'rb').read(),
}
signal_wrapper(
url, creds, 'WORKING', files=files,
exit_status=proc.returncode,
error='Finished %s [%d/%d]: %d' % (
script_name, current_script, total_scripts,
proc.returncode))
if proc.returncode != 0:
failed_scripts.append(script_name)
current_script += 1
return failed_scripts
|