diff --git a/lib/rpc.py b/lib/rpc.py index 954beb7d62d031cd1c4741d7d14cc3e37bbca233..c9ac36fe07b2ddbf4aabbb67fb5eea8bfeca92c1 100644 --- a/lib/rpc.py +++ b/lib/rpc.py @@ -516,13 +516,19 @@ def _EncodeNodeToDiskDict(value): for name, disks in value.items()) -def _PrepareFileUpload(filename): +def _PrepareFileUpload(getents_fn, filename): """Loads a file and prepares it for an upload to nodes. """ + # TODO: Use ReadFile(preread=...) and os.fstat data = _Compress(utils.ReadFile(filename)) st = os.stat(filename) - getents = runtime.GetEnts() + + if getents_fn is None: + getents_fn = runtime.GetEnts + + getents = getents_fn() + return [filename, data, st.st_mode, getents.LookupUid(st.st_uid), getents.LookupGid(st.st_gid), st.st_atime, st.st_mtime] @@ -569,7 +575,6 @@ _ENCODERS = { rpc_defs.ED_OBJECT_DICT: _ObjectToDict, rpc_defs.ED_OBJECT_DICT_LIST: _ObjectListToDict, rpc_defs.ED_NODE_TO_DISK_DICT: _EncodeNodeToDiskDict, - rpc_defs.ED_FILE_DETAILS: _PrepareFileUpload, rpc_defs.ED_COMPRESS: _Compress, rpc_defs.ED_FINALIZE_EXPORT_DISKS: _PrepareFinalizeExportDisks, rpc_defs.ED_IMPEXP_IO: _EncodeImportExportIO, @@ -597,11 +602,14 @@ class RpcRunner(_RpcClientBase, encoders = _ENCODERS.copy() - # Add encoders requiring configuration object encoders.update({ + # Encoders requiring configuration object rpc_defs.ED_INST_DICT: self._InstDict, rpc_defs.ED_INST_DICT_HVP_BEP: self._InstDictHvpBep, rpc_defs.ED_INST_DICT_OSP: self._InstDictOsp, + + # Encoders with special requirements + rpc_defs.ED_FILE_DETAILS: compat.partial(_PrepareFileUpload, _getents), }) # Resolver using configuration diff --git a/test/ganeti.rpc_unittest.py b/test/ganeti.rpc_unittest.py index a9349a67e52df2b327b21f34a83b617f94e0d8d5..691c99c68d3950dfadbc2471f51b2b70f70ddf5d 100755 --- a/test/ganeti.rpc_unittest.py +++ b/test/ganeti.rpc_unittest.py @@ -25,6 +25,7 @@ import os import sys import unittest import random +import tempfile from ganeti import constants from ganeti import compat @@ -37,6 +38,7 @@ from ganeti import objects from ganeti import backend import testutils +import mocks class _FakeRequestProcessor: @@ -682,9 +684,50 @@ class TestRpcClientBase(unittest.TestCase): self.assertFalse(res.fail_msg) +class _FakeConfigForRpcRunner: + GetAllNodesInfo = NotImplemented + + def GetNodeInfo(self, name): + return objects.Node(name=name) + + class TestRpcRunner(unittest.TestCase): def testUploadFile(self): - runner = rpc.RpcRunner(_req_process_fn=http_proc) + data = 1779 * "Hello World\n" + + tmpfile = tempfile.NamedTemporaryFile() + tmpfile.write(data) + tmpfile.flush() + st = os.stat(tmpfile.name) + + def _VerifyRequest(req): + (uldata, ) = serializer.LoadJson(req.post_data) + self.assertEqual(len(uldata), 7) + self.assertEqual(uldata[0], tmpfile.name) + self.assertEqual(list(uldata[1]), list(rpc._Compress(data))) + self.assertEqual(uldata[2], st.st_mode) + self.assertEqual(uldata[3], "user%s" % os.getuid()) + self.assertEqual(uldata[4], "group%s" % os.getgid()) + self.assertEqual(uldata[5], st.st_atime) + self.assertEqual(uldata[6], st.st_mtime) + + req.success = True + req.resp_status_code = http.HTTP_OK + req.resp_body = serializer.DumpJson((True, None)) + + http_proc = _FakeRequestProcessor(_VerifyRequest) + cfg = _FakeConfigForRpcRunner() + runner = rpc.RpcRunner(cfg, None, _req_process_fn=http_proc, + _getents=mocks.FakeGetentResolver) + + nodes = [ + "node1.example.com", + ] + + result = runner.call_upload_file(nodes, tmpfile.name) + self.assertEqual(len(result), len(nodes)) + for (idx, (node, res)) in enumerate(result.items()): + self.assertFalse(res.fail_msg) if __name__ == "__main__": diff --git a/test/mocks.py b/test/mocks.py index 61eafbca2400b98779e6dc0b73ed6004867c5aa7..3d5d68cdcfa0efafc6bccbd940c7fd13d028ca51 100644 --- a/test/mocks.py +++ b/test/mocks.py @@ -109,3 +109,9 @@ class FakeGetentResolver: self.daemons_gid = gid self.admin_gid = gid + + def LookupUid(self, uid): + return "user%s" % uid + + def LookupGid(self, gid): + return "group%s" % gid