예제 #1
0
 def test_read(self):
     data = factory.make_string(size=10).encode("ascii")
     reader = BytesReader(data)
     self.addCleanup(reader.finish)
     self.assertEqual(data[:7], reader.read(7))
     self.assertEqual(data[7:], reader.read(7))
     self.assertEqual(b"", reader.read(7))
예제 #2
0
파일: windows.py 프로젝트: zhangrb/maas
    def compose_bcd(self, kernel_params, local_host):
        """Composes the Windows boot configuration data.

        :param kernel_params: An instance of `KernelParameters`.
        :return: Binary data
        """
        preseed_url = self.compose_preseed_url(kernel_params.preseed_url)
        release_path = "%s\\source" % kernel_params.release
        remote_path = "\\\\%s\\reminst" % local_host
        loadoptions = "%s;%s;%s" % \
            (remote_path, release_path, preseed_url)

        # Generate the bcd file.
        bcd_template = self.get_resource_path(kernel_params, "bcd")
        if not os.path.isfile(bcd_template):
            raise BootMethodError("Failed to find bcd template: %s" %
                                  bcd_template)
        with tempdir() as tmp:
            bcd_tmp = os.path.join(tmp, "bcd")
            shutil.copyfile(bcd_template, bcd_tmp)

            bcd = Bcd(bcd_tmp)
            bcd.set_load_options(loadoptions)

            with open(bcd_tmp, 'rb') as stream:
                return BytesReader(stream.read())
예제 #3
0
    def test_render_GET_produces_from_reader(self):
        path = factory.make_name("path")
        ip = factory.make_ip_address()
        request = DummyRequest([path.encode("utf-8")])
        request.requestHeaders = Headers({
            "X-Server-Addr": ["192.168.1.1"],
            "X-Server-Port": ["5248"],
            "X-Forwarded-For": [ip],
            "X-Forwarded-Port": ["%s" % factory.pick_port()],
        })

        self.patch(http.log, "info")
        mock_deferLater = self.patch(http, "deferLater")
        mock_deferLater.side_effect = always_succeed_with(None)

        content = factory.make_string(size=100).encode("utf-8")
        reader = BytesReader(content)
        self.tftp.backend.get_reader.return_value = succeed(reader)

        resource = http.HTTPBootResource()
        yield self.render_GET(resource, request)

        self.assertEqual(
            [100], request.responseHeaders.getRawHeaders(b"Content-Length"))
        self.assertEqual(content, b"".join(request.written))
예제 #4
0
    def test_render_GET_produces_from_reader(self):
        path = factory.make_name('path')
        ip = factory.make_ip_address()
        request = DummyRequest([path.encode('utf-8')])
        request.requestHeaders = Headers({
            'X-Server-Addr': ['192.168.1.1'],
            'X-Server-Port': ['5248'],
            'X-Forwarded-For': [ip],
            'X-Forwarded-Port': ['%s' % factory.pick_port()],
        })

        self.patch(http.log, 'info')
        mock_deferLater = self.patch(http, 'deferLater')
        mock_deferLater.side_effect = always_succeed_with(None)

        content = factory.make_string(size=100).encode('utf-8')
        reader = BytesReader(content)
        self.tftp.backend.get_reader.return_value = succeed(reader)

        resource = http.HTTPBootResource()
        yield self.render_GET(resource, request)

        self.assertEquals(
            [100], request.responseHeaders.getRawHeaders(b'Content-Length'))
        self.assertEquals(content, b''.join(request.written))
예제 #5
0
    def get_reader(self, backend, kernel_params, mac=None, **extra):
        """Render a configuration file as a unicode string.

        :param backend: requesting backend
        :param kernel_params: An instance of `KernelParameters`.
        :param mac: Optional MAC address discovered by `match_path`.
        :param extra: Allow for other arguments. This is a safety valve;
            parameters generated in another component (for example, see
            `TFTPBackend.get_boot_method_reader`) won't cause this to break.
        """
        template = self.get_template(
            kernel_params.purpose, kernel_params.arch, kernel_params.subarch
        )
        namespace = self.compose_template_namespace(kernel_params)

        def kernel_command(params):
            cmd_line = compose_kernel_command_line(params)
            # Modify the kernel_command to inject the BOOTIF. S390X doesn't
            # support the IPAPPEND pxelinux flag.
            if mac is not None:
                return "%s BOOTIF=%s" % (cmd_line, format_bootif(mac))
            return cmd_line

        namespace["kernel_command"] = kernel_command

        # We are going to do 2 passes of tempita substitution because there
        # may be things like kernel params which include variables that can
        # only be populated at run time and thus contain variables themselves.
        # For example, an OS may need a kernel parameter that points back to
        # fs_host and the kernel parameter comes through as part of the simple
        # stream.
        step1 = template.substitute(namespace)
        return BytesReader(
            tempita.Template(step1).substitute(namespace).encode("utf-8")
        )
예제 #6
0
    def get_reader(self, backend, kernel_params, **extra):
        """Render a configuration file as a unicode string.

        :param backend: requesting backend
        :param kernel_params: An instance of `KernelParameters`.
        :param extra: Allow for other arguments. This is a safety valve;
            parameters generated in another component (for example, see
            `TFTPBackend.get_boot_method_reader`) won't cause this to break.
        """
        def kernel_command(params):
            """Return the kernel command, adjusted for UEFI to work.

            See the similar function in BootMethod, and the callsite below.

            The issue here is that grub throws a fit when the braces on
            cc:{...}end_cc are hit, for whatever reason.  Escape _JUST_ those.
            """
            return re.sub(
                r"cc:{(?P<inner>[^}]*)}end_cc",
                r"cc:\{\g<inner>\}end_cc",
                compose_kernel_command_line(params),
            )

        template = self.get_template(kernel_params.purpose, kernel_params.arch,
                                     kernel_params.subarch)
        namespace = self.compose_template_namespace(kernel_params)
        # Bug#1651452 - kernel command needs some extra escapes, but ONLY for
        # UEFI.  And so we fix it here, instead of in the common code.  See
        # also src/provisioningserver/kernel_opts.py.
        namespace["kernel_command"] = kernel_command
        return BytesReader(template.substitute(namespace).encode("utf-8"))
예제 #7
0
파일: ipxe.py 프로젝트: tai271828/maas
    def get_reader(self, backend, kernel_params, **extra):
        """Render a configuration file as a unicode string.

        :param backend: requesting backend
        :param kernel_params: An instance of `KernelParameters`.
        :param extra: Allow for other arguments. This is a safety valve;
            parameters generated in another component (for example, see
            `TFTPBackend.get_boot_method_reader`) won't cause this to break.
        """
        template = self.get_template(
            kernel_params.purpose, kernel_params.arch, kernel_params.subarch
        )
        kernel_params.mac = extra.get("mac", "")
        namespace = self.compose_template_namespace(kernel_params)

        # We are going to do 2 passes of tempita substitution because there
        # may be things like kernel params which include variables that can
        # only be populated at run time and thus contain variables themselves.
        # For example, an OS may need a kernel parameter that points back to
        # fs_host and the kernel parameter comes through as part of
        # the simplestream.
        step1 = template.substitute(namespace)
        return BytesReader(
            tempita.Template(step1).substitute(namespace).encode("utf-8")
        )
예제 #8
0
파일: test_tftp.py 프로젝트: zeronewb/maas
    def test_get_boot_method_reader_returns_rendered_params(self):
        # Fake configuration parameters, as discovered from the file path.
        fake_params = {"mac": factory.make_mac_address("-")}
        # Fake kernel configuration parameters, as returned from the RPC call.
        fake_kernel_params = make_kernel_parameters()
        fake_params = fake_kernel_params._asdict()

        # Stub the output of list_boot_images so the label is set in the
        # kernel parameters.
        boot_image = {
            "osystem": fake_params["osystem"],
            "release": fake_params["release"],
            "architecture": fake_params["arch"],
            "subarchitecture": fake_params["subarch"],
            "purpose": fake_params["purpose"],
            "supported_subarches": "",
            "label": fake_params["label"],
        }
        self.patch(tftp_module, "list_boot_images").return_value = [boot_image]
        del fake_params["label"]

        # Stub RPC call to return the fake configuration parameters.
        client = Mock()
        client.localIdent = factory.make_name("system_id")
        client.return_value = succeed(fake_params)
        client_service = Mock()
        client_service.getClientNow.return_value = succeed(client)

        # get_boot_method_reader() takes a dict() of parameters and returns an
        # `IReader` of a PXE configuration, rendered by
        # `PXEBootMethod.get_reader`.
        backend = TFTPBackend(self.make_dir(), client_service)

        # Stub get_reader to return the render parameters.
        method = PXEBootMethod()
        fake_render_result = factory.make_name("render").encode("utf-8")
        render_patch = self.patch(method, "get_reader")
        render_patch.return_value = BytesReader(fake_render_result)

        # Get the rendered configuration, which will actually be a JSON dump
        # of the render-time parameters.
        params_with_ip = dict(fake_params)
        params_with_ip['remote_ip'] = factory.make_ipv4_address()
        reader = yield backend.get_boot_method_reader(method, params_with_ip)
        self.addCleanup(reader.finish)
        self.assertIsInstance(reader, BytesReader)
        output = reader.read(10000)

        # The result has been rendered by `method.get_reader`.
        self.assertEqual(fake_render_result, output)
        self.assertThat(
            method.get_reader,
            MockCalledOnceWith(backend,
                               kernel_params=fake_kernel_params,
                               **params_with_ip))
예제 #9
0
 def test_track_time(self):
     prometheus_metrics = create_metrics(METRICS_DEFINITIONS)
     session = TransferTimeTrackingSession(
         'file.txt', BytesReader(b'some data'), _clock=Clock(),
         prometheus_metrics=prometheus_metrics)
     session.transport = Mock()
     session.startProtocol()
     session.cancel()
     metrics = prometheus_metrics.generate_latest().decode('ascii')
     self.assertIn(
         'maas_tftp_file_transfer_latency_count{filename="file.txt"} 1.0',
         metrics)
예제 #10
0
    def get_reader(self, backend, kernel_params, mac=None, path=None, **extra):
        """Render a configuration file as a unicode string.

        :param backend: requesting backend
        :param kernel_params: An instance of `KernelParameters`.
        :param path: Optional MAC address discovered by `match_path`.
        :param path: Optional path discovered by `match_path`.
        :param extra: Allow for other arguments. This is a safety valve;
            parameters generated in another component (for example, see
            `TFTPBackend.get_config_reader`) won't cause this to break.
        """
        if path is not None:
            # This is a request for a static file, not a configuration file.
            # The prefix was already trimmed by `match_path` so we need only
            # return a FilesystemReader for `path` beneath the backend's base.
            target_path = backend.base.descendant(path.split("/"))
            return FilesystemReader(target_path)

        # Return empty config for S390x local. S390x fails to
        # support the LOCALBOOT flag. Empty config will allow it
        # to select the first device.
        if kernel_params.purpose == "local":
            return BytesReader("".encode("utf-8"))

        template = self.get_template(
            kernel_params.purpose, kernel_params.arch, kernel_params.subarch
        )
        namespace = self.compose_template_namespace(kernel_params)

        # Modify the kernel_command to inject the BOOTIF. S390x fails to
        # support the IPAPPEND pxelinux flag.
        def kernel_command(params):
            cmd_line = compose_kernel_command_line(params)
            if mac is not None:
                return "%s BOOTIF=%s" % (cmd_line, format_bootif(mac))
            return cmd_line

        namespace["kernel_command"] = kernel_command
        return BytesReader(template.substitute(namespace).encode("utf-8"))
예제 #11
0
    def test_get_boot_method_reader_rendered_parms_for_depoyed_ephemeral(self):
        # Fake kernel configuration parameters, as returned from the RPC call.
        fake_kernel_params = make_kernel_parameters(
            purpose="local", label="local", osystem="caringo"
        )
        fake_params = fake_kernel_params._asdict()

        # Stub the output of list_boot_images so the label is set in the
        # kernel parameters.
        boot_image = {
            "osystem": fake_params["osystem"],
            "release": fake_params["release"],
            "architecture": fake_params["arch"],
            "subarchitecture": fake_params["subarch"],
            "purpose": "ephemeral",
            "supported_subarches": "",
            "label": fake_params["label"],
        }
        self.patch(tftp_module, "list_boot_images").return_value = [boot_image]

        del fake_params["label"]

        # Stub RPC call to return the fake configuration parameters.
        client = Mock()
        client.localIdent = factory.make_name("system_id")
        client.return_value = succeed(fake_params)
        client_service = Mock()
        client_service.getClient.return_value = client

        # get_boot_method_reader() takes a dict() of parameters and returns an
        # `IReader` of a PXE configuration, rendered by
        # `PXEBootMethod.get_reader`.
        backend = TFTPBackend(self.make_dir(), client_service)

        # Stub get_reader to return the render parameters.
        method = PXEBootMethod()
        fake_render_result = factory.make_name("render").encode("utf-8")
        render_patch = self.patch(method, "get_reader")
        render_patch.return_value = BytesReader(fake_render_result)

        # Get the rendered configuration, which will actually be a JSON dump
        # of the render-time parameters.
        reader = yield backend.get_boot_method_reader(method, fake_params)
        self.addCleanup(reader.finish)
        self.assertIsInstance(reader, BytesReader)
        output = reader.read(10000)

        # The result has been rendered by `method.get_reader`.
        self.assertEqual(fake_render_result, output)
예제 #12
0
    def test_get_boot_method_reader_returns_rendered_params_local_device(self):
        # Fake kernel configuration parameters, as returned from the RPC call.
        fake_kernel_params = make_kernel_parameters(
            purpose="local", label="local"
        )
        fake_params = fake_kernel_params._asdict()
        del fake_params["label"]

        # Set purpose to `local-device` as this is what will be passed on.
        fake_params["purpose"] = "local-device"

        # Stub RPC call to return the fake configuration parameters.
        client = Mock()
        client.localIdent = factory.make_name("system_id")
        client.return_value = succeed(fake_params)
        client_service = Mock()
        client_service.getClientNow.return_value = succeed(client)

        # get_boot_method_reader() takes a dict() of parameters and returns an
        # `IReader` of a PXE configuration, rendered by
        # `PXEBootMethod.get_reader`.
        backend = TFTPBackend(self.make_dir(), client_service)

        # Stub get_reader to return the render parameters.
        method = PXEBootMethod()
        fake_render_result = factory.make_name("render").encode("utf-8")
        render_patch = self.patch(method, "get_reader")
        render_patch.return_value = BytesReader(fake_render_result)

        # Get the rendered configuration, which will actually be a JSON dump
        # of the render-time parameters.
        params_with_ip = dict(fake_params)
        params_with_ip["remote_ip"] = factory.make_ipv4_address()
        reader = yield backend.get_boot_method_reader(method, params_with_ip)
        self.addCleanup(reader.finish)
        self.assertIsInstance(reader, BytesReader)
        output = reader.read(10000)

        # The result has been rendered by `method.get_reader`.
        self.assertEqual(fake_render_result, output)
        self.assertThat(
            method.get_reader,
            MockCalledOnceWith(
                backend, kernel_params=fake_kernel_params, **params_with_ip
            ),
        )
예제 #13
0
파일: grub.py 프로젝트: casual-lemon/maas
    def get_reader(self, backend, kernel_params, protocol, **extra):
        """Render a configuration file as a unicode string.

        :param backend: requesting backend
        :param kernel_params: An instance of `KernelParameters`.
        :param protocol: The protocol the transfer is happening over.
        :param extra: Allow for other arguments. This is a safety valve;
            parameters generated in another component (for example, see
            `TFTPBackend.get_boot_method_reader`) won't cause this to break.
        """
        def kernel_command(params):
            """Return the kernel command, adjusted for UEFI to work.

            See the similar function in BootMethod, and the callsite below.

            The issue here is that grub throws a fit when the braces on
            cc:{...}end_cc are hit, for whatever reason.  Escape _JUST_ those.
            """
            return re.sub(
                r"cc:{(?P<inner>[^}]*)}end_cc",
                r"cc:\{\g<inner>\}end_cc",
                compose_kernel_command_line(params),
            )

        template = self.get_template(kernel_params.purpose, kernel_params.arch,
                                     kernel_params.subarch)
        namespace = self.compose_template_namespace(kernel_params)

        # TFTP is much slower than HTTP. If GRUB was transfered over TFTP use
        # GRUBs internal HTTP implementation to download the kernel and initrd.
        # If HTTP or HTTPS was used don't specify host to continue to use the
        # UEFI firmware's internal HTTP implementation.
        if protocol == "tftp":
            namespace["fs_efihost"] = "(http,%s:5248)/images/" % (
                convert_host_to_uri_str(kernel_params.fs_host))
        else:
            namespace["fs_efihost"] = "/images/"

        # Bug#1651452 - kernel command needs some extra escapes, but ONLY for
        # UEFI.  And so we fix it here, instead of in the common code.  See
        # also src/provisioningserver/kernel_opts.py.
        namespace["kernel_command"] = kernel_command
        return BytesReader(
            template.substitute(namespace).strip().encode("utf-8"))
예제 #14
0
 def get_boot_method_reader(boot_method, params):
     params_json = json.dumps(params).encode("ascii")
     params_json_reader = BytesReader(params_json)
     return succeed(params_json_reader)
예제 #15
0
    def test_get_boot_method_reader_grabs_new_client_on_lost_conn(self):
        # Fake kernel configuration parameters, as returned from the RPC call.
        fake_kernel_params = make_kernel_parameters()
        fake_params = fake_kernel_params._asdict()

        # Stub the output of list_boot_images so the label is set in the
        # kernel parameters.
        boot_image = {
            "osystem": fake_params["osystem"],
            "release": fake_params["release"],
            "architecture": fake_params["arch"],
            "subarchitecture": fake_params["subarch"],
            "purpose": fake_params["purpose"],
            "supported_subarches": "",
            "label": fake_params["label"],
        }
        self.patch(tftp_module, "list_boot_images").return_value = [boot_image]
        del fake_params["label"]

        # Stub RPC call to return the fake configuration parameters.
        clients = []
        for _ in range(10):
            client = Mock()
            client.localIdent = factory.make_name("system_id")
            client.side_effect = lambda *args, **kwargs: (
                succeed(dict(fake_params))
            )
            clients.append(client)
        client_service = Mock()
        client_service.getClientNow.side_effect = [
            succeed(client) for client in clients
        ]
        client_service.getAllClients.side_effect = [clients[1:], clients[2:]]

        # get_boot_method_reader() takes a dict() of parameters and returns an
        # `IReader` of a PXE configuration, rendered by
        # `PXEBootMethod.get_reader`.
        backend = TFTPBackend(self.make_dir(), client_service)

        # Stub get_reader to return the render parameters.
        method = PXEBootMethod()
        fake_render_result = factory.make_name("render").encode("utf-8")
        render_patch = self.patch(method, "get_reader")
        render_patch.return_value = BytesReader(fake_render_result)

        # Get the reader once.
        remote_ip = factory.make_ipv4_address()
        params_with_ip = dict(fake_params)
        params_with_ip["remote_ip"] = remote_ip
        reader = yield backend.get_boot_method_reader(method, params_with_ip)
        self.addCleanup(reader.finish)

        # The first client is now saved.
        self.assertEqual(clients[0], backend.client_to_remote[remote_ip])

        # Get the reader twice.
        params_with_ip = dict(fake_params)
        params_with_ip["remote_ip"] = remote_ip
        reader = yield backend.get_boot_method_reader(method, params_with_ip)
        self.addCleanup(reader.finish)

        # The second client is now saved.
        self.assertEqual(clients[1], backend.client_to_remote[remote_ip])

        # Only the first and second client should have been called once, and
        # all the other clients should not have been called.
        self.assertEqual(1, clients[0].call_count)
        self.assertEqual(1, clients[1].call_count)
        for idx in range(2, 10):
            self.assertThat(clients[idx], MockNotCalled())
예제 #16
0
 def test_interfaces(self):
     reader = BytesReader(b"")
     self.addCleanup(reader.finish)
     verifyObject(IReader, reader)
예제 #17
0
 def get_reader(backend, kernel_params, **extra):
     return BytesReader("")
예제 #18
0
 def test_finish(self):
     reader = BytesReader(b"1234")
     reader.finish()
     self.assertRaises(ValueError, reader.read, 1)