示例#1
0
    def test_get_kernel_params_filters_out_unnecessary_arguments(self):
        params_okay = {
            name.decode("ascii"): factory.make_name("value")
            for name, _ in GetBootConfig.arguments
        }
        params_other = {
            factory.make_name("name"): factory.make_name("value")
            for _ in range(3)
        }
        params_all = params_okay.copy()
        params_all.update(params_other)

        client = Mock()
        client.localIdent = params_okay["system_id"]
        client_service = Mock()
        client_service.getClientNow.return_value = succeed(client)

        backend = TFTPBackend(self.make_dir(), client_service)
        backend.fetcher = Mock()

        backend.get_kernel_params(params_all)

        self.assertThat(
            backend.fetcher,
            MockCalledOnceWith(client, GetBootConfig, **params_okay),
        )
示例#2
0
    def test_get_reader_converts_BootConfigNoResponse_to_FileNotFound(self):
        client = Mock()
        client.localIdent = factory.make_name("system_id")
        client.return_value = fail(BootConfigNoResponse())
        client_service = Mock()
        client_service.getClientNow.return_value = succeed(client)
        backend = TFTPBackend(self.make_dir(), client_service)

        with ExpectedException(FileNotFound):
            yield backend.get_reader(b"pxelinux.cfg/default")
示例#3
0
    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))
示例#4
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)
示例#5
0
    def test_get_boot_method_render_substitutes_armhf_in_params(self):
        # get_config_reader() should substitute "arm" for "armhf" in the
        # arch field of the parameters (mapping from pxe to maas
        # namespace).
        config_path = b"pxelinux.cfg/default-arm"
        backend = TFTPBackend(self.make_dir(), "http://example.com/")
        # python-tx-tftp sets up call context so that backends can discover
        # more about the environment in which they're running.
        call_context = {
            "local": (factory.make_ipv4_address(), factory.pick_port()),
            "remote": (factory.make_ipv4_address(), factory.pick_port()),
        }

        @partial(self.patch, backend, "get_boot_method_reader")
        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)

        reader = yield context.call(
            call_context, backend.get_reader, config_path
        )
        output = reader.read(10000).decode("ascii")
        observed_params = json.loads(output)
        # XXX: GavinPanella 2015-11-25 bug=1519804: get_by_pxealias() on
        # ArchitectureRegistry is not stable, so we permit either here.
        self.assertIn(observed_params["arch"], ["armhf", "arm64"])
示例#6
0
    def _test_get_render_file(self, local, remote):
        # For paths matching PXEBootMethod.match_path, TFTPBackend.get_reader()
        # returns a Deferred that will yield a BytesReader.
        mac = factory.make_mac_address("-")
        config_path = compose_config_path(mac)
        backend = TFTPBackend(self.make_dir(), Mock())
        # python-tx-tftp sets up call context so that backends can discover
        # more about the environment in which they're running.
        call_context = {"local": local, "remote": remote}

        @partial(self.patch, backend, "get_boot_method_reader")
        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)

        reader = yield context.call(
            call_context, backend.get_reader, config_path
        )
        output = reader.read(10000).decode("ascii")
        # The addresses provided by python-tx-tftp in the call context are
        # passed over the wire as address:port strings.
        expected_params = {
            "mac": mac,
            "local_ip": call_context["local"][0],  # address only.
            "remote_ip": call_context["remote"][0],  # address only.
            "bios_boot_method": "pxe",
        }
        observed_params = json.loads(output)
        self.assertEqual(expected_params, observed_params)
示例#7
0
    def test_get_reader_handles_backslashes_in_path(self):
        data = factory.make_string().encode("ascii")
        temp_dir = self.make_dir()
        subdir = factory.make_name("subdir")
        filename = factory.make_name("file")
        os.mkdir(os.path.join(temp_dir, subdir))
        factory.make_file(os.path.join(temp_dir, subdir), filename, data)

        path = ("\\%s\\%s" % (subdir, filename)).encode("ascii")
        backend = TFTPBackend(temp_dir, "http://nowhere.example.com/")
        reader = yield backend.get_reader(path)

        self.addCleanup(reader.finish)
        self.assertEqual(len(data), reader.size)
        self.assertEqual(data, reader.read(len(data)))
        self.assertEqual(b"", reader.read(1))
示例#8
0
 def test_init(self):
     temp_dir = self.make_dir()
     client_service = Mock()
     backend = TFTPBackend(temp_dir, client_service)
     self.assertEqual((True, False), (backend.can_read, backend.can_write))
     self.assertEqual(temp_dir, backend.base.path)
     self.assertEqual(client_service, backend.client_service)
示例#9
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
            ),
        )
示例#10
0
    def test_get_reader_converts_other_exceptions_to_tftp_error(self):
        exception_type = factory.make_exception_type()
        exception_message = factory.make_string()
        client = Mock()
        client.localIdent = factory.make_name("system_id")
        client.return_value = fail(exception_type(exception_message))
        client_service = Mock()
        client_service.getClientNow.return_value = succeed(client)
        backend = TFTPBackend(self.make_dir(), client_service)

        with TwistedLoggerFixture() as logger:
            with ExpectedException(BackendError, re.escape(exception_message)):
                yield backend.get_reader(b'pxelinux.cfg/default')

        # The original exception is logged.
        self.assertDocTestMatches(
            """\
            TFTP back-end failed.
            Traceback (most recent call last):
            ...
            maastesting.factory.TestException#...
            """, logger.output)
示例#11
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())
示例#12
0
 def get_reader(self, data):
     temp_file = self.make_file(name="example", contents=data)
     temp_dir = os.path.dirname(temp_file)
     backend = TFTPBackend(temp_dir, Mock())
     return backend.get_reader(b"example")