This file is indexed.

/usr/lib/python3/dist-packages/provisioningserver/diskless.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
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# Copyright 2014-2015 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Generate diskless image for system to boot."""

__all__ = [
    'create_diskless_disk',
    'delete_diskless_disk',
    ]

import os
from textwrap import dedent

from provisioningserver.config import ClusterConfiguration
from provisioningserver.drivers.diskless import DisklessDriverRegistry
from provisioningserver.drivers.osystem import (
    BOOT_IMAGE_PURPOSE,
    OperatingSystemRegistry,
)
from provisioningserver.logger import get_maas_logger
from provisioningserver.utils.fs import (
    atomic_symlink,
    atomic_write,
)
from provisioningserver.utils.shell import call_and_check
from twisted.python.filepath import FilePath


maaslog = get_maas_logger("diskless")


class DisklessError(Exception):
    """Error raised when issue occurs during a diskless task."""


def get_diskless_store():
    """Return path to the diskless store.

    This is the location that all diskless links exist. It holds all of the
    currently in use disk for diskless booting.
    """
    with ClusterConfiguration.open() as config:
        storage = FilePath(config.tftp_root).parent()
    return storage.child('diskless').child('store').path


def compose_diskless_link_path(system_id):
    """Return path to the symbolic link for the given system_id.

    This is the link that will be written into the diskless store. It is used
    to reference what disks are currently being used for diskless booting.
    """
    return os.path.join(get_diskless_store(), system_id)


def create_diskless_link(system_id, storage_path):
    """Create symbolic link in the diskless store to the actual path
    of the backing store.

    Each diskless driver returns an absolute path to were the data can be
    accessed on the system. A symbolic link is made in the diskless store to
    reference this location, so it can be retrieved later by system_id.
    """
    link_path = compose_diskless_link_path(system_id)
    if os.path.lexists(link_path):
        raise DisklessError(
            "Backend storage link already exists for: %s" % system_id)
    atomic_symlink(storage_path, link_path)


def delete_diskless_link(system_id):
    """Delete symbolic link in the diskless store."""
    link_path = compose_diskless_link_path(system_id)
    if os.path.lexists(link_path):
        os.unlink(link_path)


def read_diskless_link(system_id):
    """Return actual path to the backing store, from the link
    in the diskless store."""
    link_path = compose_diskless_link_path(system_id)
    if not os.path.lexists(link_path):
        return None
    return os.readlink(link_path)


def get_diskless_target(system_id):
    """Get the iscsi target name for the node."""
    prefix = 'iqn.2004-05.com.ubuntu:maas'
    return '%s:root-diskless-%s' % (prefix, system_id)


def get_diskless_tgt_path():
    """Return path to maas-diskless.tgt."""
    with ClusterConfiguration.open() as config:
        storage = FilePath(config.tftp_root).parent()
    return storage.child('diskless').child('maas-diskless.tgt').path


def tgt_entry(system_id, image):
    """Generate tgt target used for root disk

    Tgt target used by the node as its root disk. This function creates target
    description in a format used by tgt-admin. It uses system_id to generate
    target name and image as a path to image file which should be available.

    :param system_id: Node system_id
    :param image: Path to the image which should be shared via tgt/iscsi
    :return Tgt entry which can be written to tgt-admin configuration file
    """
    target = get_diskless_target(system_id)
    entry = dedent("""\
        <target {target}>
            readonly 0
            backing-store "{image}"
            driver iscsi
        </target>
        """).format(target=target, image=image)
    return entry


def compose_diskless_tgt_config():
    """Produce the contents of a diskless tgt conf file.

    :return: Contents for a `targets.conf` file.
    :rtype: bytes
    """
    tgt_entries = []
    for system_id in os.listdir(get_diskless_store()):
        image_path = compose_diskless_link_path(system_id)
        tgt_entries.append(tgt_entry(system_id, image_path))
    return ''.join(tgt_entries).encode('utf-8')


def reload_diskless_tgt():
    """Reload the diskless tgt config."""
    call_and_check([
        'sudo',
        '/usr/sbin/tgt-admin',
        '--conf', get_diskless_tgt_path(),
        '--update', 'ALL',
        ])


def update_diskless_tgt():
    """Re-writes the "maas-diskless.tgt" to include all targets that have
    symlinks in the diskless store. Reloads the tgt config."""
    tgt_path = get_diskless_tgt_path()
    tgt_config = compose_diskless_tgt_config()
    atomic_write(tgt_config, tgt_path, mode=0o644)
    reload_diskless_tgt()


def get_diskless_driver(driver):
    """Return the diskless driver object.

    :raise DisklessError: if driver does not exist.
    """
    driver_obj = DisklessDriverRegistry.get_item(driver)
    if driver_obj is None:
        raise DisklessError(
            "Cluster doesn't support diskless driver: %s" % driver)
    return driver_obj


def compose_source_path(osystem_name, arch, subarch, release, label):
    """Return path to the source file for the diskless boot image.

    Each diskless driver will use this source to initialize the disk.
    """
    osystem = OperatingSystemRegistry.get_item(osystem_name)
    if osystem is None:
        raise DisklessError(
            "OS doesn't exist in operating system registry: %s" % osystem_name)
    purposes = osystem.get_boot_image_purposes(arch, subarch, release, label)
    if BOOT_IMAGE_PURPOSE.DISKLESS not in purposes:
        raise DisklessError(
            "OS doesn't support diskless booting: %s" % osystem_name)
    root_path, _ = osystem.get_xinstall_parameters()
    with ClusterConfiguration.open() as config:
        return os.path.join(
            config.tftp_root, osystem_name, arch, subarch, release, label,
            root_path)


def create_diskless_disk(driver, driver_options, system_id,
                         osystem, arch, subarch, release, label):
    """Creates a disk using the `driver` for the `system_id`. This disk will
    be used for booting diskless."""
    source_path = compose_source_path(osystem, arch, subarch, release, label)
    if not os.path.exists(source_path):
        raise DisklessError("Boot resources doesn't exist: %s" % source_path)
    link_path = compose_diskless_link_path(system_id)
    if os.path.lexists(link_path):
        raise DisklessError("Disk already exists for node: %s" % system_id)

    # Create the disk with the driver, and place the link in diskless source.
    maaslog.info(
        "Creating disk for node %s using driver: %s", system_id, driver)
    driver_obj = get_diskless_driver(driver)
    disk_path = driver_obj.create_disk(
        system_id, source_path, **driver_options)
    if disk_path is None or not os.path.exists(disk_path):
        raise DisklessError(
            "Driver failed to create disk for node: %s" % system_id)
    create_diskless_link(system_id, disk_path)

    # Re-write the tgt config, to include the new disk for the node.
    maaslog.info("Updating iSCSI targets.")
    update_diskless_tgt()


def delete_diskless_disk(driver, driver_options, system_id):
    """Deletes the disk that was used by the node for diskless booting."""
    link_path = compose_diskless_link_path(system_id)
    if not os.path.lexists(link_path):
        maaslog.warning("Disk already deleted for node: %s", system_id)
        return

    maaslog.info(
        "Destroying disk for node %s using driver: %s", system_id, driver)
    disk_path = read_diskless_link(system_id)
    if disk_path is None:
        raise DisklessError(
            "Failed to read diskless link for node: %s" % system_id)
    if os.path.exists(disk_path):
        driver_obj = get_diskless_driver(driver)
        driver_obj.delete_disk(system_id, disk_path, **driver_options)
    else:
        maaslog.warning((
            "Assuming disk has already been removed "
            "for node %s by the driver: %s"), system_id, driver)
    delete_diskless_link(system_id)

    # Re-write the tgt config, to include only the remaining disks.
    maaslog.info("Updating iSCSI targets.")
    update_diskless_tgt()