diff --git a/src/machi.proto b/src/machi.proto index 831e32c..db0fbb1 100644 --- a/src/machi.proto +++ b/src/machi.proto @@ -31,7 +31,7 @@ option java_outer_classname = "MachiPB"; ////////////////////////////////////////// // -// enums +// Enums // ////////////////////////////////////////// @@ -52,9 +52,14 @@ enum Mpb_CSumType { CSUM_TAG_SERVER_REGEN_SHA = 3; } +enum Mpb_ProjType { + PRIVATE = 20; + PUBLIC = 21; +} + ////////////////////////////////////////// // -// basic data types +// Basic data types // ////////////////////////////////////////// @@ -72,7 +77,7 @@ message Mpb_ChunkCSum { } // epoch_id() type -message Mpb_EpochId { +message Mpb_EpochID { required uint32 epoch_num = 1; required bytes epoch_csum = 2; } @@ -94,7 +99,7 @@ message Mpb_P_Srvr { ////////////////////////////////////////// // -// requests & responses +// Requests & responses // ////////////////////////////////////////// @@ -108,7 +113,7 @@ message Mpb_ErrorResp { ////////////////////////////////////////// // -// High Level API requests: +// High level API requests: // // echo() : Mpb_EchoReq and Mpb_EchoResp // auth() : Mpb_AuthReq and Mpb_AuthResp @@ -214,7 +219,7 @@ message Mpb_ListFilesResp { ////////////////////////////////////////// // -// request & response wrapper +// High level API request & response wrapper // ////////////////////////////////////////// @@ -248,7 +253,7 @@ message Mpb_Response { // Generic error response, typically used when something quite // bad/unexpected happened within the server. // Clients should always check this response and, if defined, - // ignroe any request-specific response at codes 10+. + // ignore any request-specific response at codes 10+. optional Mpb_ErrorResp generic = 2; // Specific responses. @@ -260,3 +265,164 @@ message Mpb_Response { optional Mpb_ChecksumListResp checksum_list = 15; optional Mpb_ListFilesResp list_files = 16; } + +////////////////////////////////////////// +// +// Low level API data types +// +////////////////////////////////////////// + +enum Mpb_Mode { + AP_MODE = 30; + CP_MODE = 31; +} + +message Mpb_Now { + required uint64 sec = 1; + required uint32 usec = 2; +} + +message Mpb_MembersDictEntry { + required string key = 1; + required Mpb_P_Srvr val = 2; +} + +message Mpb_ProjectionV1 { + required uint32 epoch_number = 1; + required bytes epoch_csum = 2; + required string author_server = 3; + repeated string all_members = 4; + required Mpb_Now creation_time = 5; + required Mpb_Mode mode = 6; + repeated string upi = 7; + repeated string repairing = 8; + repeated string down = 9; + optional bytes opaque_flap = 10; + optional bytes opaque_inner = 11; + required bytes opaque_dbg = 12; + required bytes opaque_dbg2 = 13; + repeated Mpb_MembersDictEntry members_dict = 14; +} + +////////////////////////////////////////// +// +// Low level API requests: +// +// echo() : Mpb_EchoReq and Mpb_EchoResp (reused from high level API) +// auth() : Mpb_AuthReq and Mpb_AuthResp (reused from high level API) +// get_latest_epochid() : Mpb_GetLatestEpochIDReq and Mpb_GetLatestEpochIDResp +// +////////////////////////////////////////// + +// Low level API: get_latest_epochid() request & response + +message Mpb_LL_GetLatestEpochIDReq { + required Mpb_ProjType type = 1; +} + +message Mpb_LL_GetLatestEpochIDResp { + required uint32 Mpb_GeneralStatusCode = 1; + optional Mpb_EpochID epoch_id = 2; +} + +// Low level API: read_latest_projection() request & response + +message Mpb_LL_ReadLatestProjectionReq { + required Mpb_ProjType type = 1; +} + +message Mpb_LL_ReadLatestProjectionResp { + required uint32 Mpb_GeneralStatusCode = 1; + optional Mpb_ProjectionV1 proj = 1; +} + +// Low level API: read_projection() request & response + +message Mpb_LL_ReadProjectionReq { + required Mpb_ProjType type = 1; + required uint32 epoch_number = 2; +} + +message Mpb_LL_ReadProjectionResp { + required uint32 Mpb_GeneralStatusCode = 1; + optional Mpb_ProjectionV1 proj = 2; +} + +// Low level API: write_projection() request & response + +message Mpb_LL_WriteProjectionReq { + required Mpb_ProjType type = 1; + required Mpb_ProjectionV1 proj = 1; +} + +message Mpb_LL_WriteProjectionResp { + required uint32 Mpb_GeneralStatusCode = 1; +} + +// Low level API: get_all_projections() request & response + +message Mpb_LL_GetAllProjectionsReq { + required Mpb_ProjType type = 1; +} + +message Mpb_LL_GetAllProjectionsResp { + required uint32 Mpb_GeneralStatusCode = 1; + repeated Mpb_ProjectionV1 proj = 2; +} + +// Low level API: list_all_projections() request & response + +message Mpb_LL_ListAllProjectionsReq { + required Mpb_ProjType type = 1; +} + +message Mpb_LL_ListAllProjectionsResp { + required uint32 Mpb_GeneralStatusCode = 1; + repeated uint32 epochs = 2; +} + +////////////////////////////////////////// +// +// Low API request & response wrapper +// +////////////////////////////////////////// + +message Mpb_LL_Request { + // TODO: If we wish to support pipelined requests sometime in the + // future, this is the placeholder to do it. + required bytes req_id = 1; + + // The client should only define one request message. If the client + // includes multiple requests here, the server may pick/choose an + // arbitrary one. + // NOTE: The erlang protobuffs compiler doesn't support 'oneof'. + // But 'oneof' appears to be a very tiny memory optimization + // that not all languages might care about? (Erlang doesn't) + optional Mpb_EchoReq echo = 10; // Re-use from high level API + optional Mpb_AuthReq auth = 11; // Re-use from high level API + optional Mpb_LL_GetLatestEpochIDReq = 12; + optional Mpb_LL_ReadLatestProjectionReq = 13; + optional Mpb_LL_ReadProjectionReq = 14; + optional Mpb_LL_WriteProjectionReq = 15; + optional Mpb_LL_GetAllProjectionsReq = 16; + optional Mpb_LL_ListAllProjectionsReq = 17; +} + +message Mpb_LL_Response { + // TODO: If we wish to support pipelined requests sometime in the + // future, this is the placeholder to do it. + required bytes req_id = 1; + + // The server will define only one of the optional responses below. + + // Generic error response, typically used when something quite + // bad/unexpected happened within the server. + // Clients should always check this response and, if defined, + // ignore any request-specific response at codes 10+. + optional Mpb_ErrorResp generic = 2; + + // Specific responses. + optional Mpb_EchoResp echo = 10; // Re-use from high level API + optional Mpb_AuthResp auth = 11; // Re-use from high level API + optional Mpb_LL_GetLatestEpochIDResp = 12; +} diff --git a/src/machi_pb_wrap.erl b/src/machi_pb_wrap.erl index d79f3ae..c2e21bc 100644 --- a/src/machi_pb_wrap.erl +++ b/src/machi_pb_wrap.erl @@ -30,70 +30,131 @@ -compile(export_all). -endif. % TEST -enc_p_srvr(#p_srvr{name=Name, +enc_p_srvr(P) -> + machi_pb:encode_mpb_p_srvr(conv_from_p_srvr(P)). + +dec_p_srvr(Bin) -> + conv_to_p_srvr(machi_pb:decode_mpb_p_srvr(Bin)). + +conv_from_p_srvr(#p_srvr{name=Name, proto_mod=ProtoMod, address=Address, port=Port, props=Props}) -> - machi_pb:encode_mpb_p_srvr(#mpb_p_srvr{name=to_list(Name), - proto_mod=to_list(ProtoMod), - address=to_list(Address), - port=to_list(Port), - props=todo_enc_opaque(Props)}). + #mpb_p_srvr{name=to_list(Name), + proto_mod=to_list(ProtoMod), + address=to_list(Address), + port=to_list(Port), + props=enc_sexp(Props)}. -dec_p_srvr(Bin) -> - #mpb_p_srvr{name=Name, - proto_mod=ProtoMod, - address=Address, - port=Port, - props=Props} = machi_pb:decode_mpb_p_srvr(Bin), +conv_to_p_srvr(#mpb_p_srvr{name=Name, + proto_mod=ProtoMod, + address=Address, + port=Port, + props=Props}) -> #p_srvr{name=to_atom(Name), proto_mod=to_atom(ProtoMod), address=to_list(Address), port=to_integer(Port), - props=todo_dec_opaque(Props)}. + props=dec_sexp(to_list(Props))}. -enc_projection_v1(#projection_v1{dbg=Dbg, - dbg2=Dbg2, - members_dict=MembersDict} = P) -> - term_to_binary(P#projection_v1{dbg=enc_sexp(Dbg), - dbg2=enc_sexp(Dbg2), - members_dict=enc_members_dict(MembersDict)}). +enc_projection_v1(P) -> + %% Awww, flatten it here + list_to_binary( + machi_pb:encode_mpb_projectionv1(conv_from_projection_v1(P))). dec_projection_v1(Bin) -> - P = #projection_v1{dbg=Dbg, - dbg2=Dbg2, - members_dict=MembersDict} = binary_to_term(Bin), - P#projection_v1{dbg=dec_sexp(Dbg), - dbg2=dec_sexp(Dbg2), - members_dict=dec_members_dict(MembersDict)}. + conv_to_projection_v1(machi_pb:decode_mpb_projectionv1(Bin)). + +conv_from_projection_v1(#projection_v1{epoch_number=Epoch, + epoch_csum=CSum, + author_server=Author, + all_members=AllMembers, + creation_time=CTime, + mode=Mode, + upi=UPI, + repairing=Repairing, + down=Down, + flap=Flap, + inner=Inner, + dbg=Dbg, + dbg2=Dbg2, + members_dict=MembersDict}) -> + #mpb_projectionv1{epoch_number=Epoch, + epoch_csum=CSum, + author_server=to_list(Author), + all_members=[to_list(X) || X <- AllMembers], + creation_time=conv_from_now(CTime), + mode=conv_from_mode(Mode), + upi=[to_list(X) || X <- UPI], + repairing=[to_list(X) || X <- Repairing], + down=[to_list(X) || X <- Down], + opaque_flap=enc_optional_sexp(Flap), + opaque_inner=enc_optional_sexp(Inner), + opaque_dbg=enc_sexp(Dbg), + opaque_dbg2=enc_sexp(Dbg2), + members_dict=conv_from_members_dict(MembersDict)}. + +conv_to_projection_v1(#mpb_projectionv1{epoch_number=Epoch, + epoch_csum=CSum, + author_server=Author, + all_members=AllMembers, + creation_time=CTime, + mode=Mode, + upi=UPI, + repairing=Repairing, + down=Down, + opaque_flap=Flap, + opaque_inner=Inner, + opaque_dbg=Dbg, + opaque_dbg2=Dbg2, + members_dict=MembersDict}) -> + #projection_v1{epoch_number=Epoch, + epoch_csum=CSum, + author_server=to_atom(Author), + all_members=[to_atom(X) || X <- AllMembers], + creation_time=conv_to_now(CTime), + mode=conv_to_mode(Mode), + upi=[to_atom(X) || X <- UPI], + repairing=[to_atom(X) || X <- Repairing], + down=[to_atom(X) || X <- Down], + flap=dec_optional_sexp(Flap), + inner=dec_optional_sexp(Inner), + dbg=dec_sexp(Dbg), + dbg2=dec_sexp(Dbg2), + members_dict=conv_to_members_dict(MembersDict)}. %%%%%%%%%%%%%%%%%%% enc_sexp(T) -> lists:flatten(io_lib:format("~w.", [T])). -dec_sexp(String) -> +dec_sexp(Bin) when is_binary(Bin) -> + dec_sexp(binary_to_list(Bin)); +dec_sexp(String) when is_list(String) -> {ok,Tks,_} = erl_scan:string(String), {ok,E} = erl_parse:parse_exprs(Tks), {value,Funs,_} = erl_eval:exprs(E,[]), Funs. -enc_members_dict(D) -> +enc_optional_sexp(undefined) -> + undefined; +enc_optional_sexp(T) -> + enc_sexp(T). + +dec_optional_sexp(undefined) -> + undefined; +dec_optional_sexp(T) -> + dec_sexp(T). + +conv_from_members_dict(D) -> %% Use list_to_binary() here to "flatten" the serialized #p_srvr{} - [{K, list_to_binary(enc_p_srvr(V))} || {K, V} <- orddict:to_list(D)]. + [#mpb_membersdictentry{key=to_list(K), val=conv_from_p_srvr(V)} || + {K, V} <- orddict:to_list(D)]. -dec_members_dict(List) -> - orddict:from_list([{K, dec_p_srvr(V)} || {K, V} <- List]). - -to_binary(X) when is_list(X) -> - list_to_binary(X); -to_binary(X) when is_integer(X) -> - list_to_binary(integer_to_list(X)); -to_binary(X) when is_atom(X) -> - erlang:atom_to_binary(X, latin1); -to_binary(X) when is_binary(X) -> - X. +conv_to_members_dict(List) -> + orddict:from_list([{to_atom(K), conv_to_p_srvr(V)} || + #mpb_membersdictentry{key=K, val=V} <- List]). to_list(X) when is_atom(X) -> atom_to_list(X); @@ -118,8 +179,16 @@ to_integer(X) when is_binary(X) -> to_integer(X) when is_integer(X) -> X. -todo_enc_opaque(X) -> - erlang:term_to_binary(X). +conv_from_now({A,B,C}) -> + #mpb_now{sec=(1000000 * A) + B, + usec=C}. + +conv_to_now(#mpb_now{sec=Sec, usec=USec}) -> + {Sec div 1000000, Sec rem 1000000, USec}. + +conv_from_mode(ap_mode) -> 'AP_MODE'; +conv_from_mode(cp_mode) -> 'CP_MODE'. + +conv_to_mode('AP_MODE') -> ap_mode; +conv_to_mode('CP_MODE') -> cp_mode. -todo_dec_opaque(X) -> - erlang:binary_to_term(X).