[CRIU] [PATCH] test: add rpc tests

Andrew Vagin avagin at parallels.com
Sun Sep 15 10:28:57 EDT 2013


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?

> 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 $?

> +
> +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.

> +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

> +
> +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)

> +{
> +	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?

> +
> +# 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