[CRIU] [PATCH] test: add rpc tests

Ruslan Kuprieiev kupruser at gmail.com
Sun Sep 15 10:51:52 EDT 2013


On 09/15/2013 06:28 PM, Andrew Vagin wrote:
> On Sat, Sep 14, 2013 at 09:43:44PM +0400, Ruslan Kuprieiev wrote:
>> Hi!
>>
>> This patch provides tests(in C and Python) of rpc, that can be used
>> as examples.
>> Also, it contains shell script to automatize testing.
>>
>> This patch requires patch "rpc: change some "required" fields to
>> "optional" and rename rpc.proto to criu-rpc.proto" by Ruslan
>> Kuprieiev.
>>
>> Signed-off-by: Ruslan Kuprieiev <kupruser at gmail.com>
>>
> Offtop: Do you try to send message from non-leader thread? What do we
> expect in this case?

Hm... Could you explain in detail, please?

>> diff --git a/test/rpc/Makefile b/test/rpc/Makefile
>> new file mode 100644
>> index 0000000..726fd21
>> --- /dev/null
>> +++ b/test/rpc/Makefile
>> @@ -0,0 +1,20 @@
>> +all: test-c criu-rpc_pb2.py
>> +
>> +test-c: criu-rpc.pb-c.o test.o
>> +	gcc $^ -o test-c -lprotobuf-c
>> +
>> +#FIXME protoc*
>> +criu-rpc_pb2.py: ../../protobuf/criu-rpc.proto
>> +	protoc --proto_path=$(CURDIR)/../../protobuf/ --python_out=. $(CURDIR)/../../protobuf/criu-rpc.proto
>> +
>> +criu-rpc.pb-c.c: ../../protobuf/criu-rpc.proto
>> +	protoc-c --proto_path=$(CURDIR)/../../protobuf/ --c_out=. $(CURDIR)/../../protobuf/criu-rpc.proto
>> +
>> +clean:
>> +	rm -f *.o
>> +	rm -f criu*
>> +	rm -f test-c
>> +	rm -f _*
>> +	rm -rf imgs_*
>> +	rm -f *.log
>> +	rm -f *.socket
> Can we save all test files in one directory?
>
>> diff --git a/test/rpc/run.sh b/test/rpc/run.sh
>> new file mode 100755
>> index 0000000..17f0f09
>> --- /dev/null
>> +++ b/test/rpc/run.sh
>> @@ -0,0 +1,72 @@
>> +#!/bin/bash
>> +
>> +source ../env.sh || exit 1
>> +
>> +function my_print {
>> +	echo -e "\n**************************************************"
>> +	echo -e "\t\t"$1
>> +	echo -e "**************************************************\n"
>> +
>> +}
>> +
>> +function my_exit {
>> +	if [ $1 -ne 0 ]; then
>> +		echo FAIL
>> +	fi
>> +
>> +	my_print "Shutdown service server"
>> +	kill -SIGTERM ${SRV_PID}
>> +
>> +	exit $1
>> +}
>> +
>> +IMGS_DIR_C="imgs_c"
>> +IMGS_DIR_PY="imgs_py"
>> +SERV_LOG="service.log"
>> +REST_C_LOG="restore-c.log"
>> +REST_PY_LOG="restore-py.log"
>> +
>> +my_print "Build services"
>> +make clean && make || { echo "Failed to build"; exit 1; }
>> +if [ $? -ne 0 ]; then
>> +	echo FAIL
>> +	exit 1
>> +fi
>> +rm -rf ${IMGS_DIR_C} ${IMGS_DIR_PY} ${REST_LOG_C} ${REST_LOG_PY}
>> +mkdir ${IMGS_DIR_C} ${IMGS_DIR_PY}
>> +
>> +my_print "Start service server"
>> +setsid ${CRIU} service -v4 -o ${SERV_LOG} --address ./criu_service.socket &
>> +SRV_PID=${!}
>> +if [ $? -ne 0 ]; then
>> +	echo FAIL
>> +	exit 1
>> +fi
>> +echo PID ${SRV_PID}
>> +sleep 1 #server needs some time to initialize
>> +
>> +my_print "Run test-c"
>> +./test-c
>> +if [ $? -ne 0 ]; then
>> +	my_exit 1
>> +fi
> ./test-c
> my_exit $?

But it should not exit here, if everything is ok.

>> +
>> +my_print "Run test-py"
>> +./test.py
>> +if [ $? -ne 0 ]; then
>> +	my_exit 1
>> +fi
>> +
>> +my_print "Restore test-c"
>> +${CRIU} restore -v4 -o ${REST_C_LOG} -D ./imgs_c --shell-job
> Where do you check that the origin process was completed? If it is not,
> restore will fail, because the pid is busy.

test-c and test.py are waiting for response, and will exit only when 
response is received. So on restoring, origin process will be done.

>
>> +if [ $? -ne 0 ]; then
>> +	my_exit 1
>> +fi
>> +
>> +my_print "Restore test-py"
>> +${CRIU} restore -v4 -o ${REST_PY_LOG} -D ./imgs_py --shell-job
>> +if [ $? -ne 0 ]; then
>> +	my_exit 1
>> +fi
> The python test always returns 0

Oh, and i forgot to return -1 in test-c if resp->success = false...

>
>> +
>> +my_exit 0
>> diff --git a/test/rpc/test.c b/test/rpc/test.c
>> new file mode 100644
>> index 0000000..b67c082
>> --- /dev/null
>> +++ b/test/rpc/test.c
>> @@ -0,0 +1,160 @@
>> +#include "criu-rpc.pb-c.h"
>> +#include <stdbool.h>
>> +#include <sys/socket.h>
>> +#include <sys/un.h>
>> +#include <sys/fcntl.h>
>> +#include <stdio.h>
>> +
>> +#define MAX_MSG_SIZE 1024
>> +
>> +/*
>> + * recv_resp() reads criu msg from the socket,
>> + * unpacks it and unwraps a dump response.
>> + */
>> +
>> +static int recv_resp(int fd, CriuDumpResp **resp)
> Can we return CriuDumpResp *resp?
>
> static CriuDumpResp *resp recv_resp(int sk)

Yes, we can. But i was writing it using pb_read_one as an example.

>> +{
>> +	unsigned char buf[MAX_MSG_SIZE];
>> +	int len;
>> +
>> +	CriuMsg *msg = 0;
>> +
>> +	len = read(fd, buf, MAX_MSG_SIZE);
>> +	if (len == -1) {
>> +		perror("Can't read resp");
>> +		return -1;
>> +	}
>> +
>> +	msg = criu_msg__unpack(NULL, len, buf);
>> +	if (!msg) {
>> +		perror("Failed unpacking resp");
>> +		return -1;
>> +	}
>> +
>> +	if (msg->type == CRIU_MSG__TYPE__DUMPRESP)
>> +		*resp = msg->dump_resp;
>> +	else {
>> +		perror("Unexpected msg type");
>> +		return -1;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * send_req gets dump request as an argument,
>> + * wraps it into criu msg and writes it to the socket
>> + */
>> +
>> +static int send_req(int fd, CriuDumpReq *req)
>> +{
>> +	unsigned char buf[MAX_MSG_SIZE];
>> +	int len;
>> +
>> +	CriuMsg msg = CRIU_MSG__INIT;
>> +	msg.dump_req = req;
>> +	msg.type = CRIU_MSG__TYPE__DUMPREQ;
>> +
>> +	len = criu_msg__get_packed_size(&msg);
>> +
>> +	if (criu_msg__pack(&msg, buf) != len) {
>> +		perror("Failed packing request");
>> +		return -1;
>> +	}
>> +
>> +	if (write(fd, buf, len)  == -1) {
>> +		perror("Can't send request");
>> +		return -1;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +int main()
>> +{
>> +	CriuDumpReq req		= CRIU_DUMP_REQ__INIT;
>> +	CriuDumpResp *resp	= NULL;
>> +	int fd, dir_fd;
>> +	int ret = 0;
>> +	struct sockaddr_un addr;
>> +	socklen_t addr_len;
>> +
>> +	/*
>> +	 * Open a directory, in which criu will
>> +	 * put images
>> +	 */
>> +	dir_fd = open("./imgs_c", O_DIRECTORY);
>> +	if (dir_fd == -1) {
>> +		perror("Can't open dir");
>> +		return -1;
>> +	}
>> +
>> +	/*
>> +	 * Set dump options.
>> +	 * Checkout more in protobuf/criu-rpc.proto.
>> +	 */
>> +	req.has_leave_running	= true;
>> +	req.leave_running	= true;
>> +	req.images_dir_fd	= dir_fd;
>> +	req.has_shell_job	= true;
>> +	req.shell_job		= true;
>> +
>> +	/*
>> +	 * Connect to service socket
>> +	 */
>> +	fd = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
>> +	if (fd == -1) {
>> +		perror("Can't create socket");
>> +		return -1;
>> +	}
>> +
>> +	memset(&addr, 0, sizeof(addr));
>> +	addr.sun_family = AF_LOCAL;
>> +	strcpy(addr.sun_path, "./criu_service.socket");
>> +
>> +	addr_len = strlen(addr.sun_path) + sizeof(addr.sun_family);
>> +
>> +	ret = connect(fd, (struct sockaddr *) &addr, addr_len);
>> +	if (ret == -1) {
>> +		perror("Cant connect to socket");
>> +		goto exit;
>> +	}
>> +
>> +	/*
>> +	 * Send dump request
>> +	 */
>> +	ret = send_req(fd, &req);
>> +	if (ret == -1) {
>> +		perror("Can't send request");
>> +		goto exit;
>> +	}
>> +
>> +	/*
>> +	 * Recv dump response
>> +	 */
>> +	ret = recv_resp(fd, &resp);
>> +	if (ret == -1) {
>> +		perror("Can't recv response");
>> +		goto exit;
>> +	}
>> +
>> +	/*
>> +	 * Check response.
>> +	 */
>> +	if (resp->success)
>> +		puts("Success");
>> +	else {
>> +		puts("Fail");
>> +		ret = -1;
>> +		goto exit;
>> +	}
>> +
>> +	if (resp->has_restored && resp->restored)
>> +		puts("Restored");
>> +
>> +exit:
>> +	close(fd);
>> +	close(dir_fd);
>> +	criu_dump_resp__free_unpacked(resp, NULL);
>> +	return ret;
>> +}
>> diff --git a/test/rpc/test.py b/test/rpc/test.py
>> new file mode 100755
>> index 0000000..74f0830
>> --- /dev/null
>> +++ b/test/rpc/test.py
>> @@ -0,0 +1,35 @@
>> +#!/usr/bin/python
>> +
>> +import socket, os
>> +import criu_rpc_pb2
> Where is this module?

It is generated by protobuf and situated in the same directory as test.py.

>> +
>> +# Connect to service socket
>> +s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
>> +s.connect('./criu_service.socket')
>> +
>> +# Create criu msg, set it's type to dump request
>> +# and set dump options
>> +msg_req		= criu_rpc_pb2.criu_msg()
>> +msg_req.type	= criu_rpc_pb2.criu_msg.DUMPREQ
>> +msg_req.dump_req.leave_running	= True
>> +msg_req.dump_req.shell_job	= True
>> +msg_req.dump_req.images_dir_fd	= os.open('./imgs_py', os.O_DIRECTORY)
>> +
>> +# Send criu msg with request
>> +s.send(msg_req.SerializeToString())
>> +
>> +# Recv criu msg with response
>> +msg_resp	= criu_rpc_pb2.criu_msg()
>> +MAX_MSG_SIZE = 1024
>> +msg_resp.ParseFromString(s.recv(MAX_MSG_SIZE))
>> +
>> +if msg_resp.type != criu_rpc_pb2.criu_msg.DUMPRESP:
>> +	print 'Unexpected msg type'
> 	sys.exit(1)
>
>> +else:
>> +	if msg_resp.dump_resp.success:
>> +		print 'Success'
>> +	else:
>> +		print 'Fail'
>> +
>> +	if msg_resp.dump_resp.restored:
>> +		print 'Restored'
>>
>> _______________________________________________
>> CRIU mailing list
>> CRIU at openvz.org
>> https://lists.openvz.org/mailman/listinfo/criu


More information about the CRIU mailing list