GlusterFS/코드 분석/xlator/mgmt/snapshot

From PGWiki

CLI

  • cli_snapshot_remove_reply
  • cli_snapshot_config_display
  • cli_snapshot_list
  • cli_snapshot_status
  • gf_cli_snapshot_get_data_from_dict
  • gf_cli_snapshot_cbk
  • gf_cli_snapshot_for_delete
  • gf_cli_snapshot_for_status
  • gf_cli_snapshot

GlusterD

서비스

char *snapd_svc_name = "snapd";

glusterd에서 실행시키는 서비스 프로세스의 명은 **snapd**

void
glusterd_snapdsvc_build (glusterd_svc_t *svc)
{
    svc->manager = glusterd_snapdsvc_manager;
    svc->start   = glusterd_snapdsvc_start;
    svc->stop    = glusterd_snapdsvc_stop;
}

서비스의 시작점은 glsuterd_snapdsvc_manager(), 시작과 종료는 각각 glusterd_snapdsvc_start, glusterd_snapdsvc_stop


서비스 초기화

int
glusterd_snapdsvc_manager(
    glusterd_svc_t  *svc,   # glusterd가  서비스 별로 유지하는 서비스 컨텍스트
    void            *data,  #  서비스 관리 함수에 전달되는 데이터이며,
                            # snapd의 경우 볼륨 정보(glusterd_volinfo_t)가 전달됨
    int             flags   # 서비스 구동과 관련된 플래그이며,
                            # 주로 서비스 프로세스 시작 방식을 지정함
)
  • svc->inited를 통해 서비스가 초기화되지 않은 상태라면 glusterd_snapdsvc_init(volinfo를 호출하고 svc->inited = _gf_true
  • glusterd_is_snapd_enabled(volinfo)를 호출하여 해당 볼륨에 대한 스냅샷 활성화 설정을 확인
    • 해당 볼륨의 features.uss를 반환함
  • 활성화하도록 설정되어 있다면 서비스 시작 절차 수행
    • glusterd_is_volume_started(volinfo)을 통해 볼륨이 시작되었는지 확인하고, 시작되지 않은 상태에서 서비스 프로세스만 존재한다면 서비스 프로세스를 종료하고 빠져나감
      • volinfo->status == GLUSTERD_STATUS_STARTED
    • glusterd_snapdsvc_create_volfile(volinfo)를 호출하여 snapd를 위한 volfile을 생성
      1. glusterd_svc_build_snapd_volfile(volinfo, filename, PATH_MAX)을 호출하여 snapd의 volfile이 위치할 경로를 filename에 기록
      2. glusterd_snapdsvc_generate_volfile(&graph, filename)
      3. volgen_write_volfile(&graph, filename)
      4. volgen_graph_free(&graph)
    • svc->start(svc, flags)
      • 서비스 객체로부터 volinfo->snapd
    • glusterd_volinfo_ref(volinfo)
      • volinfo의 참조 계수(volinfo->refcnt를 1만큼 증가
    • glusterd_conn_connect(&(svc->conn))
      • rpc_clnt_start(conn->rpc)
  • 활성화 설정이 되어있지 않다면 기존 서비스 유무를 확인하여 중단 수행
    • glusterd_proc_is_running(&svc->proc))를 호출하여 서비스 프로세스가 동작 중이라면 서비스를 중단하고 snapd의 포트 번호volinfo->snapd.port를 0으로 기록한다.
      • gf_is_service_running(proc->pidfile, &pid)는 PID 파일을 읽어온 후에 그 PID를 매개 변수로 전달 받는 gf_is_pid_running(*pid)/proc/<PID>/cmdline에 접근이 가능한 지를 통해 서비스 프로세스의 동작 유무를 판별한다.
    • svc->stop(svc, SIGTERM)
      • glusterd_svc_stop()는 일반적인 절차이므로 생략
int
glusterd_snapdsvc_init(
    void *data  # snapd 초기화를 수행하기 위해 필요한 볼륨 정보
                # (glusterd_volinfo_t)이며, glusterd_snapdsvc_manager() 호출 때
                # 전달된 매개 변수인 data와 동일
)
  • glusterd_snapdsvc_rpc_notify(...) 함수의 포인터를 notify에 기록
    • 해당 함수에 대한 설명은 아래 참조
  • glusterd_svc_build_snapd_rundir(volinfo, rundir, sizeof(rundir))
    • GLUSTERD_GET_VOLUME_PID_DIR() 매크로 함수를 통해 실행 시간에 사용되는 데이터가 저장될 디렉터리 경로명을 얻음
      • 스냅샷 볼륨일 경우에는 <workdir>/snaps/<volinfo->snapshot->snapname>/<volinfo->volname>
      • 이외에는 <workdir>/vols/<volinfo->volname
  • glusterd_svc_create_rundir(rundir)
    • 매개 변수로 전달 받은 rundir을 매개 변수로 하는 mkdir_p(rundir, 0777, _gf_true)을 호출하여 디렉터리 생성
  • Initialize the connection mgmt
  • glusterd_svc_build_snapd_socket_filepath(volinfo, sockpath, sizeof(sockpath)
    • glusterd_svc_build_snapd_rundir(...) 함수를 호출하여 런타임 디렉터리 경로명을 다시 얻음(초기화 과정에서는 굳이 불필요해 보임)
    • snprintf(sockfilepath, sizeof(sockfilepath), "%s/run-%s", rundir, uuid_utoa(MY_UUID))를 통해 런타임 디렉터리 하위의 소켓 파일 경로명을 sockfilepath 변수에 기록
      • MY_UUID는 glusterd의 UUID이며, __glusterd_uuid()의 호출 결과를 반환함
    • glusterd_set_socket_filepath(sockfilepath, path, path_len)를 호출하여 sockfilepath를 MD5 해싱한 결과인 md5_sum을 파일 이름으로 하는 "<GLUSTERD_SOCK_DIR>/<md5_sum>.socket"의 경로명을 path에 기록
      • MD5 해싱에는 md5_wrapper() 함수가 사용됨
  • glusterd_conn_init(&(svc->conn), sockpath, 600, notify)을 호출하여 sockpath 경로에 유닉스 소켓을 생성하는 RPC 서비스를 초기화
    • glusterd_conn_get_svc_object(conn)를 호출하여 conn의 CDS 리스트로부터 svc 객체를 얻음
    • rpc_transport_unix_options_build(&options, sockpath, frame_timeout)를 호출하여 options에 RPC 옵션들을 기록
      • "transport.socket.connect-path" = sockpath
      • "transport.address_family" = "unix"
      • "transport.socket_nodelay" = "off"
      • "transport-type" = "socket"
      • "transport.socket_keepalive" = "off"
      • "frame-timeout" = frame_timeout
    • "transport.socket.ignore_enoent" 옵션을 "no"로 설정
    • rpc_clnt_new(options, this, (char *)svc->name, 16)를 호출하여 RPC 클라이언트 자료 구조를 새로 생성
    • rpc_clnt_register_notify(rpc, glusterd_conn_common_notify, conn)를 호출하여 RPC 클라이언트의 이벤트 콜백을 등록
      • glusterd_conn_common_notify() 함수는 RPC 이벤트를 알리는 절차를 수행하며, 이벤트 콜백을 호출할 때 전달하는 매개 변수는 conn->notify(glusterd_conn_t *conn, rpc_clnt_event_t event)의 형태를 취한다.
      • *struct rpc_clnt, glusterd_conn_t, rpc_clnt_event_t* 간의 관계에 대한 추가 분석 필요
    • snprintf(conn->sockpath, sizeof(conn->sockpath), "%s", sockpath)를 호출하여 RPC 클라이언트 자료 구조의 소켓 파일 경로를 기록
    • conn->frame_timeout = frame_timeout을 통해 RPC 클라이언트 자료 구조의 프레임 만료 시간을 기록
    • conn->rpc = rpc를 통해 RPC 클라이언트 자료 구조를 기록
    • conn->notify = notify를 통해 RPC 클라이언트의 이벤트 콜백 정보를 기록
  • Initialize the process mgmt
  • glusterd_svc_build_snapd_pidfile(volinfo, pidfile, sizeof(pidfile))
    • glusterd_svc_build_snapd_rundir(...)를 호출하여 런타임 디렉터리 경로명을 얻음
    • "<rundir>/<volinfo->volname>-snapd.pid" 형식의 snapd 프로세스의 PID 파일 경로명을 pidfile에 기록
  • glusterd_svc_build_snapd_volfile(volinfo, volfile, sizeof(volfile))
    • "<workdir>/<volinfo->volname>-snapd.vol" 형식의 volfile 경로명을 volfile에 기록
  • glusterd_svc_build_snapd_logdir(logdir, volinfo->volname, sizeof(logdir))
    • "<DEFAULT_LOG_FILE_DIRECTORY>/snaps/<volname>" 형식의 로그 디렉터리 경로명을 logdir에 기록
  • mkdir_p(logdir, 0755, _gf_true)
    • logdir 경로명에 해당하는 디렉터리를 생성
  • glusterd_svc_build_snapd_logfile(logfile, logdir, sizeof(logfile)
    • "<logdir>/snapd.log" 형식의 로그 파일 경로명을 logfile에 기록
  • snprintf(volfileid, sizeof(volfileid), "snapd/%s", volinfo->volname)
    • "snapd/<volinfo->volname> 형식의 volfile 식별자를 volfileid에 기록
  • "transport.socket.bind-address"의 값을 volfileserver에 기록
    • 해당 키에 대한 값이 없다면 "localhost"를 기록
  • glusterd_proc_init(&(svc->proc), snapd_svc_name, pidfile, logdir, logfile, volfile, volfileid, volfileserver)
    • 앞서 초기화한 매개 변수들을 입력으로 하여 서비스 자료 구조가 포함하는 프로세스 정보(glusterd_proc_t)를 초기화
      • svc->proc : 초기화되는 대상으로서, 프로세스 정보를 기록
      • snapd_svc_name : 서비스 이름
      • pidfile : 서비스의 PID 파일 경로명
      • logdir : 서비스의 로그 디렉터리 경로명
      • logfile : 서비스의 로그 파일 경로명
      • volfile : 서비스가 사용하는 volfile 경로명
      • volfileid : 서비스가 사용하는 volfile의 식별자 (snapd의 경우 snapd/<volname>)
      • volfileserver : volfile 서버의 주소
int
glusterd_snapdsvc_rpc_notify(
    glusterd_conn_t     *conn,  # glusterd와의 연결 정보
    rpc_clnt_event_t    event   # RPC 클라이언트에서 발생한 이벤트 정보
)
    • RPC 통신에서 발생하는 클라이언트(snapd 자신) 이벤트에 대한 콜백이며, snapd의 경우 다음과 같은 항목들을 처리
      • RPC_CLNT_CONNECT - RPC 클라이언트가 서버(glusterd)와 연결 되었을 때 발생
        • 로그 메시지를 남기고, gf_event(EVENT_SVC_CONNECTED, ...)를 통해 이벤트를 발생시킨 후에 서비스의 온라인 상태(svc->online)를 true로 설정
      • RPC_CLNT_DISCONNECT - RPC 클라이언트가 서버(glusterd)와의 연결이 해제되었을 때 발생
        • 서비스가 온라인 상태(svc->online)라면 연결 해제 로그를 남기고, gf_event(EVENT_SVC_DISCONNECTED, ...)를 통해 이벤트를 발생시킨 후에 서비스의 온라인 상태를 false로 설정
      • RPC_CLNT_DESTROY - RPC 클라이언트 객체가 소멸될 때 발생
        • glusterd_volinfo_unref(volinfo)를 통해 volfile 정보에 대한 참조 계수를 1만큼 감소시키고 반환
        • 이때, glusterd_volinfo_unref()는 해당 변수의 참조 계수가 0이라면 glusterd_volinfo_delete(volinfo를 통해 메모리 상에서 객체를 완전히 제거


서비스 시작

int32_t
glusterd_snapdsvc_start(
    glusterd_svc_t  *svc,   # glusterd가  서비스 별로 유지하는 서비스 컨텍스트
    int             flags   # 서비스 구동과 관련된 플래그이며,
                            # 주로 서비스 프로세스 시작 방식을 지정함
)
  • glusterd_proc_is_running(&svc->proc)을 통해 기존에 실행 중인 snapd 서비스 프로세스의 유무 확인하여 있으면 바로 반환
  • snapd = cds_list_entry(svc, glusterd_snapdsvc_t, svc)를 통해 volinfo->snapd 서비스 객체를 얻음
  • volinfo = cds_list_entry(snapd, glusterd_volinfo_t, snapd)를 통해 volinfo를 얻음
  • sys_access(svc->proc.volfile, F_OK)로 volfile의 존재 유무를 판별하여, 없으면 snapd 서비스의 volfile 생성
  • runinit(&runner)로 실행자 자료 구조 초기화
  • valgrind 실행 옵션이 있다면 runner에 해당 실행 옵션을 추가
  • snprintf(snapd_id, sizeof(snapd_id), "snapd-%s", volinfo->volname) volinfo의 볼륨 이름을 포함하는 snapd 식별자 생성
  • runner_add_args(...)를 통해 snapd 서비스 프로세스 실행에 필요한 기본 옵션들을 추가
    • "-s svc->proc.volfileserver" - volfile 정보가 있는 서버 (glusterd)
    • "--volfile-id svc->proc.volfileid - volfile의 식별자
    • "-p svc->proc.pidfile - 서비스 프로세스의 PID 파일이 기록될 파일의 경로
    • "-l svc->proc.logfile - 서비스 프로세스의 로그가 기록될 파일의 경로
    • "--brick-name snapd_id - 서비스 프로세스가 대상으로 하는 브릭명(snapd 서비스는 snapd 식별자)
    • "-S svc->conn.sockpath - 서비스 프로세스가 RPC 통신에 사용할 소켓 파일 경로
  • snapd_port = pmap_assign_port(THIS, volinfo->snapd.port, snapd_id)
    • 포트맵에서 할당 가능한 RPC 포트 번호를 찾아 snapd_port에 기록한다.(volinfo->snapd.port는 이전에 사용 중이던 포트가 있는 경우를 위해 전달)
  • volinfo->snapd.port = snapd_port를 통해 새로 가져온 포트 번호를 volinfo에 기록
  • runner_add_arg(&runner, "--brick-port")를 통해 브릭 포트 번호를 지정하는 --brick-port 옵션을 추가
  • runner_argprintf(&runner, "%d", snapd_port)를 통해 앞선 --brick-port 옵션의 값을 snapd_port로 지정
  • runner_add_arg(&runner, "--xlator-option")를 통해 xlator 옵션을 지정하는 --xlator-option 옵션을 추가
  • runner_argprintf(&runner, "%s-server.liten-port=%d", volinfo->volname, snapd_port)
    • server xlator의 옵션인 listen-port를 snapd_port로 지정
  • runner_add_arg(&runner, "--no-mem-accounting")을 통해 메모리 회계 기능을 비활성화
  • 호출 시 매개 변수로 전달된 flagsPROC_START_NO_WAIT으로 지정되었다면 runner_no_wait(&runner)를 호출하여서, 이외에는 runner_run(&runner)을 호출하여서 서비스 프로세스를 실행한다.
    • runner_run()runner_run_generic()의 래퍼
    • runner_run_nowait()runner_run_generic()과 달리 실행된 자식 프로세스의 종료 상태 등을 고려하지 않고 바로 반환한다.


서비스 재시작

glusterd_snapdsvc_restart()
  • 현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻음
  • 볼륨 별로 순회하기 위해 설정 정보에 있는 볼륨들의 CDS 리스트(conf->volumes)를 순회
  • 매 순회마다 볼륨 상태(volinfo->status)가 GLUSTERD_STATUS_STARTED인지 확인하여 snapd 서비스 시작(manager로 등록된 glusterd_snapdsvc_manager(...)에서 기존 프로세스 정리 후 시작을 수행)


서비스 종료

glusterd_svc_stop()는 일반적인 절차이므로 생략


RPC 핸들러

gd_svc_cli_actorsGLUSTER_CLI_SNAP이 단일 명령으로 등록되어 있으며, glusterd_handle_snapshot에 사상된다.

또한, 이 glusterd_handle_snapshot(rpcsvc_request_t *req) 핸들러는 glusterd_big_locked_handler(rpcsvc_request_t *req, rpcsvc_actor actor_fn)의 호출을 통해 전역적 잠금인 big_lock으로 보호되는 임계 영역 내에서 glusterd_handle_snapshot_fn()을 호출한다.

int
glusterd_handle_snapshot_fn(
    rpcsvc_request_t *req   # RPC를 통해 수신한 서비스 요청 정보
)
  • xdr_to_generic(req->msg[0], &cli_req, (xdrproc_t)xdr_gf_cli_req)를 호출하여 RPC 요청에 포함된 XDR 포맷으로 인코딩된 매개 변수를 디코딩하여 cli_req(gf_cli_req)에 기록한다.
    • 함수 포인터로서 매개 변수로 넘겨진 xdr_gf_cli_req(XDR *xdrs, gf_cli_req *objp) 함수에서 xdr_bytes(...) 함수를 통해 디코딩된다.
  • dict_unserialize(cli_req.dict.dict_val, cli_req.dict.dict_len, &dict)를 호출하여 바이트 스트림으로 디코딩된 XDR 매개 변수를 dict에 기록한다.
  • dict->extra_stdfree = cli_req.dict.dict_val
    • 잔여 메모리로서 추후 할당 해제가 될 필요가 있는 값을 기록하는 것으로 보여지나, 정확히 확인되지 않았으며 추후 확인 필요.
  • host_uuid = gf_strdup(uuid_utoa(MY_UUID))
    • MY_UUID(현재 프로세스인 glusterd의 UUID)를 host_uuid에 기록한다.
  • dict_set_dynstr(dict, "host-uuid", host_uuid)를 통해 RPC 요청 매개 변수의 사전에 "host-uuid" 키에 대한 값을 host_uuid로 기록
  • glusterd의 OP 버전(conf->op_version)이 GD_OP_VERISON_3_6_0미만인 경우, 스냅샷 기능을 지원하지 않기 때문에 op_errno = EG_OPNOTSUP; ret = -1;과 함께 out으로 점프
  • 요청 매개 변수의 사전에서 "type" 키에 대한 값을 type에 기록
  • "/sbin/lvcreate" 문자열에 대한 매크로인 LVM_CREATE를 대상으로 sys_stat()을 호출하여 LVM 명령을 사용할 수 있는지 확인 후, 사용할 수 없으면 이에 대한 오류 메시지를 err_str에 기록한 후 ret = -1;과 함께 out으로 점프
  • 요청 매개 변수에서 가져온 type값을 기준으로 분기 처리

GF_SNAP_OPTION_TYPE_CREATE

int
glusterd_handle_snapshot_create(
    rpcsvc_request_t    *req,       # RPC를 통해 수신한 서비스 요청 정보
    glusterd_op_t       op,         # glusterd 명령 코드
    dict_t              *dict,      # 명령 요청 정보를 포함하는 사전
    char                *err_str,   # (오류가 발생할 경우) 오류 문자열
    size_t              len         # err_str 버퍼의 길이
)
  • ret = dict_get_int64(dict, "volcount", &volcount)를 통해 매개 변수로 넘겨 받은 사전의 "volcount" 키가 기록하는 볼륨 개수를 volcount 변수에 기록한다.
  • ret = dict_get_str(dict, "snapname", &snapname)를 통해 "snapname" 키가 기록하는 스냅샷 이름을 snapname 변수에 기록한다.
  • timestamp = dict_get_str_boolean(dict, "no-timestamp", _gf_false)를 통해 "no-timestamp" 키가 기록하는 타임스탬프 사용 여부를 timestamp 변수에 기록한다. (기본값은 _gf_false)
  • ret = dict_set_int64(dict, "snap-time", (int64_t)time(&snap_time))를 통해 snap_time 변수에 현재 유닉스 시간을 기록하고, 이 값을 "snap-time" 키에 기록한다.
  • 타임스탬프 사용 여부를 나타내는 timestampFALSE(_gf_false)일 경우, snap_time을 기준으로 하는 UTC 시간을 "<snapname>_GMT-%Y.%m.%d-%H.%M.%S" 형식의 문자열로 포맷팅하여 새로운 스냅샷의 이름을 나타내는 변수인 snapname에 기록한다.
  • snapname의 문자열 길이가 GLUSTERD_MAX_SNAP_NAME(= 255) 이상이라면 오류를 발생시킨다.
  • uuid_ptr = GF_CALLOC(1, sizeof(uuid_t), gf_common_mt_uuid_t)를 통해 스냅샷의 UUID를 기록하기 위한 메모리를 할당한다.
  • gf_uuid_generate(*uuid_ptr)을 통해 uuid_ptr 변수에 새로운 UUID를 기록한다.
  • ret = dict_set_bin(dict, "snap-id", uuid_ptr, sizeof(uuid_t))를 통해 uuid_ptr이 기록하고 있는 UUID를 "snap-id" 키에 기록한다.
  • uuid_ptr = NULL을 통해 uuid_ptr 변수를 NULL로 초기화한다.
  • 변수 i가 1부터 시작하여 볼륨 개수를 기록하고 있는 volcount이 될 때까지 1만큼 증가시키며 반복을 수행한다.
    • key 변수에 "volname%d" (%d = i) 형식의 문자열을 기록한다.
    • ret = dict_get_str(dict, key, &volname)를 통해 key 변수에 저장된 문자열의 키가 기록하는 값을 volname 변수에 기록한다.
    • gf_uuid_generate(tmp_uuid)를 통해 새로운 UUID를 생성하여 tmp_uuid에 기록한다.
    • username = gf_strdup(uuid_utoa(tmp_uuid))를 통해 바이너리 형태의 UUID를 문자열로 변환하여 username 변수에 기록한다.
    • key 변수에 "volume%d_username" (%d = i) 형식의 문자열을 기록한다.
    • ret = dict_set_dynstr(dict, key, username)를 통해 key 변수에 저장된 문자열의 키(volume%d_username)에 동적 할당된 문자열인 username 변수를 기록한다.
    • gf_uuid_generate(tmp_uuid)를 통해 새로운 UUID를 생성하여 tmp_uuid에 기록한다.
    • password = gf_strdup(uuid_utoa(tmp_uuid))를 통해 바이너리 형태의 UUID를 문자열로 변환하여 password 변수에 기록한다.
    • key 변수에 "volume%d_password" (%d = i) 형식의 문자열을 기록한다.
    • ret = dict_set_dynstr(dict, key, password)를 통해 key 변수에 저장된 문자열의 키(volume%d_password)에 동적 할당된 문자열인 password 변수를 기록한다.
    • 스냅샷 볼륨의 UUID를 기록하기 위해 uuid_ptr = GF_CALLOC(1, sizeof(uuid_t), gf_common_mt_uuid_t)을 통해 uuid_ptr을 초기화한다.
    • "vol%d_volid" (%d = i) 형식의 스냅샷 볼륨의 UUID를 key에 기록한다.
    • gf_uuid_generate(*uuid_ptr)을 통해 uuid_ptr을 초기화한다.
    • dict_set_bin(dict, key, uuid_ptr, sizeof(uuid_t))을 통해 key 변수에 저장된 문자열의 키(vol%d_volid)에 uuid_ptr 변수가 가리키는 UUID를 기록한다.
    • 하이픈(-)이 없는 형식으로 UUID를 얻기 위해 GLUSTERD_GET_UUID_NOHYPHEN(snap_volname, *uuid_ptr)을 호출하며, snap_volname 변수에 결과가 기록된다.
    • "snap-volume%d" (%d = i) 형식의 문자열을 key 변수에 기록한다.
    • ret = dict_set_dynstr_with_alloc(dict, key, snap_volname)을 수행하여 key 변수에 저장된 문자열의 키(snap-volume%d)에 동적 할당된 문자열인 snap_volname 변수를 문자열 복사한 후에 기록한다.
  • glusterd_mgmt_v3_initiate_snap_phases(req, op, dict)를 호출하여 v3 프로토콜을 시작한다.
    • 후술되는 glusterd_mgmt_v3_initiate_snap_phases()의 내용을 참조

GF_SNAP_OPTION_TYPE_CLONE

glusterd_handle_snapshot_clone(
    rpcsvc_request_t    *req,       # RPC를 통해 수신한 서비스 요청 정보
    glusterd_op_t       op,         # glusterd 명령 코드
    dict_t              *dict,      # 명령 요청 정보를 포함하는 사전
    char                *err_str,   # (오류가 발생할 경우) 오류 문자열
    size_t              len         # err_str 버퍼의 길이
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • ret = dict_get_str(dict, "clonename", &clonename)
    복제 대상 이름을 사전으로부터 얻기 위해서 "clonename"을 키로 호출하며, 그 키에 대한 값은 clonename 변수에 저장된다.
  • volname = gf_strdup(clonename)
    clonename 변수에 저장된 복제 대상 이름을 volname에 복사한다.
  • snprintf(key, sizeof(key), "volname1")
    "volname1" 문자열을 key 변수에 저장한다.
  • ret = dict_set_dynstr(dict, key, volname)
    key 변수에 저장된 "volname1"을 키로 하여 동적 할당된 문자열 변수인 volname을 사전에 저장한다.
  • ret = dict_get_str(dict, "snapname", &snapname)
    "snapname" 키에 대한 값을 snapname 변수에 저장한다.
  • uuid_ptr = GF_CALLOC(1, sizeof(uuid_t), gf_common_mt_uuid_t)
    UUID 저장을 위한 포인터 변수인 uuid_ptr가 새로 할당된 메모리를 가리키도록 한다.
  • gf_uuid_generate(*uuid_ptr)
    uuid_ptr이 가리키는 메모리에 새로운 UUID를 만들어서 저장한다.
  • ret = dict_set_bin(dict, "clone-id", uuid_ptr, sizeof(uuid_t))
    "clone-id"라는 키의 값을 uuid_ptr이 가리키는 메모리 영역으로 사전에 저장한다.
  • uuid_ptr = NULL
    새로운 UUID를 저장하고 있는 메모리를 가리키는 uuid_ptr이 아무 것도 가리키지 않도록 한다.
    이는 dict_set_bin(...)을 통해 해당 메모리가 사전에 포함되었기 때문이다.
  • ret = dict_get_str(dict, "snapname", &snapname)
    "snapname" 키에 대한 값을 snapname 변수에 저장한다.
    앞서 가져온 코드와 중복으로 보여지는데 왜?
  • gf_uuid_generate(tmp_uuid)
    tmp_uuid 변수에 새로운 UUID를 만들어 저장한다.
  • username = gf_strdup(uuid_utoa(tmp_uuid))
    새로운 UUID를 uuid_utoa(...) 함수를 통해 문자열로 변환하여 username 변수에 저장한다.
  • snprintf(key, sizeof(key), "volume1_username")
    key 변수에 "volume1_username" 문자열을 저장한다.
  • ret = dict_set_dynstr(dict, key, username)
    key 변수에 저장된 키인 "volume1_username"을 키로 하여 그 값을 username 변수의 값으로 사전에 저장한다.
  • gf_uuid_generate(tmp_uuid)
    tmp_uuid 변수에 새로운 UUID를 만들어 저장한다.
  • password = gf_strdup(uuid_utoa(tmp_uuid))
    새로운 UUID를 uuid_utoa(...) 함수를 통해 문자열로 변환하여 password 변수에 저장한다.
  • snprintf(key, sizeof(key), "volume1_password")
    key 변수에 "volume1_password" 문자열을 저장한다.
  • ret = dict_set_dynstr(dict, key, password)
    key 변수에 저장된 키인 "volume1_password"을 키로 하여 그 값을 password 변수의 값으로 사전에 저장한다.
  • uuid_ptr = GF_CALLOC(1, sizeof(uuid_t), gf_common_mt_uuid_t)
    UUID 저장을 위한 포인터 변수인 uuid_ptr가 새로 할당된 메모리를 가리키도록 한다.
  • snprintf(key, sizeof(key) - 1, "vol1_volid")
    key 변수에 "vol1_volid" 문자열을 저장한다.
  • gf_uuid_generate(*uuid_ptr)
    uuid_ptr이 가리키는 메모리에 새로운 UUID를 만들어서 저장한다.
  • ret = dict_set_bin(dict, key, uuid_ptr, sizeof(uuid_t))
    key 변수가 저장하고 있는 "vol1_volid"라는 키의 값을 uuid_ptr이 가리키는 메모리 영역으로 사전에 저장한다.
  • snprintf(key, sizeof(key), "clone-volname%d", i)
    key 변수에 "clone-volname%d" (%d = 0) 형식의 문자열을 저장한다.
    여기에서 i는 항상 0이며, 향후 다른 방식으로 개선을 위해 사용되고 있는 것으로 보여진다.
  • ret = dict_set_dynstr_with_alloc(dict, key, snap_volname)
    key 변수가 저장하고 있는 "clone-volname0"라는 키의 값을 snap_volname 변수에 저장된 값으로 사전에 저장한다.
    여기에서 snap_volname 변수는 초기화된 채로 사용되지 않고 있으며, 향후 다른 방식으로 개선을 위해 사용되고 있는 것으로 보여진다.
  • ret = glusterd_mgmt_v3_initiate_snap_phases(req, op, dict)
    v3 프로토콜을 시작한다.
    후술되는 glusterd_mgmt_v3_initiate_snap_phases()의 내용을 참조

GF_SNAP_OPTION_TYPE_RESTORE

glusterd_handle_snapshot_restore(
    rpcsvc_request_t    *req,       # RPC를 통해 수신한 서비스 요청 정보
    glusterd_op_t       op,         # glusterd 명령 코드
    dict_t              *dict,      # 명령 요청 정보를 포함하는 사전
    char                *err_str,   # (오류가 발생할 경우) 오류 문자열
    uint32_t            *op_errno,  # 명령 결과 코드
    size_t              len         # err_str 버퍼의 길이
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • ret = dict_get_str(dict, "snapname", &snapname)
    사전의 "snapname" 키에 해당하는 값을 snapname 변수에 저장한다.
  • snap = glusterd_find_snap_by_name(snapname)
    snapname 변수에 저장된 이름으로 스냅샷 자료 구조(glusterd_snap_t)를 찾아 snap 변수에 저장한다.
  • list_for_each_entry(snap_volinfo, &snap->volumes, vol_list) {
    검색된 스냅샷의 볼륨 목록(snap->volumes)의 각각을 snap_volinfo 변수(glusterd_volinfo_t 자료형)에 저장하면서 순회한다.
    • i++
      초기값이 0인 변수 i를 1만큼 증가시킨다.
    • snprintf(key, sizeof(key), "volname%d", i)
      key 변수에 "volname%d" (%d = i) 형식의 문자열을 저장한다.
    • buf = gf_strdup(snap_volinfo->parent_volname)
      buf 변수에 스냅샷 볼륨의 부모 볼륨 이름(snap_volinfo->parent_volname)을 복사한다.
    • ret = dict_set_dynstr(dict, key, buf)
      key 변수가 저장하고 있는 "volname%d" (%d = i)라는 키의 값을 부모 볼륨 이름을 저장하고 있는 buf 변수에 저장된 값으로 사전에 저장한다.
    • buf = NULL
      buf 문자열 포인터 변수가 가리키던 메모리가 사전에 저장되었으므로, buf를 초기화한다.
  • ret = dict_set_int32(dict, "volcount", i)
    위 순회 과정을 모두 마치면 i는 해당 스냅샷에 연관된 볼륨들의 개수를 가리키고 있으며, 이 값을 "volcount"라는 키의 값으로 사전에 저장한다.
  • ret = glusterd_mgmt_v3_initiate_snap_phases(req, op, dict)
    v3 프로토콜을 시작한다.
    후술되는 glusterd_mgmt_v3_initiate_snap_phases()의 내용을 참조

GF_SNAP_OPTION_TYPE_INFO

glusterd_handle_snapshot_info(
    rpcsvc_request_t    *req,       # RPC를 통해 수신한 서비스 요청 정보
    glusterd_op_t       op,         # glusterd 명령 코드
    dict_t              *dict,      # 명령 요청 정보를 포함하는 사전
    char                *err_str,   # (오류가 발생할 경우) 오류 문자열
    size_t              len         # err_str 버퍼의 길이
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • ret = dict_get_int32(dict, "sub-cmd", &cmd)
    하위 명령 코드를 나타내는 "sub-cmd"라는 키의 값을 cmd 변수(int32_t 자료형)에 저장한다.
  • switch (cmd) {
    요청 정보 사전에 포함되었던 하위 명령 코드인 "sub-cmd" 키의 값에 따라 분기를 수행한다.
    • case GF_SNAP_INFO_TYPE_ALL:
      glusterd_snapshot_get_all_snap_info(dict)를 호출한다.
    • case GF_SNAP_INFO_TYPE_SNAP:
      "snapname" 키의 값을 snapname 변수에 가져온다.
      "snapcount" 키의 값을 1로 설정한다.
      glusterd_find_snap_by_name(snapname)를 호출한다.
      glusterd_snapshot_get_snap_detail(dict, snap, "snap1", NULL)을 호출한다.
    • case GF_SNAP_INFO_TYPE_VOL:
      glusterd_snapshot_get_info_by_volume(dict, volname, err_str, len)을 호출한다.
      snap_driven 변수의 값을 0으로 저장한다. (초기값은 1)
  • ret = dict_set_int8(dict, "snap-driven", snap_driven)
    "snap_driven" 키의 값을 snap_driven 변수의 값으로 저장한다.
  • ret = glusterd_op_send_cli_response(op, 0, 0, req, dict, err_str)
    요청 수행 결과를 응답한다.

GF_SNAP_OPTION_TYPE_LIST

glusterd_handle_snapshot_list(
    rpcsvc_request_t    *req,       # RPC를 통해 수신한 서비스 요청 정보
    glusterd_op_t       op,         # glusterd 명령 코드
    dict_t              *dict,      # 명령 요청 정보를 포함하는 사전
    char                *err_str,   # (오류가 발생할 경우) 오류 문자열
    size_t              len         # err_str 버퍼의 길이
    uint32_t            *op_errno,  # 명령 결과 코드
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • ret = dict_get_str(dict, "volname", &volname)
    "volname" 키의 값을 volname 변수에 가져온다.
  • ret = glusterd_volinfo_find(volname, &volinfo)
    volname 변수에 저장된 볼륨 이름을 기준으로 볼륨 정보(glusterd_volinfo_t)를 찾아 volinfo 변수에 저장한다.
  • ret = glusterd_snapshot_get_vol_snapnames(dict, volinfo)
    볼륨 정보를 통해 해당 볼륨과 관련된 스냅샷 목록을 dict에 저장한다.
    자세한 내용은 후술할 glusterd_snapshot_get_vol_snapnames() 함수를 참고
  • ret = glusterd_op_send_cli_response(op, 0, 0, req, dict, err_str)
    요청 수행 결과를 응답한다.

GF_SNAP_OPTION_TYPE_CONFIG

glusterd_handle_snapshot_config(
    rpcsvc_request_t    *req,       # RPC를 통해 수신한 서비스 요청 정보
    glusterd_op_t       op,         # glusterd 명령 코드
    dict_t              *dict,      # 명령 요청 정보를 포함하는 사전
    char                *err_str,   # (오류가 발생할 경우) 오류 문자열
    size_t              len         # err_str 버퍼의 길이
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • ret = dict_get_int32(dict, "config-command", &config_command)
    "config-command" 키의 값을 config_command 변수에 가져온다.
  • ret = dict_get_str(dict, "volname", &volname)
    "volname" 키의 값을 volname 변수에 가져온다.
  • switch (config_command) {
    요청 정보 사전에 포함되었던 하위 명령 코드인 "config-command" 키의 값에 따라 분기를 수행한다.
    • case GF_SNAP_CONFIG_TYPE_SET:
      • volname 변수가 NULL이라면 "hold_vol_locks"의 값을 FALSE(_gf_false)로 사전에 저장한다.
      • ret = glusterd_mgmt_v3_initiate_all_phases(req, op, dict)를 호출하여 v3 프로토콜을 시작한다. 후술되는 glusterd_mgmt_v3_initiate_all_phases()의 내용을 참조
    • case GF_SNAP_CONFIG_DISPLAY:
      • snap_max_limits_display_commit(dict, volname, err_str, len)
        스냅샷 제약 정보를 로컬 노드로부터 가져와서 dict 사전에 설정한다.
      • glusterd_op_send_cli_response(op, 0, 0, req, dict, err_str)
        요청 수행 결과를 응답한다.

GF_SNAP_OPTION_TYPE_DELETE

glusterd_handle_snapshot_delete(
    rpcsvc_request_t    *req,       # RPC를 통해 수신한 서비스 요청 정보
    glusterd_op_t       op,         # glusterd 명령 코드
    dict_t              *dict,      # 명령 요청 정보를 포함하는 사전
    char                *err_str,   # (오류가 발생할 경우) 오류 문자열
    uint32_t            *op_errno,  # 명령 결과 코드
    size_t              len         # err_str 버퍼의 길이
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • ret = dict_get_int32(dict, "sub-cmd", &delete_cmd)
    하위 명령 코드를 나타내는 "sub-cmd"라는 키의 값을 delete_cmd 변수(int32_t 자료형)에 저장한다.
  • switch (delete_cmd) {
    요청 정보 사전에 포함되었던 하위 명령 코드인 "delete-command" 키의 값에 따라 분기를 수행한다.
    • case GF_SNAP_DELETE_TYPE_SNAP:
      • glusterd_handle_snapshot_delete_type_snap(req, op, dict, err_str, op_errno, len)
        삭제 대상 스냅샷의 정보를 사전에 기록한다.
        후술되는 glusterd_handle_snapshot_delete_type_snap(...)을 호출하며, 이 과정에서 glusterd_mgmt_v3_initiate_snap_phases(...)의 호출을 통해 스냅샷은 바로 제거된다.
    • case GF_SNAP_DELETE_TYPE_ITER:
      • GF_SNAP_DELETE_TYPE_SNAP과 동일 (fallthrough)
    • case GF_SNAP_DELETE_TYPE_ALL:
      • glusterd_handle_snapshot_delete_all(dict)을 호출하여 모든 스냅샷의 이름을 사전에 기록한다.
    • case GF_SNAP_DELETE_TYPE_VOL:
      • glusterd_handle_snapshot_delete_vol(dict, err_str, op_errno, len)을 호출하여 사전의 volname 키 값에 해당하는 볼륨에 대한 스냅샷의 이름을 사전에 기록한다.
      • 후술되는 glusterd_handle_snapshot_delete_vol(...)을 참조
    • default:
      • 예외 처리
  • if (ret == 0 && (delete_cmd == GF_SNAP_DELETE_TYPE_ALL || delete_cmd == GF_SNAP_DELETE_TYPE_VOL)) {
    ret를 참조하여 각 분기 수행의 결과가 정상이고, 하위 명령 코드가 GF_SNAP_DELETE_TYPE_ALL이거나 GF_SNAP_DELETE_TYPE_VOL인 경우에는 glusterd_op_send_cli_response(op, 0, 0, req, dict, err_str)을 호출하여 요청 수행 결과를 응답한다.
    이 경우, 실제 삭제는 이 함수의 반환을 응답 받은 클라이언트의 확인 후 발생하는 추가적인 v3 프로토콜 요청을 통해 이루어진다.

GF_SNAP_OPTION_TYPE_ACTIVATE

후술되는 glusterd_mgmt_v3_initiate_snap_phases(...)의 내용을 참조

GF_SNAP_OPTION_TYPE_DEACTIVATE

후술되는 glusterd_mgmt_v3_initiate_snap_phases(...)의 내용을 참조

GF_SNAP_OPTION_TYPE_STATUS

glusterd_handle_snapshot_status(
    rpcsvc_request_t    *req,       # RPC를 통해 수신한 서비스 요청 정보
    glusterd_op_t       op,         # glusterd 명령 코드
    dict_t              *dict,      # 명령 요청 정보를 포함하는 사전
    char                *err_str,   # (오류가 발생할 경우) 오류 문자열
    size_t              len         # err_str 버퍼의 길이
)

후술되는 glusterd_mgmt_v3_initiate_snap_phases(...)의 내용을 참조

glusterd_mgmt_v3_initiate_snap_phases()

int32_t
glusterd_mgmt_v3_initiate_snap_phases(
    rpcsvc_request_t    *req,   # RPC를 통해 수신한 서비스 요청 정보
    glusterd_op_t       op,     # glusterd 명령 코드
    dict_t              *dict   # 명령 요청 정보를 포함하는 사전
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • txn_generation = conf->generation
    현재 설정 정보의 피어 목록 세대 번호(conf->generation)을 txn_generation에 저장한다.
  • cmm_smp_rmb()
    URCU 라이브러리의 함수이며, 컴파일러와 CPU가 메모리 읽기를 이동 혹은 재정렬 시키는 코드 최적화를 수행하지 못하게 하는 메모리 장벽을 시작한다.
    다시 말해서, 이 시점 이후로 수행되는 코드의 메모리 읽기에 대한 CPU 인스트럭션은 있는 그대로 수행된다는 말이다.
  • orignator_uuid = GF_CALLOC(1, sizeof(uuid_t), gf_common_mt_uuid_t)
    새로운 UUID 할당을 위한 메모리를 할당하여 originator_uuid 포인터 변수가 이 메모리를 가리키게 한다.
  • gf_uuid_copy(*originator_uuid, MY_UUID)
    MY_UUID 매크로 함수를 호출하여, 이 코드를 수행 중인 glusterd(mgmt)의 UUID를 복사한다.
    이는 추후 is_origin_glusterd()를 호출하여 해당 노드가 명령을 시작한 노드인지 확인하기 위함이다.
  • ret = dict_set_bin(dict, "originator_uuid", originator_uuid, sizeof(uuid_t))
    originator_uuid가 가리키는 메모리를 "originator_uuid" 키의 값으로 사전에 저장한다.
  • ret = dict_set_int32(dict, "is_synctasked", _gf_true)
    사전의 "is_synctasked" 키에 대한 값을 TRUE(_gf_true)로 저장하여, 해당 명령이 완료된 동기 작업이라고 표기한다.
  • tmp_dict = dict_new()
    새로운 사전을 생성하여 tmp_dict 변수에 저장한다.
    이는 로컬 잠금 해제가 되기 전에 CLI 응답이 송신되는 과정에서 사전 안에 있는 volname이 제거되기 때문에 로컬 잠금 해제를 하기 위해 동일한 사전을 사용하면 이미 제거된 사전 정보를 통해 해제를 시도하기 때문이다.
  • dict_copy(dict, tmp_dict)
    새로 할당한 tmp_dict에 기존 dict 사전 변수를 복사한다.
  • ret = glusterd_mgmt_v3_initiate_lockdown(op, dict, &op_errstr, &op_errno, &is_acquired, txn_generation)
    v3 프로토콜의 첫번째 단계로서, 잠금을 확보하는 락다운 단계를 수행한다.
  • ret = glusterd_mgmt_v3_build_payload(&req_dict, &op_errstr, dict, op)
    v3 프로토콜의 두번째 단계로서, 통신에 첨부되는 부가 데이터를 꾸미는 페이로드 생성 단계를 수행한다.
  • ret = glusterd_mgmt_v3_pre_validate(op, req_dict, &op_errstr, &op_errno, txn_generation)
    v3 프로토콜의 세번째 단계로서, 명령 수행이 가능한 상태인지 사전에 확인하는 사전 검증 단계를 수행한다.
  • ret = glusterd_snap_quorum_check(req_dict, _gf_false, &op_errstr, &op_errno)
    v3 프로토콜 단계와는 별개로, 볼륨의 서버 쿼럼 상태가 정상인지 확인한다.
  • ret = dict_set_dynstr_with_alloc(req_dict, "operation-type", "pre")
    페이로드로 사용되는 사전인 req_dict"operation-type" 키의 값을 "pre"로 저장한다.
    이렇게 함으로써 브릭 명령이 사전-커밋(pre-commit)과 사후-커밋(post-commit) 사이에 보내졌는지를 구별해낼 수 있다.
  • ret = glusterd_mgmt_v3_brick_op(op, req_dict, &op_errstr, txn_generation)
    v3 프로토콜의 네번째 단계로서, 브릭 명령을 요청하는 브릭 명령 단계를 수행한다.
    여기에선 유형이 "pre"이기 때문에 커밋 단계 이후에 "post" 유형의 브릭 명령을 다시 요청한다.
  • ret = dict_set_int32(req_dict, "cleanup", 1)
    req_dict 사전에 "cleanup" 키의 값을 1로 저장한다.
  • ret = glusterd_mgmt_v3_commit(op, dict, req_dict, &op_errstr, &op_errno, txn_generation)
    v3 프로토콜의 다섯번째 단계로서, 수행된 명령을 최종적으로 반영하는 커밋 단계를 수행한다.
    현재로서 커밋 FOP를 보내기 전에 쿼럼을 검사해서 쿼럼이 성공하면 다른 모든 glusterd(mgmt)에게 커밋을 보내는 것을 계획하고 있다고 한다.
    스냅 생성 기능은 미완료 상태로 표기된 메모리와 디스크 상에서 객체를 만들고, LVM 스냅샷을 만들고 난 이후에 이 스냅샷 객체들의 상태를 갱신하는데, glusterd(mgmt) 중의 하나가 LVM 스냅샷을 생성한 이후에 중단되었다고 가정하면 스냅샷 객체를 갱신하기 전에 스냅샷 생성을 실패한 것으로 간주하고 청소(cleanup) 과정을 트리거한다. 다시 말하면, 명령을 시작한(originator) glusterd가 수신하는 커밋 응답의 개수는 요청을 보낸 피어의 수와 같아야 하고, 그렇지 않을 경우에는 명령을 시작한 glusterd(originator)는 사후 검증 단계에서 청소(cleanup)를 할 것이다.
  • success = _gf_true
    success 변수에 TRUE(_gf_true)를 저장한다.
  • ret = dict_set_dynstr_with_alloc(req_dict, "operation-type", "post")
    req_dict 사전의 "operation-type" 키의 값을 "post"로 저장한다.
    이렇게 함으로써 브릭 명령이 사전-커밋(pre-commit)과 사후-커밋(post-commit) 사이에 보내졌는지를 구별해낼 수 있다.
  • unbarrier:
    이 레이블은 "pre" 유형의 브릭 명령 전송 과정 이후에 발생하는 예외에 대해 점프하기 위한 레이블이며, 이는 "pre", "post" 쌍으로 이루어지는 배리어를 해제하는 예외 처리를 위한 것이다.
  • ret = glusterd_mgmt_v3_brick_op(op, req_dict, &op_errstr, txn_generation)
    v3 프로토콜의 여섯번째 단계로서, 브릭 명령을 요청하는 브릭 명령 단계를 수행한다.
    앞서 "pre" 유형으로 브릭 명령을 지정했기에 이후 커밋이 수행되었어야 해당 단계에서 정상 처리가 될 것임을 알 수 있다.
  • if (success) {
    커밋 단계가 정상적으로 수행되었다면 분기를 수행한다.
    • ret = glusterd_snap_quorum_check(dict, _gf_true, &op_errstr, &op_errno)
      스냅샷 볼륨의 쿼럼을 검사한다.
  • ret = 0
    요청에 응답할 반환 값을 0으로 설정한다.
  • out:
    이 레이블은 "pre", "post" 쌍으로 이루어지는 배리어 밖에서 발생하는 예외에 대해 점프하기 위한 레이블이다.
  • op_ret = ret
    op_ret 변수에 ret의 값을 저장한다.
  • if (success == _gf_false)
            op_ret = -1
    
    커밋 성공 여부를 나타내는 success 변수가 FALSE(_gf_false)라면 커밋이 실패한 것이므로, 요청의 성공 여부를 나타내는 op_ret의 값을 -1로 저장한다.
  • ret = glusterd_mgmt_v3_post_validate(op, op_ret, dict, req_dict, &op_errstr, txn_generation)
    v3 프로토콜의 일곱번째 단계로서, 사후-검증을 수행한다.
  • (void) glusterd_mgmt_v3_release_peer_locks(op, dict, op_ret, &op_errstr, is_acquired, txn_generation)
    v3 프로토콜의 여덟번째 단계로서, 피어 잠금 해제를 수행한다.
  • if (cli_errstr) {
            GF_FREE(op_errstr);
            op_errstr = NULL;
            op_errstr = cli_errstr;
    }
    
    커밋 명령이 실패한다면 오류 메시지는 cli_errstr에 저장되고 배리어 해제가 수행된다. 하지만 배리어 해제마저도 실패한다면 오류 메시지는 로그로 남고 할당 해제가 된다. 따라서 cli_errstr에 저장된 커밋 명령에서 발생한 오류가 CLI로 보내져야 하기 때문에 이를 op_errstr 변수에 복사한다.
  • if (is_acquired) {
    로컬에서의 볼륨 잠금이 걸려있는지를 나타내는 is_acquired 플래그가 설정되어 있다면 분기를 수행한다.
    • ret = glusterd_multiple_mgmt_v3_unlock(tmp_dict, MY_UUID)
      로컬 glusterd에서 잠근 볼륨, 스냅샷 등의 여러 항목들에 대한 잠금을 일괄적으로 해제한다.
  • if (op_ret && (op_errno == 0))
            op_errno = EG_INTRNL;
    
    명령 반환값이 오류임에도 불구하고 오류 번호가 0이라면 내부 오류로 간주하여 이를 나타내는 EG_INTRNLop_errno에 저장한다.
  • glusterd_op_send_cli_response(op, op_ret, op_errno, req, dict, op_errstr)
    CLI 요청에 대한 응답을 수행한다.
int
glusterd_mgmt_v3_initiate_lockdown(
    glusterd_op_t   op,             #
    dict_t          *dict,          #
    char            **op_errstr,    #
    uint32_t        *op_errno,      #
    gf_boolean_t    *is_acquired,   #
    uint32_t        txn_generation  #
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • ret = glusterd_multiple_mgmt_v3_lock(dict, MY_UUID, op_errno)
    로컬 피어(glusterd)의 항목들에 대해 잠금을 수행한다.
  • *is_acquired = _gf_true
    로컬 잠금이 수행되었음을 나타내는 플래그의 포인터 변수인 is_acquiredTRUE(_gf_true)를 저장한다.
  • gd_syncargs_init(&args, NULL)
    클러스터의 모든 피어에 대한 요청을 동기적으로 요청하기 위해 args 동기 작업 변수(struct syncargs)를 초기화한다.
    후술되는 gd_syncargs_init()을 참고
  • synctask_barrier_init((&args))
    GlusterFS/코드_분석/동기_작업_처리/syncbarrier_init() 함수의 매크로 함수인 synctask_barrier_init()을 호출하여 동기 작업에 대한 배리어를 초기화한다.
    GlusterFS/코드_분석/동기_작업_처리 참조
  • peer_cnt = 0
    정상적으로 요청된 피어의 수를 나타내는 peer_cnt 변수의 값을 0으로 저장한다.
  • rcu_read_lock()
    CDS(Concurrent Data Structures) 리스트로 이루어진 피어 목록을 순회하기에 앞서 RCU 읽기 잠금을 수행한다.
  • cds_list_for_each_entry_rcu(peerinfo, &conf->peers, uuid_list)
    설정 정보의 피어 목록을 peerinfo에 하나씩 저장하면서 순회한다.
    • if (peerinfo->generation > txn_generation)
              continue;
      
      피어 정보의 세대 번호가 트랜잭션 세대 번호보다 크다면 정보가 갱신된 것이므로 건너뛴다.
    • if (!peerinfo->connected)
              continue;
      
      피어 정보의 연결 상태를 나타내는 connected 멤버가 FALSE라면 연결되지 않은 상태이므로 건너뛴다.
    • if (op != GD_OP_SYNC_VOLUME &&
          peerinfo->state.state != GD_FRIEND_STATE_BEFRIENDED)
              continue;
      
      요청된 명령 코드가 GD_OP_SYNC_VOLUME이 아니고, 피어 상태 기계의 현재 상태가 클러스터에 정상 참여 중임을 나타내는 GD_FRIEND_STATE_BEFRIENDED가 아니라면 건너뛴다.
    • gd_mgmt_v3_lock(op, dict, peerinfo, &args, MY_UUID, peer_uuid)
      해당 노드에 대해 v3 잠금 프로토콜을 요청한다.
      자세한 내용은 gd_mgmt_v3_lock()을 참조
    • peer_cnt++
      정상적으로 요청된 노드의 개수를 1만큼 증가시킨다.
  • rcu_read_unlock()
    순회에 앞서 잠궜던 RCU 읽기 잠금을 해제한다.
  • if (0 == peer_cnt) {
            ret = 0;
            goto out;
    }
    
    요청을 수행한 노드가 없다면 반환값(ret)을 0으로 저장하고 out: 레이블로 점프하여 정상 처리한다.
  • gd_synctask_barrier_wait((&args), peer_cnt)
    앞서 수행한 배리어가 해제될 때까지 대기한다.
    자세한 내용은 gd_synctask_barrier_wait() 참조
  • if (args.errstr)
            *op_errstr = gf_strdup(args.errstr);
    
    요청에 대한 오류 메시지인 args.errstr 멤버가 NULL이 아니라면 오류가 발생한 것이므로 op_errstr이 가리키는 메모리에 args.errstr의 오류 메시지를 복사한다.
  • ret = args.op_ret
    요청에 대한 명령 반환값인 args.op_ret 멤버의 값을 ret에 복사한다.
  • *op_errno = args.op_errno
    요청에 대한 오류 코드인 args.op_errno 멤버의 값을 op_errno 포인터가 가리키는 대상에 복사한다.
int
glusterd_mgmt_v3_build_payload(
    dict_t          **req,          #
    char            **op_errstr,    #
    dict_t          *dict,          #
    glusterd_op_t   op              #
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • req_dict = dict_new();
    사전형(dict_t)의 포인터 변수인 req_dict에 새로운 사전을 할당한다.
  • switch (op) {
    매개 변수로 넘겨 받은 명령 코드(glusterd_op_t) 별로 분기를 수행한다.
    • case GD_OP_MAX_OPVERSION:
      fallthrough
    • case GD_OP_SNAP:
      • dict_copy(dict, req_dict)
        새로 할당한 req_dict에 기존 dict 사전 변수를 복사한다.
    • case GD_OP_START_VOLUME:
      fallthrough
    • case GD_OP_ADD_BRICK:
      fallthrough
    • case GD_OP_REPLACE_BRICK:
      fallthrough
    • case GD_OP_RESET_BRICK:
      fallthrough
    • case GD_OP_ADD_TIER_BRICK:
      • ret = dict_get_str(dict, "volname", &volname)
        dict 사전에 있는 "volname" 키의 값을 volname 변수에 저장한다.
      • if (strcasecmp(volname, "all")) {
        volname 변수가 "all"이 아니라면 분기르 수행한다.
        • ret = glusterd_dict_set_volid(dict, volname, op_errstr)
          volname에 해당하는 볼륨 정보를 찾아서 그 UUID를 dict 사전의 "vol-id"에 복사한다.
          자세한 내용은 glusterd_dict_set_volid() 참조
        • dict_copy(dict, req_dict)
        req_dict에 기존 dict 사전 변수를 복사한다.
    • case GD_OP_TIER_START_STOP:
      fallthrough
    • case GD_OP_REMOVE_TIER_BRICK:
      fallthrough
    • case GD_OP_DETACH_TIER_STATUS:
      fallthrough
    • case GD_OP_TIER_STATUS:
      • dict_copy(dict, req_dict)
        req_dict에 기존 dict 사전 변수를 복사한다.
  • *req = req_dict
    요청 매개 변수로 들어온 req가 가리키는 사전의 포인터 변수가 새로 할당한 req_dict를 가리키도록 한다.
  • ret = 0
    함수의 반환 값을 0으로 저장한다.


int
glusterd_mgmt_v3_pre_validate(
    glusterd_op_t   op,             #
    dict_t          *req_dict,      #
    char            **op_errstr,    #
    uint32_t        *op_errno,      #
    uint32_t        txn_generation  #
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • rsp_dict = dict_new()
    응답으로 보낼 사전 변수인 rsp_dict를 할당한다.
  • ret = gd_mgmt_v3_pre_validate_fn(op, req_dict, op_errstr, rsp_dict, op_errno)
    로컬 노드에서의 사전-검증을 수행한다.
    자세한 내용은 gd_mgmt_v3_pre_validate_fn() 참조
  • if (op != GD_OP_MAX_OPVERSION) {
    요청 받은 명령이 해당 glusterd(mgmt)가 지원하는 가장 높은 명령 버전을 조회하는 GD_OP_MAX_OPVERSION이 아니라면 분기를 수행한다.
    • ret = glusterd_pre_validate_aggr_rsp_dict(op, req_dict, rsp_dict)
      각 glusterd(mgmt)의 응답 사전인 rsp_dict를 요청 사전인 req_dict에 취합한다.
    • dict_unref(rsp_dict)
      취합되었으므로 필요 없어진 rsp_dict의 참조 계수를 줄인다.
    • rsp_dict = NULL
      rsp_dictNULL로 저장한다.
  • gd_syncargs_init(&args, NULL)
    클러스터의 모든 피어에 대한 요청을 동기적으로 요청하기 위해 args 동기 작업 변수(struct syncargs)를 초기화한다.
    자세한 내용은 gd_syncargs_init()을 참조
  • synctask_barrier_init((&args))
    GlusterFS/코드_분석/동기_작업_처리/syncbarrier_init() 함수의 매크로 함수인 synctask_barrier_init()을 호출하여 동기 작업에 대한 배리어를 초기화한다.
    GlusterFS/코드_분석/동기_작업_처리 참조
  • peer_cnt = 0
    정상적으로 요청된 피어의 수를 나타내는 peer_cnt 변수의 값을 0으로 저장한다.
  • rcu_read_lock()
    CDS(Concurrent Data Structures) 리스트로 이루어진 피어 목록을 순회하기에 앞서 RCU 읽기 잠금을 수행한다.
  • cds_list_for_each_entry_rcu(peerinfo, &conf->peers, uuid_list)
    설정 정보의 피어 목록을 peerinfo에 하나씩 저장하면서 순회한다.
    • if (peerinfo->generation > txn_generation)
              continue;
      
      피어 정보의 세대 번호가 트랜잭션 세대 번호보다 크다면 정보가 갱신된 것이므로 건너뛴다.
    • if (!peerinfo->connected)
              continue;
      
      피어 정보의 연결 상태를 나타내는 connected 멤버가 FALSE라면 연결되지 않은 상태이므로 건너뛴다.
    • if (op != GD_OP_SYNC_VOLUME &&
          peerinfo->state.state != GD_FRIEND_STATE_BEFRIENDED)
              continue;
      
      요청된 명령 코드가 GD_OP_SYNC_VOLUME이 아니고, 피어 상태 기계의 현재 상태가 클러스터에 정상 참여 중임을 나타내는 GD_FRIEND_STATE_BEFRIENDED가 아니라면 건너뛴다.
    • gd_mgmt_v3_pre_validate_req(op, req_dict, peerinfo, &args, MY_UUID, peer_uuid)
      해당 노드에 대해 v3 사전 검증 프로토콜을 요청한다.
      자세한 내용은 gd_mgmt_v3_pre_validate_req() 참조
    • peer_cnt++
      정상적으로 요청된 노드의 개수를 1만큼 증가시킨다.
  • rcu_read_unlock()
    순회에 앞서 잠궜던 RCU 읽기 잠금을 해제한다.
  • if (0 == peer_cnt) {
            ret = 0;
            goto out;
    }
    
    요청을 정상적으로 수행한 노드가 없다면 반환값(ret)을 0으로 저장하고 out: 레이블로 점프하여 정상 처리한다.
  • gd_synctask_barrier_wait((&args), peer_cnt)
    앞서 수행한 배리어가 해제될 때까지 대기한다.
    자세한 내용은 gd_synctask_barrier_wait() 참조
  • if (args.op_ret) {
            ...;
    
            if (args.errstr)
                *op_errstr = gf_strdup(args.errstr);
    }
    
    사전 검증 요청에 대한 오류 메시지인 args.errstr 멤버가 NULL이 아니라면 오류가 발생한 것이므로 op_errstr이 가리키는 메모리에 args.errstr의 오류 메시지를 복사한다.
  • ret = args.op_ret
    사전 검증 요청에 대한 명령 반환값인 args.op_ret 멤버의 값을 ret에 복사한다.
  • *op_errno = args.op_errno
    사전 검증 요청에 대한 오류 코드인 args.op_errno 멤버의 값을 op_errno 포인터가 가리키는 대상에 복사한다.
int32_t
glusterd_snap_quorum_check(
    dict_t          *dict,          #
    gf_boolean_t    snap_volume,    #
    char            **op_errstr,    #
    uint32_t        *op_errno       #
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • ret = dict_get_int32(dict, "type", &snap_command)
    매개 변수로 넘겨 받은 요청 사전인 dict"type" 키의 값을 snap_command 변수에 가져온다.
  • switch (snap_command) {
    snap_command 변수의 값에 따라 분기를 수행한다.
    • case GF_SNAP_OPTION_TYPE_CREATE:
      스냅샷 하위 명령이 생성인 경우
    • case GF_SNAP_OPTION_TYPE_CLONE:
      스냅샷 하위 명령이 복제인 경우
    • case GF_SNAP_OPTION_TYPE_DELETE:
      스냅샷 하위 명령이 삭제인 경우
      • fallthrough
    • case GF_SNAP_OPTION_TYPE_RESTORE:
      스냅샷 하위 명령이 복원인 경우
      • if (!does_gd_meet_server_quorum(this)) {
                ret = -1;
                snprintf (err_str, sizeof(err_str),
                          "glusterds are not in quorum");
                gf_msg (this->name, GF_LOG_WARNING, 0,
                        GD_MSG_SERVER_QUORUM_NOT_MET, "%s",
                        err_str);
                *op_errstr = gf_strdup (err_str);
                *op_errno = EG_NODEDWN;
                goto out;
        }
        
        gf_msg_debug (this->name, 0, "glusterds are in "
                "quorum");
        
        자세한 내용은 does_gd_meet_server_quorum() 참조
int
glusterd_mgmt_v3_brick_op(
    glusterd_op_t   op,             #
    dict_t          *req_dict,      #
    char            **op_errstr,    #
    uint32_t        txn_generation  #
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • rsp_dict = dict_new()
    응답으로 보낼 사전 변수인 rsp_dict를 할당한다.
  • ret = gd_mgmt_v3_brick_op_fn(op, req_dict, op_errstr, rsp_dict)
    로컬 노드에서의 브릭 명령을 수행한다.
    자세한 내용은 gd_mgmt_v3_brick_op_fn() 참조
  • dist_unref(rsp_dict)
    필요 없어진 rsp_dict를 참조 계수를 줄인다.
  • rsp_dict = NULL
    rsp_dictNULL로 저장한다.
  • gd_syncargs_init(&args, NULL)
    클러스터의 모든 피어에 대한 요청을 동기적으로 요청하기 위해 args 동기 작업 변수(struct syncargs)를 초기화한다.
    자세한 내용은 gd_syncargs_init()을 참조
  • synctask_barrier_init((&args))
    GlusterFS/코드_분석/동기_작업_처리/syncbarrier_init() 함수의 매크로 함수인 synctask_barrier_init()을 호출하여 동기 작업에 대한 배리어를 초기화한다.
    GlusterFS/코드_분석/동기_작업_처리 참조
  • peer_cnt = 0
    정상적으로 요청된 피어의 수를 나타내는 peer_cnt 변수의 값을 0으로 저장한다.
  • rcu_read_lock()
    CDS(Concurrent Data Structures) 리스트로 이루어진 피어 목록을 순회하기에 앞서 RCU 읽기 잠금을 수행한다.
  • cds_list_for_each_entry_rcu(peerinfo, &conf->peers, uuid_list)
    설정 정보의 피어 목록을 peerinfo에 하나씩 저장하면서 순회한다.
    • if (peerinfo->generation > txn_generation)
              continue;
      
      피어 정보의 세대 번호가 트랜잭션 세대 번호보다 크다면 정보가 갱신된 것이므로 건너뛴다.
    • if (!peerinfo->connected)
              continue;
      


int
glusterd_mgmt_v3_commit(
        glusterd_op_t   op,
        dict_t         *op_ctx,
        dict_t         *req_dict,
        char          **op_errstr,
        uint32_t       *op_errno,
        uint32_t        txn_generation
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • rsp_dict = dict_new()
    응답으로 보낼 사전 변수인 rsp_dict를 할당한다.