Beispiel #1
0
    def _find_and_copy_bootloaders(self, destination, log_missing=True):
        if not super()._find_and_copy_bootloaders(destination, False):
            # If a previous copy of the UEFI AMD64 Grub files can't be found
            # see the files are on the system from an Ubuntu package install.
            # The package uses a different filename than what MAAS uses so
            # when we copy make sure the right name is used.
            missing_files = []

            if os.path.exists("/usr/lib/shim/shim.efi.signed"):
                atomic_symlink(
                    "/usr/lib/shim/shim.efi.signed",
                    os.path.join(destination, "bootx64.efi"),
                )
            else:
                missing_files.append("bootx64.efi")

            if os.path.exists(
                    "/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed"):
                atomic_symlink(
                    "/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed",
                    os.path.join(destination, "grubx64.efi"),
                )
            else:
                missing_files.append("grubx64.efi")

            if missing_files != [] and log_missing:
                err_msg = (
                    "Unable to find a copy of %s in the SimpleStream and the "
                    "packages shim-signed, and grub-efi-amd64-signed are not "
                    "installed. The %s bootloader type may not work." %
                    (", ".join(missing_files), self.name))
                try_send_rack_event(EVENT_TYPES.RACK_IMPORT_ERROR, err_msg)
                maaslog.error(err_msg)
                return False
        return True
Beispiel #2
0
    def test_link_bootloader_copies_previously_downloaded_files(self):
        method = PXEBootMethod()
        with tempdir() as tmp:
            new_dir = os.path.join(tmp, "new")
            current_dir = os.path.join(tmp, "current")
            os.makedirs(new_dir)
            os.makedirs(current_dir)
            factory.make_file(current_dir, method.bootloader_files[0])
            for bootloader_file in method.bootloader_files[1:]:
                factory.make_file(current_dir, bootloader_file)
            real_syslinux_dir = os.path.join(tmp, "syslinux")
            os.makedirs(real_syslinux_dir)
            atomic_symlink(
                real_syslinux_dir, os.path.join(current_dir, "syslinux")
            )

            method.link_bootloader(new_dir)

            for bootloader_file in method.bootloader_files:
                bootloader_file_path = os.path.join(new_dir, bootloader_file)
                self.assertTrue(os.path.isfile(bootloader_file_path))
            syslinux_link = os.path.join(new_dir, "syslinux")
            self.assertTrue(os.path.islink(syslinux_link))
            self.assertEqual(
                real_syslinux_dir, os.path.realpath(syslinux_link)
            )
Beispiel #3
0
 def test_atomic_symlink_uses_relative_path(self):
     filename = self.make_file(contents=factory.make_string())
     link_name = factory.make_name("link")
     target = os.path.join(os.path.dirname(filename), link_name)
     atomic_symlink(filename, target)
     self.assertEqual(os.path.basename(filename), os.readlink(target))
     self.assertTrue(os.path.samefile(filename, target))
Beispiel #4
0
    def _find_and_copy_bootloaders(self, destination, log_missing=True):
        """Attempt to copy bootloaders from the previous snapshot

        :param destination: The path to link the bootloaders to
        :param log_missing: Log missing files, default True

        :return: True if all bootloaders have been found and copied, False
                 otherwise.
        """
        boot_sources_base = os.path.realpath(os.path.join(destination, ".."))
        previous_snapshot = os.path.join(boot_sources_base, "current")
        files_found = True
        for bootloader_file in self.bootloader_files:
            bootloader_src = os.path.join(previous_snapshot, bootloader_file)
            bootloader_src = os.path.realpath(bootloader_src)
            bootloader_dst = os.path.join(destination, bootloader_file)
            if os.path.exists(bootloader_src):
                # Copy files if their realpath is inside the previous snapshot
                # as once we're done the previous snapshot is deleted. Symlinks
                # to other areas of the filesystem are maintained.
                if boot_sources_base in bootloader_src:
                    atomic_copy(bootloader_src, bootloader_dst)
                else:
                    atomic_symlink(bootloader_src, bootloader_dst)
            else:
                files_found = False
                if log_missing:
                    err_msg = (
                        "Unable to find a copy of %s in the SimpleStream or a "
                        "previously downloaded copy. The %s bootloader type "
                        "may not work." % (bootloader_file, self.name)
                    )
                    try_send_rack_event(EVENT_TYPES.RACK_IMPORT_ERROR, err_msg)
                    maaslog.error(err_msg)
        return files_found
Beispiel #5
0
 def test_atomic_symlink_uses_relative_path_for_directory(self):
     target_path = self.make_dir()  # The target is a directory.
     link_path = os.path.join(self.make_dir(), factory.make_name("sub"))
     atomic_symlink(target_path, link_path)
     self.assertThat(
         os.readlink(link_path),
         Equals(os.path.relpath(target_path, os.path.dirname(link_path))))
     self.assertTrue(os.path.samefile(target_path, link_path))
Beispiel #6
0
 def test_atomic_symlink_creates_symlink(self):
     filename = self.make_file(contents=factory.make_string())
     target_dir = self.make_dir()
     link_name = factory.make_name("link")
     target = os.path.join(target_dir, link_name)
     atomic_symlink(filename, target)
     self.assertTrue(os.path.islink(target),
                     "atomic_symlink didn't create a symlink")
     self.assertThat(target, SamePath(filename))
Beispiel #7
0
 def test_atomic_symlink_does_not_leak_temp_file_if_failure(self):
     # In the face of failure, no temp file is leaked.
     self.patch(os, "rename", Mock(side_effect=OSError()))
     filename = self.make_file()
     target_dir = self.make_dir()
     link_name = factory.make_name("link")
     target = os.path.join(target_dir, link_name)
     with ExpectedException(OSError):
         atomic_symlink(filename, target)
     self.assertEqual([], os.listdir(target_dir))
Beispiel #8
0
    def _link_simplestream_bootloaders(self, stream_path, destination):
        super()._link_simplestream_bootloaders(stream_path, destination)

        # MAAS only requires the bootloader_files listed above to boot.
        # However some users may want to use extra PXE files in custom
        # configs or for debug. PXELinux checks / and then /syslinux so
        # create a symlink to the stream_path which contains all extra PXE
        # files. This also ensures if upstream ever starts requiring more
        # modules PXE will continue to work.
        syslinux_dst = os.path.join(destination, 'syslinux')
        atomic_symlink(stream_path, syslinux_dst)
Beispiel #9
0
 def test_atomic_symlink_overwrites_dest_file(self):
     filename = self.make_file(contents=factory.make_string())
     target_dir = self.make_dir()
     link_name = factory.make_name("link")
     # Create a file that will be overwritten.
     factory.make_file(location=target_dir, name=link_name)
     target = os.path.join(target_dir, link_name)
     atomic_symlink(filename, target)
     self.assertTrue(os.path.islink(target),
                     "atomic_symlink didn't create a symlink")
     self.assertThat(target, SamePath(filename))
Beispiel #10
0
    def test_link_bootloader_links_bootloaders_found_elsewhere_on_fs(self):
        method = FakeBootMethod()
        with tempdir() as tmp:
            bootresources_dir = os.path.join(tmp, 'boot-resources')
            new_dir = os.path.join(bootresources_dir, 'new')
            current_dir = os.path.join(bootresources_dir, 'current')
            os.makedirs(new_dir)
            os.makedirs(current_dir)
            for bootloader_file in method.bootloader_files:
                factory.make_file(tmp, bootloader_file)
                atomic_symlink(os.path.join(tmp, bootloader_file),
                               os.path.join(current_dir, bootloader_file))

            method.link_bootloader(new_dir)

            for bootloader_file in method.bootloader_files:
                bootloader_file_path = os.path.join(new_dir, bootloader_file)
                self.assertTrue(os.path.islink(bootloader_file_path))
Beispiel #11
0
    def _link_simplestream_bootloaders(self, stream_path, destination):
        """Link the bootloaders downloaded from the SimpleStream into the
        destination(tftp root).

        :param stream_path: The path to the bootloaders in the SimpleStream
        :param destination: The path to link the bootloaders to
        """
        for bootloader_file in self.bootloader_files:
            bootloader_src = os.path.join(stream_path, bootloader_file)
            bootloader_dst = os.path.join(destination, bootloader_file)
            if os.path.exists(bootloader_src):
                atomic_symlink(bootloader_src, bootloader_dst)
            else:
                err_msg = (
                    "SimpleStream is missing required bootloader file '%s' "
                    "from bootloader %s." % (bootloader_file, self.name))
                try_send_rack_event(EVENT_TYPES.RACK_IMPORT_ERROR, err_msg)
                maaslog.error(err_msg)
Beispiel #12
0
    def link_bootloader(self, destination: str):
        """Installs the required files for this boot method into the
        destination.

        :param destination: path to install bootloader
        """
        super(PXEBootMethod, self).link_bootloader(destination)
        # When lpxelinux.0 doesn't exist we run find and copy to add that file
        # in the correct place.
        lpxelinux = os.path.join(destination, 'lpxelinux.0')
        if not os.path.exists(lpxelinux):
            self._find_and_copy_bootloaders(destination,
                                            bootloader_files=['lpxelinux.0'])

        # Symlink pxelinux.0 to lpxelinux.0 for backwards compatibility of
        # external DHCP servers that point next-server to pxelinux.0.
        pxelinux = os.path.join(destination, 'pxelinux.0')
        atomic_symlink(lpxelinux, pxelinux)
Beispiel #13
0
def update_current_symlink(storage, latest_snapshot):
    """Symlink `latest_snapshot` as the "current" snapshot."""
    atomic_symlink(latest_snapshot, os.path.join(storage, "current"))
Beispiel #14
0
    def _find_and_copy_bootloaders(self,
                                   destination,
                                   log_missing=True,
                                   bootloader_files=None):
        if bootloader_files is None:
            bootloader_files = self.bootloader_files
        boot_sources_base = os.path.realpath(os.path.join(destination, '..'))
        default_search_path = os.path.join(boot_sources_base, 'current')
        syslinux_search_path = os.path.join(default_search_path, 'syslinux')
        # In addition to the default search path search the previous
        # syslinux subdir as well. Previously MAAS didn't copy all of the
        # files required for PXE into the root tftp path. Also search the
        # paths the syslinux-common and pxelinux Ubuntu packages installs files
        # to on Xenial.
        search_paths = [
            default_search_path,
            syslinux_search_path,
            '/usr/lib/PXELINUX',
            '/usr/lib/syslinux/modules/bios',
        ]
        files_found = []
        for search_path in search_paths:
            for bootloader_file in bootloader_files:
                bootloader_src = os.path.join(search_path, bootloader_file)
                bootloader_src = os.path.realpath(bootloader_src)
                bootloader_dst = os.path.join(destination, bootloader_file)
                if (os.path.exists(bootloader_src)
                        and not os.path.exists(bootloader_dst)):
                    # If the file was found in a previous snapshot copy it as
                    # once we're done the previous snapshot will be deleted. If
                    # the file was found elsewhere on the filesystem create a
                    # symlink so we stay current with that source.
                    if boot_sources_base in bootloader_src:
                        atomic_copy(bootloader_src, bootloader_dst)
                    else:
                        atomic_symlink(bootloader_src, bootloader_dst)
                    files_found.append(bootloader_file)

        missing_files = [
            bootloader_file for bootloader_file in bootloader_files
            if bootloader_file not in files_found
        ]
        if missing_files != []:
            files_are_missing = True
            if log_missing:
                err_msg = (
                    "Unable to find a copy of %s in the SimpleStream or in "
                    "the system search paths %s. The %s bootloader type may "
                    "not work." % (', '.join(missing_files),
                                   ', '.join(search_paths), self.name))
                try_send_rack_event(EVENT_TYPES.RACK_IMPORT_ERROR, err_msg)
                maaslog.error(err_msg)
        else:
            files_are_missing = False

        syslinux_search_paths = [
            syslinux_search_path,
            '/usr/lib/syslinux/modules/bios',
        ]
        for search_path in syslinux_search_paths:
            if os.path.exists(search_path):
                syslinux_src = os.path.realpath(search_path)
                syslinux_dst = os.path.join(destination, 'syslinux')
                if destination in os.path.realpath(syslinux_src):
                    shutil.copy(bootloader_src, bootloader_dst)
                else:
                    atomic_symlink(syslinux_src, syslinux_dst)
                break

        return files_are_missing