/usr/lib/python3/dist-packages/provisioningserver/rpc/boot_images.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 | # Copyright 2014-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""RPC relating to boot images."""
__all__ = [
"import_boot_images",
"list_boot_images",
"is_import_boot_images_running",
]
from urllib.parse import urlparse
from provisioningserver import concurrency
from provisioningserver.auth import get_maas_user_gpghome
from provisioningserver.boot import tftppath
from provisioningserver.config import ClusterConfiguration
from provisioningserver.import_images import boot_resources
from provisioningserver.logger import LegacyLogger
from provisioningserver.rpc import getRegionClient
from provisioningserver.rpc.region import UpdateLastImageSync
from provisioningserver.utils.env import (
environment_variables,
get_maas_id,
)
from provisioningserver.utils.twisted import synchronous
from twisted.internet.defer import (
fail,
inlineCallbacks,
)
from twisted.internet.threads import deferToThread
log = LegacyLogger()
CACHED_BOOT_IMAGES = None
def list_boot_images():
"""List the boot images that exist on the cluster.
This return value of this function is cached. This helps reduce the amount
of IO, as this function is called often. To update the cache call
`reload_boot_images`.
"""
global CACHED_BOOT_IMAGES
if CACHED_BOOT_IMAGES is None:
with ClusterConfiguration.open() as config:
tftp_root = config.tftp_root
CACHED_BOOT_IMAGES = tftppath.list_boot_images(tftp_root)
return CACHED_BOOT_IMAGES
def reload_boot_images():
"""Update the cached boot images so `list_boot_images` returns the
most up-to-date boot images list."""
global CACHED_BOOT_IMAGES
with ClusterConfiguration.open() as config:
tftp_root = config.tftp_root
CACHED_BOOT_IMAGES = tftppath.list_boot_images(tftp_root)
def get_hosts_from_sources(sources):
"""Return set of hosts that are contained in the given sources."""
hosts = set()
for source in sources:
url = urlparse(source['url'])
if url.hostname is not None:
hosts.add(url.hostname)
return hosts
def fix_sources_for_cluster(sources):
"""Return modified sources that use the URL to the region defined in the
cluster configuration instead of the one the region suggested."""
sources = list(sources)
with ClusterConfiguration.open() as config:
maas_url = config.maas_url
maas_url_parsed = urlparse(maas_url)
maas_url_path = maas_url_parsed.path.lstrip('/').rstrip('/')
for source in sources:
url = urlparse(source['url'])
source_path = url.path.lstrip('/')
# Most likely they will both have 'MAAS/' at the start. We can't just
# append because then the URL would be 'MAAS/MAAS/' which is incorrect.
# If the initial part of the URL defined in the config matches the
# beginning of what the region told the cluster to use then strip it
# out and build the new URL.
if source_path.startswith(maas_url_path):
source_path = source_path[len(maas_url_path):]
url = maas_url.rstrip('/') + '/' + source_path.lstrip('/')
source['url'] = url
return sources
@synchronous
def _run_import(sources, http_proxy=None, https_proxy=None):
"""Run the import.
This is function is synchronous so it must be called with deferToThread.
"""
# Fix the sources to download from the IP address defined in the cluster
# configuration, instead of the URL that the region asked it to use.
sources = fix_sources_for_cluster(sources)
variables = {
'GNUPGHOME': get_maas_user_gpghome(),
}
if http_proxy is not None:
variables['http_proxy'] = http_proxy
if https_proxy is not None:
variables['https_proxy'] = https_proxy
# Communication to the sources and loopback should not go through proxy.
no_proxy_hosts = ["localhost", "::ffff:127.0.0.1", "127.0.0.1", "::1"]
no_proxy_hosts += list(get_hosts_from_sources(sources))
variables['no_proxy'] = ','.join(no_proxy_hosts)
with environment_variables(variables):
imported = boot_resources.import_images(sources)
# Update the boot images cache so `list_boot_images` returns the
# correct information.
reload_boot_images()
# Tell callers if anything happened.
return imported
def import_boot_images(sources, http_proxy=None, https_proxy=None):
"""Imports the boot images from the given sources."""
lock = concurrency.boot_images
# This checks if any other defer is already waiting. If nothing is waiting
# then add the _import again. If its already waiting nothing is added.
#
# This is important to how this functions. If the rackd is already
# importing images and the regiond triggers another import then after the
# original import another will be fired.
if not lock.waiting:
return lock.run(
_import_boot_images, sources, http_proxy=http_proxy,
https_proxy=https_proxy)
@inlineCallbacks
def _import_boot_images(sources, http_proxy=None, https_proxy=None):
"""Import boot images then inform the region.
Helper for `import_boot_images`.
"""
proxies = dict(http_proxy=http_proxy, https_proxy=https_proxy)
imported = yield deferToThread(_run_import, sources, **proxies)
if imported:
yield touch_last_image_sync_timestamp().addErrback(
log.err, "Failure touching last image sync timestamp.")
def is_import_boot_images_running():
"""Return True if the import process is currently running."""
return concurrency.boot_images.locked
def touch_last_image_sync_timestamp():
"""Inform the region that images have just been synchronised.
:return: :class:`Deferred` that can fail with `NoConnectionsAvailable` or
any exception arising from an `UpdateLastImageSync` RPC.
"""
try:
client = getRegionClient()
except:
return fail()
else:
return client(UpdateLastImageSync, system_id=get_maas_id())
|