diff --git a/apis/job_create.go b/apis/job_create.go index 7919d7e..ed54f9b 100644 --- a/apis/job_create.go +++ b/apis/job_create.go @@ -16,9 +16,5 @@ func (api *API) JobCreate(ctx context.Context, req *entity.JobCreateRequest) (*e return nil, err } - if err := api.exe.Start(ctx, job); err != nil { - return nil, err - } - return &entity.JobCreateReply{Job: convertJobs(job)[0]}, nil } diff --git a/apis/job_edit_state.go b/apis/job_edit_state.go new file mode 100644 index 0000000..43ec6fb --- /dev/null +++ b/apis/job_edit_state.go @@ -0,0 +1,44 @@ +package apis + +import ( + "context" + "fmt" + + "github.com/samuelncui/yatm/entity" +) + +func (api *API) JobEditState(ctx context.Context, req *entity.JobEditStateRequest) (*entity.JobEditStateReply, error) { + job, err := api.exe.GetJob(ctx, req.Id) + if err != nil { + return nil, err + } + if job == nil { + return nil, fmt.Errorf("job not found, id= %d", req.Id) + } + + if job.Status == entity.JobStatus_PROCESSING { + return nil, fmt.Errorf("job status 'PROCESSING' is unexpected") + } + if req.Status != nil { + if *req.Status == entity.JobStatus_PROCESSING { + return nil, fmt.Errorf("job target status 'PROCESSING' is unexpected") + } + job.Status = *req.Status + } + + job.State = req.State + if _, err := api.exe.SaveJob(ctx, job); err != nil { + return nil, fmt.Errorf("save job fail, %w", err) + } + + executor, err := api.exe.GetJobExecutor(ctx, job.ID) + if err != nil { + return nil, fmt.Errorf("get job executor fail, %w", err) + } + + if err := executor.Close(ctx); err != nil { + return nil, fmt.Errorf("close job executor fail, %w", err) + } + + return &entity.JobEditStateReply{}, nil +} diff --git a/apis/job_next.go b/apis/job_next.go index eb0cf79..0af3606 100644 --- a/apis/job_next.go +++ b/apis/job_next.go @@ -6,15 +6,10 @@ import ( "github.com/samuelncui/yatm/entity" ) -func (api *API) JobNext(ctx context.Context, req *entity.JobNextRequest) (*entity.JobNextReply, error) { - job, err := api.exe.GetJob(ctx, req.Id) - if err != nil { +func (api *API) JobDispatch(ctx context.Context, req *entity.JobDispatchRequest) (*entity.JobDispatchReply, error) { + if err := api.exe.Dispatch(ctx, req.Id, req.Param); err != nil { return nil, err } - if err := api.exe.Submit(ctx, job, req.Param); err != nil { - return nil, err - } - - return &entity.JobNextReply{Job: convertJobs(job)[0]}, nil + return &entity.JobDispatchReply{}, nil } diff --git a/entity/job.pb.go b/entity/job.pb.go index 422d0f8..6d4d298 100644 --- a/entity/job.pb.go +++ b/entity/job.pb.go @@ -328,19 +328,19 @@ func (*JobState_Archive) isJobState_State() {} func (*JobState_Restore) isJobState_State() {} -type JobNextParam struct { +type JobDispatchParam struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Param: - // *JobNextParam_Archive - // *JobNextParam_Restore - Param isJobNextParam_Param `protobuf_oneof:"param"` + // *JobDispatchParam_Archive + // *JobDispatchParam_Restore + Param isJobDispatchParam_Param `protobuf_oneof:"param"` } -func (x *JobNextParam) Reset() { - *x = JobNextParam{} +func (x *JobDispatchParam) Reset() { + *x = JobDispatchParam{} if protoimpl.UnsafeEnabled { mi := &file_job_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -348,13 +348,13 @@ func (x *JobNextParam) Reset() { } } -func (x *JobNextParam) String() string { +func (x *JobDispatchParam) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobNextParam) ProtoMessage() {} +func (*JobDispatchParam) ProtoMessage() {} -func (x *JobNextParam) ProtoReflect() protoreflect.Message { +func (x *JobDispatchParam) ProtoReflect() protoreflect.Message { mi := &file_job_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -366,47 +366,47 @@ func (x *JobNextParam) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use JobNextParam.ProtoReflect.Descriptor instead. -func (*JobNextParam) Descriptor() ([]byte, []int) { +// Deprecated: Use JobDispatchParam.ProtoReflect.Descriptor instead. +func (*JobDispatchParam) Descriptor() ([]byte, []int) { return file_job_proto_rawDescGZIP(), []int{3} } -func (m *JobNextParam) GetParam() isJobNextParam_Param { +func (m *JobDispatchParam) GetParam() isJobDispatchParam_Param { if m != nil { return m.Param } return nil } -func (x *JobNextParam) GetArchive() *JobArchiveNextParam { - if x, ok := x.GetParam().(*JobNextParam_Archive); ok { +func (x *JobDispatchParam) GetArchive() *JobArchiveDispatchParam { + if x, ok := x.GetParam().(*JobDispatchParam_Archive); ok { return x.Archive } return nil } -func (x *JobNextParam) GetRestore() *JobRestoreNextParam { - if x, ok := x.GetParam().(*JobNextParam_Restore); ok { +func (x *JobDispatchParam) GetRestore() *JobRestoreDispatchParam { + if x, ok := x.GetParam().(*JobDispatchParam_Restore); ok { return x.Restore } return nil } -type isJobNextParam_Param interface { - isJobNextParam_Param() +type isJobDispatchParam_Param interface { + isJobDispatchParam_Param() } -type JobNextParam_Archive struct { - Archive *JobArchiveNextParam `protobuf:"bytes,1,opt,name=archive,proto3,oneof"` +type JobDispatchParam_Archive struct { + Archive *JobArchiveDispatchParam `protobuf:"bytes,1,opt,name=archive,proto3,oneof"` } -type JobNextParam_Restore struct { - Restore *JobRestoreNextParam `protobuf:"bytes,2,opt,name=restore,proto3,oneof"` +type JobDispatchParam_Restore struct { + Restore *JobRestoreDispatchParam `protobuf:"bytes,2,opt,name=restore,proto3,oneof"` } -func (*JobNextParam_Archive) isJobNextParam_Param() {} +func (*JobDispatchParam_Archive) isJobDispatchParam_Param() {} -func (*JobNextParam_Restore) isJobNextParam_Param() {} +func (*JobDispatchParam_Restore) isJobDispatchParam_Param() {} type CreatableJob struct { state protoimpl.MessageState @@ -697,57 +697,58 @@ var file_job_proto_rawDesc = []byte{ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x07, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x93, 0x01, 0x0a, 0x0c, 0x4a, 0x6f, 0x62, 0x4e, - 0x65, 0x78, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x3c, 0x0a, 0x07, 0x61, 0x72, 0x63, 0x68, - 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, - 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, - 0x76, 0x65, 0x4e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x07, 0x61, - 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x4e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x4f, 0x0a, - 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x23, 0x0a, 0x05, 0x70, 0x61, 0x72, - 0x61, 0x6d, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, - 0x6f, 0x62, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x52, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x90, - 0x01, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x6a, - 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x18, 0x21, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x22, - 0x20, 0x01, 0x28, 0x03, 0x48, 0x02, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x88, 0x01, - 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x08, 0x0a, 0x06, - 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x22, 0x7f, 0x0a, 0x17, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x6c, 0x79, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x0f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, - 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x18, 0x21, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, - 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, - 0x79, 0x12, 0x3a, 0x0a, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, - 0x2e, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, - 0x61, 0x79, 0x48, 0x00, 0x52, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x3a, 0x0a, - 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, - 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, 0x6f, 0x62, - 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x48, 0x00, - 0x52, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x64, 0x69, 0x73, - 0x70, 0x6c, 0x61, 0x79, 0x2a, 0x6b, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x52, 0x41, 0x46, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, - 0x4e, 0x4f, 0x54, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, - 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x4f, 0x43, - 0x45, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x50, - 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, - 0x44, 0x10, 0x7f, 0x12, 0x0c, 0x0a, 0x07, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0xff, - 0x01, 0x42, 0x23, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x73, 0x61, 0x6d, 0x75, 0x65, 0x6c, 0x6e, 0x63, 0x75, 0x69, 0x2f, 0x79, 0x61, 0x74, 0x6d, 0x2f, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x9f, 0x01, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x44, + 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x40, 0x0a, 0x07, + 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x41, + 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x40, + 0x0a, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, 0x6f, + 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x42, 0x07, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x4f, 0x0a, 0x0c, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x69, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x23, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x11, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x52, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x90, 0x01, 0x0a, 0x09, 0x4a, + 0x6f, 0x62, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, + 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x21, + 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, + 0x12, 0x1b, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x22, 0x20, 0x01, 0x28, 0x03, + 0x48, 0x02, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, + 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x7f, 0x0a, + 0x17, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x0f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x00, 0x52, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x69, 0x6e, 0x63, 0x65, + 0x4e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x21, + 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, + 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x63, + 0x65, 0x5f, 0x6e, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x8f, + 0x01, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x3a, 0x0a, + 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2e, 0x4a, 0x6f, 0x62, + 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x48, 0x00, + 0x52, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x72, 0x65, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6a, 0x6f, 0x62, + 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x2a, 0x6b, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x09, 0x0a, + 0x05, 0x44, 0x52, 0x41, 0x46, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x54, 0x5f, + 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, + 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x49, + 0x4e, 0x47, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, + 0x44, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x7f, 0x12, + 0x0c, 0x0a, 0x07, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0xff, 0x01, 0x42, 0x23, 0x5a, + 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x61, 0x6d, 0x75, + 0x65, 0x6c, 0x6e, 0x63, 0x75, 0x69, 0x2f, 0x79, 0x61, 0x74, 0x6d, 0x2f, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -769,7 +770,7 @@ var file_job_proto_goTypes = []interface{}{ (*Job)(nil), // 1: job.Job (*JobParam)(nil), // 2: job.JobParam (*JobState)(nil), // 3: job.JobState - (*JobNextParam)(nil), // 4: job.JobNextParam + (*JobDispatchParam)(nil), // 4: job.JobDispatchParam (*CreatableJob)(nil), // 5: job.CreatableJob (*JobFilter)(nil), // 6: job.JobFilter (*JobRecentlyUpdateFilter)(nil), // 7: job.JobRecentlyUpdateFilter @@ -778,8 +779,8 @@ var file_job_proto_goTypes = []interface{}{ (*JobRestoreParam)(nil), // 10: job_restore.JobRestoreParam (*JobArchiveState)(nil), // 11: job_archive.JobArchiveState (*JobRestoreState)(nil), // 12: job_restore.JobRestoreState - (*JobArchiveNextParam)(nil), // 13: job_archive.JobArchiveNextParam - (*JobRestoreNextParam)(nil), // 14: job_restore.JobRestoreNextParam + (*JobArchiveDispatchParam)(nil), // 13: job_archive.JobArchiveDispatchParam + (*JobRestoreDispatchParam)(nil), // 14: job_restore.JobRestoreDispatchParam (*JobArchiveDisplay)(nil), // 15: job_archive.JobArchiveDisplay (*JobRestoreDisplay)(nil), // 16: job_restore.JobRestoreDisplay } @@ -790,8 +791,8 @@ var file_job_proto_depIdxs = []int32{ 10, // 3: job.JobParam.restore:type_name -> job_restore.JobRestoreParam 11, // 4: job.JobState.archive:type_name -> job_archive.JobArchiveState 12, // 5: job.JobState.restore:type_name -> job_restore.JobRestoreState - 13, // 6: job.JobNextParam.archive:type_name -> job_archive.JobArchiveNextParam - 14, // 7: job.JobNextParam.restore:type_name -> job_restore.JobRestoreNextParam + 13, // 6: job.JobDispatchParam.archive:type_name -> job_archive.JobArchiveDispatchParam + 14, // 7: job.JobDispatchParam.restore:type_name -> job_restore.JobRestoreDispatchParam 2, // 8: job.CreatableJob.param:type_name -> job.JobParam 0, // 9: job.JobFilter.status:type_name -> job.JobStatus 15, // 10: job.JobDisplay.archive:type_name -> job_archive.JobArchiveDisplay @@ -848,7 +849,7 @@ func file_job_proto_init() { } } file_job_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobNextParam); i { + switch v := v.(*JobDispatchParam); i { case 0: return &v.state case 1: @@ -917,8 +918,8 @@ func file_job_proto_init() { (*JobState_Restore)(nil), } file_job_proto_msgTypes[3].OneofWrappers = []interface{}{ - (*JobNextParam_Archive)(nil), - (*JobNextParam_Restore)(nil), + (*JobDispatchParam_Archive)(nil), + (*JobDispatchParam_Restore)(nil), } file_job_proto_msgTypes[5].OneofWrappers = []interface{}{} file_job_proto_msgTypes[6].OneofWrappers = []interface{}{} diff --git a/entity/job.proto b/entity/job.proto index 01192de..9879f0e 100644 --- a/entity/job.proto +++ b/entity/job.proto @@ -41,10 +41,10 @@ message JobState { } } -message JobNextParam { +message JobDispatchParam { oneof param { - job_archive.JobArchiveNextParam archive = 1; - job_restore.JobRestoreNextParam restore = 2; + job_archive.JobArchiveDispatchParam archive = 1; + job_restore.JobRestoreDispatchParam restore = 2; } } diff --git a/entity/job_archive.pb.go b/entity/job_archive.pb.go index 2bd218a..68894a1 100644 --- a/entity/job_archive.pb.go +++ b/entity/job_archive.pb.go @@ -119,20 +119,20 @@ func (x *JobArchiveParam) GetSources() []*Source { return nil } -type JobArchiveNextParam struct { +type JobArchiveDispatchParam struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Param: - // *JobArchiveNextParam_WaitForTape - // *JobArchiveNextParam_Copying - // *JobArchiveNextParam_Finished - Param isJobArchiveNextParam_Param `protobuf_oneof:"param"` + // *JobArchiveDispatchParam_WaitForTape + // *JobArchiveDispatchParam_Copying + // *JobArchiveDispatchParam_Finished + Param isJobArchiveDispatchParam_Param `protobuf_oneof:"param"` } -func (x *JobArchiveNextParam) Reset() { - *x = JobArchiveNextParam{} +func (x *JobArchiveDispatchParam) Reset() { + *x = JobArchiveDispatchParam{} if protoimpl.UnsafeEnabled { mi := &file_job_archive_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -140,13 +140,13 @@ func (x *JobArchiveNextParam) Reset() { } } -func (x *JobArchiveNextParam) String() string { +func (x *JobArchiveDispatchParam) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobArchiveNextParam) ProtoMessage() {} +func (*JobArchiveDispatchParam) ProtoMessage() {} -func (x *JobArchiveNextParam) ProtoReflect() protoreflect.Message { +func (x *JobArchiveDispatchParam) ProtoReflect() protoreflect.Message { mi := &file_job_archive_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -158,60 +158,60 @@ func (x *JobArchiveNextParam) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use JobArchiveNextParam.ProtoReflect.Descriptor instead. -func (*JobArchiveNextParam) Descriptor() ([]byte, []int) { +// Deprecated: Use JobArchiveDispatchParam.ProtoReflect.Descriptor instead. +func (*JobArchiveDispatchParam) Descriptor() ([]byte, []int) { return file_job_archive_proto_rawDescGZIP(), []int{1} } -func (m *JobArchiveNextParam) GetParam() isJobArchiveNextParam_Param { +func (m *JobArchiveDispatchParam) GetParam() isJobArchiveDispatchParam_Param { if m != nil { return m.Param } return nil } -func (x *JobArchiveNextParam) GetWaitForTape() *JobArchiveWaitForTapeParam { - if x, ok := x.GetParam().(*JobArchiveNextParam_WaitForTape); ok { +func (x *JobArchiveDispatchParam) GetWaitForTape() *JobArchiveWaitForTapeParam { + if x, ok := x.GetParam().(*JobArchiveDispatchParam_WaitForTape); ok { return x.WaitForTape } return nil } -func (x *JobArchiveNextParam) GetCopying() *JobArchiveCopyingParam { - if x, ok := x.GetParam().(*JobArchiveNextParam_Copying); ok { +func (x *JobArchiveDispatchParam) GetCopying() *JobArchiveCopyingParam { + if x, ok := x.GetParam().(*JobArchiveDispatchParam_Copying); ok { return x.Copying } return nil } -func (x *JobArchiveNextParam) GetFinished() *JobArchiveFinishedParam { - if x, ok := x.GetParam().(*JobArchiveNextParam_Finished); ok { +func (x *JobArchiveDispatchParam) GetFinished() *JobArchiveFinishedParam { + if x, ok := x.GetParam().(*JobArchiveDispatchParam_Finished); ok { return x.Finished } return nil } -type isJobArchiveNextParam_Param interface { - isJobArchiveNextParam_Param() +type isJobArchiveDispatchParam_Param interface { + isJobArchiveDispatchParam_Param() } -type JobArchiveNextParam_WaitForTape struct { +type JobArchiveDispatchParam_WaitForTape struct { WaitForTape *JobArchiveWaitForTapeParam `protobuf:"bytes,1,opt,name=wait_for_tape,json=waitForTape,proto3,oneof"` } -type JobArchiveNextParam_Copying struct { +type JobArchiveDispatchParam_Copying struct { Copying *JobArchiveCopyingParam `protobuf:"bytes,2,opt,name=copying,proto3,oneof"` } -type JobArchiveNextParam_Finished struct { +type JobArchiveDispatchParam_Finished struct { Finished *JobArchiveFinishedParam `protobuf:"bytes,255,opt,name=finished,proto3,oneof"` } -func (*JobArchiveNextParam_WaitForTape) isJobArchiveNextParam_Param() {} +func (*JobArchiveDispatchParam_WaitForTape) isJobArchiveDispatchParam_Param() {} -func (*JobArchiveNextParam_Copying) isJobArchiveNextParam_Param() {} +func (*JobArchiveDispatchParam_Copying) isJobArchiveDispatchParam_Param() {} -func (*JobArchiveNextParam_Finished) isJobArchiveNextParam_Param() {} +func (*JobArchiveDispatchParam_Finished) isJobArchiveDispatchParam_Param() {} type JobArchiveWaitForTapeParam struct { state protoimpl.MessageState @@ -503,61 +503,61 @@ var file_job_archive_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x28, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x13, - 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x4e, 0x65, 0x78, 0x74, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x12, 0x4d, 0x0a, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, - 0x74, 0x61, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6a, 0x6f, 0x62, + 0x63, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0xf7, 0x01, 0x0a, 0x17, + 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x4d, 0x0a, 0x0d, 0x77, 0x61, 0x69, 0x74, 0x5f, + 0x66, 0x6f, 0x72, 0x5f, 0x74, 0x61, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2e, 0x4a, 0x6f, 0x62, + 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x54, 0x61, + 0x70, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x0b, 0x77, 0x61, 0x69, 0x74, 0x46, + 0x6f, 0x72, 0x54, 0x61, 0x70, 0x65, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x70, 0x79, 0x69, 0x6e, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x72, + 0x63, 0x68, 0x69, 0x76, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, + 0x43, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x07, + 0x63, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x12, 0x43, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, + 0x68, 0x65, 0x64, 0x18, 0xff, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, + 0x69, 0x76, 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x48, 0x00, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x1c, 0x0a, 0x1a, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x54, 0x61, 0x70, 0x65, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x0b, 0x77, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x54, 0x61, - 0x70, 0x65, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, - 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x70, 0x79, - 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x70, 0x79, - 0x69, 0x6e, 0x67, 0x12, 0x43, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, - 0xff, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x72, 0x63, - 0x68, 0x69, 0x76, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x46, - 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x08, - 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x22, 0x1c, 0x0a, 0x1a, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x57, - 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x54, 0x61, 0x70, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x22, - 0x5e, 0x0a, 0x16, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x70, - 0x79, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0x19, 0x0a, 0x17, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x46, 0x69, 0x6e, - 0x69, 0x73, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x71, 0x0a, 0x0f, 0x4a, 0x6f, - 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, - 0x04, 0x73, 0x74, 0x65, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6a, 0x6f, - 0x62, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, - 0x68, 0x69, 0x76, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x12, 0x2d, - 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0xdf, 0x01, - 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x44, 0x69, 0x73, 0x70, - 0x6c, 0x61, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x5f, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x69, 0x65, - 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, - 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, - 0x70, 0x69, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x73, - 0x70, 0x65, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x05, 0x73, 0x70, - 0x65, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x2a, - 0x4c, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x53, 0x74, 0x65, - 0x70, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x11, - 0x0a, 0x0d, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x54, 0x41, 0x50, 0x45, 0x10, - 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0d, - 0x0a, 0x08, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0xff, 0x01, 0x42, 0x23, 0x5a, - 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x61, 0x6d, 0x75, - 0x65, 0x6c, 0x6e, 0x63, 0x75, 0x69, 0x2f, 0x79, 0x61, 0x74, 0x6d, 0x2f, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x61, 0x6d, 0x22, 0x5e, 0x0a, 0x16, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, + 0x65, 0x43, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x16, 0x0a, + 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x72, 0x63, 0x6f, 0x64, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, + 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x71, + 0x0a, 0x0f, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x74, 0x65, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1b, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x2e, 0x4a, 0x6f, + 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x04, 0x73, 0x74, + 0x65, 0x70, 0x12, 0x2d, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x22, 0xdf, 0x01, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, + 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x70, 0x69, 0x65, + 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, + 0x6f, 0x70, 0x69, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, + 0x70, 0x69, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1f, 0x0a, + 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1f, + 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, + 0x19, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, + 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x70, + 0x65, 0x65, 0x64, 0x2a, 0x4c, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, + 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, + 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x54, + 0x41, 0x50, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47, + 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0xff, + 0x01, 0x42, 0x23, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x73, 0x61, 0x6d, 0x75, 0x65, 0x6c, 0x6e, 0x63, 0x75, 0x69, 0x2f, 0x79, 0x61, 0x74, 0x6d, 0x2f, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -577,7 +577,7 @@ var file_job_archive_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_job_archive_proto_goTypes = []interface{}{ (JobArchiveStep)(0), // 0: job_archive.JobArchiveStep (*JobArchiveParam)(nil), // 1: job_archive.JobArchiveParam - (*JobArchiveNextParam)(nil), // 2: job_archive.JobArchiveNextParam + (*JobArchiveDispatchParam)(nil), // 2: job_archive.JobArchiveDispatchParam (*JobArchiveWaitForTapeParam)(nil), // 3: job_archive.JobArchiveWaitForTapeParam (*JobArchiveCopyingParam)(nil), // 4: job_archive.JobArchiveCopyingParam (*JobArchiveFinishedParam)(nil), // 5: job_archive.JobArchiveFinishedParam @@ -588,9 +588,9 @@ var file_job_archive_proto_goTypes = []interface{}{ } var file_job_archive_proto_depIdxs = []int32{ 8, // 0: job_archive.JobArchiveParam.sources:type_name -> source.Source - 3, // 1: job_archive.JobArchiveNextParam.wait_for_tape:type_name -> job_archive.JobArchiveWaitForTapeParam - 4, // 2: job_archive.JobArchiveNextParam.copying:type_name -> job_archive.JobArchiveCopyingParam - 5, // 3: job_archive.JobArchiveNextParam.finished:type_name -> job_archive.JobArchiveFinishedParam + 3, // 1: job_archive.JobArchiveDispatchParam.wait_for_tape:type_name -> job_archive.JobArchiveWaitForTapeParam + 4, // 2: job_archive.JobArchiveDispatchParam.copying:type_name -> job_archive.JobArchiveCopyingParam + 5, // 3: job_archive.JobArchiveDispatchParam.finished:type_name -> job_archive.JobArchiveFinishedParam 0, // 4: job_archive.JobArchiveState.step:type_name -> job_archive.JobArchiveStep 9, // 5: job_archive.JobArchiveState.sources:type_name -> source.SourceState 6, // [6:6] is the sub-list for method output_type @@ -620,7 +620,7 @@ func file_job_archive_proto_init() { } } file_job_archive_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobArchiveNextParam); i { + switch v := v.(*JobArchiveDispatchParam); i { case 0: return &v.state case 1: @@ -693,9 +693,9 @@ func file_job_archive_proto_init() { } } file_job_archive_proto_msgTypes[1].OneofWrappers = []interface{}{ - (*JobArchiveNextParam_WaitForTape)(nil), - (*JobArchiveNextParam_Copying)(nil), - (*JobArchiveNextParam_Finished)(nil), + (*JobArchiveDispatchParam_WaitForTape)(nil), + (*JobArchiveDispatchParam_Copying)(nil), + (*JobArchiveDispatchParam_Finished)(nil), } file_job_archive_proto_msgTypes[6].OneofWrappers = []interface{}{} type x struct{} diff --git a/entity/job_archive.proto b/entity/job_archive.proto index 70354b4..178dcbc 100644 --- a/entity/job_archive.proto +++ b/entity/job_archive.proto @@ -16,7 +16,7 @@ message JobArchiveParam { repeated source.Source sources = 1; } -message JobArchiveNextParam { +message JobArchiveDispatchParam { oneof param { JobArchiveWaitForTapeParam wait_for_tape = 1; JobArchiveCopyingParam copying = 2; diff --git a/entity/job_restore.pb.go b/entity/job_restore.pb.go index 7163f8a..f690dd5 100644 --- a/entity/job_restore.pb.go +++ b/entity/job_restore.pb.go @@ -119,20 +119,20 @@ func (x *JobRestoreParam) GetFileIds() []int64 { return nil } -type JobRestoreNextParam struct { +type JobRestoreDispatchParam struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Param: - // *JobRestoreNextParam_WaitForTape - // *JobRestoreNextParam_Copying - // *JobRestoreNextParam_Finished - Param isJobRestoreNextParam_Param `protobuf_oneof:"param"` + // *JobRestoreDispatchParam_WaitForTape + // *JobRestoreDispatchParam_Copying + // *JobRestoreDispatchParam_Finished + Param isJobRestoreDispatchParam_Param `protobuf_oneof:"param"` } -func (x *JobRestoreNextParam) Reset() { - *x = JobRestoreNextParam{} +func (x *JobRestoreDispatchParam) Reset() { + *x = JobRestoreDispatchParam{} if protoimpl.UnsafeEnabled { mi := &file_job_restore_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -140,13 +140,13 @@ func (x *JobRestoreNextParam) Reset() { } } -func (x *JobRestoreNextParam) String() string { +func (x *JobRestoreDispatchParam) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobRestoreNextParam) ProtoMessage() {} +func (*JobRestoreDispatchParam) ProtoMessage() {} -func (x *JobRestoreNextParam) ProtoReflect() protoreflect.Message { +func (x *JobRestoreDispatchParam) ProtoReflect() protoreflect.Message { mi := &file_job_restore_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -158,60 +158,60 @@ func (x *JobRestoreNextParam) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use JobRestoreNextParam.ProtoReflect.Descriptor instead. -func (*JobRestoreNextParam) Descriptor() ([]byte, []int) { +// Deprecated: Use JobRestoreDispatchParam.ProtoReflect.Descriptor instead. +func (*JobRestoreDispatchParam) Descriptor() ([]byte, []int) { return file_job_restore_proto_rawDescGZIP(), []int{1} } -func (m *JobRestoreNextParam) GetParam() isJobRestoreNextParam_Param { +func (m *JobRestoreDispatchParam) GetParam() isJobRestoreDispatchParam_Param { if m != nil { return m.Param } return nil } -func (x *JobRestoreNextParam) GetWaitForTape() *JobRestoreWaitForTapeParam { - if x, ok := x.GetParam().(*JobRestoreNextParam_WaitForTape); ok { +func (x *JobRestoreDispatchParam) GetWaitForTape() *JobRestoreWaitForTapeParam { + if x, ok := x.GetParam().(*JobRestoreDispatchParam_WaitForTape); ok { return x.WaitForTape } return nil } -func (x *JobRestoreNextParam) GetCopying() *JobRestoreCopyingParam { - if x, ok := x.GetParam().(*JobRestoreNextParam_Copying); ok { +func (x *JobRestoreDispatchParam) GetCopying() *JobRestoreCopyingParam { + if x, ok := x.GetParam().(*JobRestoreDispatchParam_Copying); ok { return x.Copying } return nil } -func (x *JobRestoreNextParam) GetFinished() *JobRestoreFinishedParam { - if x, ok := x.GetParam().(*JobRestoreNextParam_Finished); ok { +func (x *JobRestoreDispatchParam) GetFinished() *JobRestoreFinishedParam { + if x, ok := x.GetParam().(*JobRestoreDispatchParam_Finished); ok { return x.Finished } return nil } -type isJobRestoreNextParam_Param interface { - isJobRestoreNextParam_Param() +type isJobRestoreDispatchParam_Param interface { + isJobRestoreDispatchParam_Param() } -type JobRestoreNextParam_WaitForTape struct { +type JobRestoreDispatchParam_WaitForTape struct { WaitForTape *JobRestoreWaitForTapeParam `protobuf:"bytes,1,opt,name=wait_for_tape,json=waitForTape,proto3,oneof"` } -type JobRestoreNextParam_Copying struct { +type JobRestoreDispatchParam_Copying struct { Copying *JobRestoreCopyingParam `protobuf:"bytes,2,opt,name=copying,proto3,oneof"` } -type JobRestoreNextParam_Finished struct { +type JobRestoreDispatchParam_Finished struct { Finished *JobRestoreFinishedParam `protobuf:"bytes,255,opt,name=finished,proto3,oneof"` } -func (*JobRestoreNextParam_WaitForTape) isJobRestoreNextParam_Param() {} +func (*JobRestoreDispatchParam_WaitForTape) isJobRestoreDispatchParam_Param() {} -func (*JobRestoreNextParam_Copying) isJobRestoreNextParam_Param() {} +func (*JobRestoreDispatchParam_Copying) isJobRestoreDispatchParam_Param() {} -func (*JobRestoreNextParam_Finished) isJobRestoreNextParam_Param() {} +func (*JobRestoreDispatchParam_Finished) isJobRestoreDispatchParam_Param() {} type JobRestoreWaitForTapeParam struct { state protoimpl.MessageState @@ -661,84 +661,84 @@ var file_job_restore_proto_rawDesc = []byte{ 0x6f, 0x74, 0x6f, 0x22, 0x2c, 0x0a, 0x0f, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, - 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x13, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x4e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x4d, 0x0a, 0x0d, 0x77, 0x61, 0x69, - 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x74, 0x61, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x27, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, + 0x73, 0x22, 0xf7, 0x01, 0x0a, 0x17, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x4d, 0x0a, + 0x0d, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x74, 0x61, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x57, 0x61, 0x69, + 0x74, 0x46, 0x6f, 0x72, 0x54, 0x61, 0x70, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, + 0x0b, 0x77, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x54, 0x61, 0x70, 0x65, 0x12, 0x3f, 0x0a, 0x07, + 0x63, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, + 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x52, + 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x12, 0x43, 0x0a, + 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0xff, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, + 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, + 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, + 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x1c, 0x0a, 0x1a, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, - 0x54, 0x61, 0x70, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, 0x52, 0x0b, 0x77, 0x61, 0x69, - 0x74, 0x46, 0x6f, 0x72, 0x54, 0x61, 0x70, 0x65, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x70, 0x79, - 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, - 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x48, 0x00, - 0x52, 0x07, 0x63, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x12, 0x43, 0x0a, 0x08, 0x66, 0x69, 0x6e, - 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0xff, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6a, - 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x48, 0x00, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x42, 0x07, - 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x1c, 0x0a, 0x1a, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x54, 0x61, 0x70, 0x65, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x30, 0x0a, 0x16, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x12, - 0x16, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x22, 0xf7, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, - 0x6c, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, - 0x61, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x61, - 0x70, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x12, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, - 0x73, 0x68, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1b, - 0x0a, 0x09, 0x74, 0x61, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x21, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x74, 0x61, 0x70, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x22, 0xa1, 0x01, 0x0a, - 0x0b, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x61, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, - 0x74, 0x61, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, - 0x61, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x72, 0x63, 0x6f, 0x64, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x72, 0x63, 0x6f, 0x64, 0x65, 0x12, - 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x17, 0x2e, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x43, 0x6f, - 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x2e, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x52, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x22, 0x72, 0x0a, 0x0f, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x74, 0x65, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1b, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, - 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x04, - 0x73, 0x74, 0x65, 0x70, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x61, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x54, 0x61, 0x70, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x30, 0x0a, 0x16, 0x4a, 0x6f, 0x62, + 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x69, 0x6e, 0x67, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x4a, + 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, + 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x22, 0xf7, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, + 0x17, 0x0a, 0x07, 0x74, 0x61, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x06, 0x74, 0x61, 0x70, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x70, 0x79, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, + 0x7a, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x61, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x21, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x61, 0x70, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, + 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x22, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, + 0x22, 0xa1, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x61, 0x70, 0x65, + 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x06, 0x74, 0x61, 0x70, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x72, + 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x72, 0x63, + 0x6f, 0x64, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x11, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x2e, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x61, 0x70, 0x65, 0x52, 0x05, 0x74, - 0x61, 0x70, 0x65, 0x73, 0x22, 0xdf, 0x01, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, - 0x70, 0x69, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, - 0x0c, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x69, 0x6c, - 0x65, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x48, 0x00, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, - 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x08, 0x0a, 0x06, - 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x2a, 0x4c, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, - 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x46, 0x4f, - 0x52, 0x5f, 0x54, 0x41, 0x50, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x50, 0x59, - 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, - 0x44, 0x10, 0xff, 0x01, 0x42, 0x23, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x73, 0x61, 0x6d, 0x75, 0x65, 0x6c, 0x6e, 0x63, 0x75, 0x69, 0x2f, 0x79, 0x61, - 0x74, 0x6d, 0x2f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x65, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x22, 0x72, 0x0a, 0x0f, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x74, 0x65, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, + 0x65, 0x70, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x61, 0x70, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x61, 0x70, + 0x65, 0x52, 0x05, 0x74, 0x61, 0x70, 0x65, 0x73, 0x22, 0xdf, 0x01, 0x0a, 0x11, 0x4a, 0x6f, 0x62, + 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x21, + 0x0a, 0x0c, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, 0x88, 0x01, + 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x2a, 0x4c, 0x0a, 0x0e, 0x4a, 0x6f, + 0x62, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x0b, 0x0a, 0x07, + 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x41, 0x49, + 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x54, 0x41, 0x50, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, + 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x08, 0x46, 0x49, 0x4e, + 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0xff, 0x01, 0x42, 0x23, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x61, 0x6d, 0x75, 0x65, 0x6c, 0x6e, 0x63, 0x75, + 0x69, 0x2f, 0x79, 0x61, 0x74, 0x6d, 0x2f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -758,7 +758,7 @@ var file_job_restore_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_job_restore_proto_goTypes = []interface{}{ (JobRestoreStep)(0), // 0: job_restore.JobRestoreStep (*JobRestoreParam)(nil), // 1: job_restore.JobRestoreParam - (*JobRestoreNextParam)(nil), // 2: job_restore.JobRestoreNextParam + (*JobRestoreDispatchParam)(nil), // 2: job_restore.JobRestoreDispatchParam (*JobRestoreWaitForTapeParam)(nil), // 3: job_restore.JobRestoreWaitForTapeParam (*JobRestoreCopyingParam)(nil), // 4: job_restore.JobRestoreCopyingParam (*JobRestoreFinishedParam)(nil), // 5: job_restore.JobRestoreFinishedParam @@ -769,9 +769,9 @@ var file_job_restore_proto_goTypes = []interface{}{ (CopyStatus)(0), // 10: copy_status.CopyStatus } var file_job_restore_proto_depIdxs = []int32{ - 3, // 0: job_restore.JobRestoreNextParam.wait_for_tape:type_name -> job_restore.JobRestoreWaitForTapeParam - 4, // 1: job_restore.JobRestoreNextParam.copying:type_name -> job_restore.JobRestoreCopyingParam - 5, // 2: job_restore.JobRestoreNextParam.finished:type_name -> job_restore.JobRestoreFinishedParam + 3, // 0: job_restore.JobRestoreDispatchParam.wait_for_tape:type_name -> job_restore.JobRestoreWaitForTapeParam + 4, // 1: job_restore.JobRestoreDispatchParam.copying:type_name -> job_restore.JobRestoreCopyingParam + 5, // 2: job_restore.JobRestoreDispatchParam.finished:type_name -> job_restore.JobRestoreFinishedParam 10, // 3: job_restore.RestoreFile.status:type_name -> copy_status.CopyStatus 10, // 4: job_restore.RestoreTape.status:type_name -> copy_status.CopyStatus 6, // 5: job_restore.RestoreTape.files:type_name -> job_restore.RestoreFile @@ -804,7 +804,7 @@ func file_job_restore_proto_init() { } } file_job_restore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobRestoreNextParam); i { + switch v := v.(*JobRestoreDispatchParam); i { case 0: return &v.state case 1: @@ -901,9 +901,9 @@ func file_job_restore_proto_init() { } } file_job_restore_proto_msgTypes[1].OneofWrappers = []interface{}{ - (*JobRestoreNextParam_WaitForTape)(nil), - (*JobRestoreNextParam_Copying)(nil), - (*JobRestoreNextParam_Finished)(nil), + (*JobRestoreDispatchParam_WaitForTape)(nil), + (*JobRestoreDispatchParam_Copying)(nil), + (*JobRestoreDispatchParam_Finished)(nil), } file_job_restore_proto_msgTypes[8].OneofWrappers = []interface{}{} type x struct{} diff --git a/entity/job_restore.proto b/entity/job_restore.proto index 88489cd..b9c4935 100644 --- a/entity/job_restore.proto +++ b/entity/job_restore.proto @@ -16,7 +16,7 @@ message JobRestoreParam { repeated int64 file_ids = 1; } -message JobRestoreNextParam { +message JobRestoreDispatchParam { oneof param { JobRestoreWaitForTapeParam wait_for_tape = 1; JobRestoreCopyingParam copying = 2; diff --git a/entity/service.pb.go b/entity/service.pb.go index 7818e20..a59484b 100644 --- a/entity/service.pb.go +++ b/entity/service.pb.go @@ -1156,6 +1156,107 @@ func (x *JobCreateReply) GetJob() *Job { return nil } +type JobEditStateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Status *JobStatus `protobuf:"varint,2,opt,name=status,proto3,enum=job.JobStatus,oneof" json:"status,omitempty"` + State *JobState `protobuf:"bytes,3,opt,name=state,proto3,oneof" json:"state,omitempty"` +} + +func (x *JobEditStateRequest) Reset() { + *x = JobEditStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_service_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobEditStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobEditStateRequest) ProtoMessage() {} + +func (x *JobEditStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobEditStateRequest.ProtoReflect.Descriptor instead. +func (*JobEditStateRequest) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{22} +} + +func (x *JobEditStateRequest) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *JobEditStateRequest) GetStatus() JobStatus { + if x != nil && x.Status != nil { + return *x.Status + } + return JobStatus_DRAFT +} + +func (x *JobEditStateRequest) GetState() *JobState { + if x != nil { + return x.State + } + return nil +} + +type JobEditStateReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *JobEditStateReply) Reset() { + *x = JobEditStateReply{} + if protoimpl.UnsafeEnabled { + mi := &file_service_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JobEditStateReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JobEditStateReply) ProtoMessage() {} + +func (x *JobEditStateReply) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JobEditStateReply.ProtoReflect.Descriptor instead. +func (*JobEditStateReply) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{23} +} + type JobDeleteRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1167,7 +1268,7 @@ type JobDeleteRequest struct { func (x *JobDeleteRequest) Reset() { *x = JobDeleteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[22] + mi := &file_service_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1180,7 +1281,7 @@ func (x *JobDeleteRequest) String() string { func (*JobDeleteRequest) ProtoMessage() {} func (x *JobDeleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[22] + mi := &file_service_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1193,7 +1294,7 @@ func (x *JobDeleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use JobDeleteRequest.ProtoReflect.Descriptor instead. func (*JobDeleteRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{22} + return file_service_proto_rawDescGZIP(), []int{24} } func (x *JobDeleteRequest) GetIds() []int64 { @@ -1212,7 +1313,7 @@ type JobDeleteReply struct { func (x *JobDeleteReply) Reset() { *x = JobDeleteReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[23] + mi := &file_service_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1225,7 +1326,7 @@ func (x *JobDeleteReply) String() string { func (*JobDeleteReply) ProtoMessage() {} func (x *JobDeleteReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[23] + mi := &file_service_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1238,35 +1339,35 @@ func (x *JobDeleteReply) ProtoReflect() protoreflect.Message { // Deprecated: Use JobDeleteReply.ProtoReflect.Descriptor instead. func (*JobDeleteReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{23} + return file_service_proto_rawDescGZIP(), []int{25} } -type JobNextRequest struct { +type JobDispatchRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Param *JobNextParam `protobuf:"bytes,2,opt,name=param,proto3" json:"param,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Param *JobDispatchParam `protobuf:"bytes,2,opt,name=param,proto3" json:"param,omitempty"` } -func (x *JobNextRequest) Reset() { - *x = JobNextRequest{} +func (x *JobDispatchRequest) Reset() { + *x = JobDispatchRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[24] + mi := &file_service_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *JobNextRequest) String() string { +func (x *JobDispatchRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobNextRequest) ProtoMessage() {} +func (*JobDispatchRequest) ProtoMessage() {} -func (x *JobNextRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[24] +func (x *JobDispatchRequest) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1277,50 +1378,48 @@ func (x *JobNextRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use JobNextRequest.ProtoReflect.Descriptor instead. -func (*JobNextRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{24} +// Deprecated: Use JobDispatchRequest.ProtoReflect.Descriptor instead. +func (*JobDispatchRequest) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{26} } -func (x *JobNextRequest) GetId() int64 { +func (x *JobDispatchRequest) GetId() int64 { if x != nil { return x.Id } return 0 } -func (x *JobNextRequest) GetParam() *JobNextParam { +func (x *JobDispatchRequest) GetParam() *JobDispatchParam { if x != nil { return x.Param } return nil } -type JobNextReply struct { +type JobDispatchReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - Job *Job `protobuf:"bytes,1,opt,name=job,proto3" json:"job,omitempty"` } -func (x *JobNextReply) Reset() { - *x = JobNextReply{} +func (x *JobDispatchReply) Reset() { + *x = JobDispatchReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[25] + mi := &file_service_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *JobNextReply) String() string { +func (x *JobDispatchReply) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobNextReply) ProtoMessage() {} +func (*JobDispatchReply) ProtoMessage() {} -func (x *JobNextReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[25] +func (x *JobDispatchReply) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1331,16 +1430,9 @@ func (x *JobNextReply) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use JobNextReply.ProtoReflect.Descriptor instead. -func (*JobNextReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{25} -} - -func (x *JobNextReply) GetJob() *Job { - if x != nil { - return x.Job - } - return nil +// Deprecated: Use JobDispatchReply.ProtoReflect.Descriptor instead. +func (*JobDispatchReply) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{27} } type JobDisplayRequest struct { @@ -1354,7 +1446,7 @@ type JobDisplayRequest struct { func (x *JobDisplayRequest) Reset() { *x = JobDisplayRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[26] + mi := &file_service_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1367,7 +1459,7 @@ func (x *JobDisplayRequest) String() string { func (*JobDisplayRequest) ProtoMessage() {} func (x *JobDisplayRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[26] + mi := &file_service_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1380,7 +1472,7 @@ func (x *JobDisplayRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use JobDisplayRequest.ProtoReflect.Descriptor instead. func (*JobDisplayRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{26} + return file_service_proto_rawDescGZIP(), []int{28} } func (x *JobDisplayRequest) GetId() int64 { @@ -1401,7 +1493,7 @@ type JobDisplayReply struct { func (x *JobDisplayReply) Reset() { *x = JobDisplayReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[27] + mi := &file_service_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1414,7 +1506,7 @@ func (x *JobDisplayReply) String() string { func (*JobDisplayReply) ProtoMessage() {} func (x *JobDisplayReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[27] + mi := &file_service_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1427,7 +1519,7 @@ func (x *JobDisplayReply) ProtoReflect() protoreflect.Message { // Deprecated: Use JobDisplayReply.ProtoReflect.Descriptor instead. func (*JobDisplayReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{27} + return file_service_proto_rawDescGZIP(), []int{29} } func (x *JobDisplayReply) GetDisplay() *JobDisplay { @@ -1449,7 +1541,7 @@ type JobGetLogRequest struct { func (x *JobGetLogRequest) Reset() { *x = JobGetLogRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[28] + mi := &file_service_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1462,7 +1554,7 @@ func (x *JobGetLogRequest) String() string { func (*JobGetLogRequest) ProtoMessage() {} func (x *JobGetLogRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[28] + mi := &file_service_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1475,7 +1567,7 @@ func (x *JobGetLogRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use JobGetLogRequest.ProtoReflect.Descriptor instead. func (*JobGetLogRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{28} + return file_service_proto_rawDescGZIP(), []int{30} } func (x *JobGetLogRequest) GetJobId() int64 { @@ -1504,7 +1596,7 @@ type JobGetLogReply struct { func (x *JobGetLogReply) Reset() { *x = JobGetLogReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[29] + mi := &file_service_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1517,7 +1609,7 @@ func (x *JobGetLogReply) String() string { func (*JobGetLogReply) ProtoMessage() {} func (x *JobGetLogReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[29] + mi := &file_service_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1530,7 +1622,7 @@ func (x *JobGetLogReply) ProtoReflect() protoreflect.Message { // Deprecated: Use JobGetLogReply.ProtoReflect.Descriptor instead. func (*JobGetLogReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{29} + return file_service_proto_rawDescGZIP(), []int{31} } func (x *JobGetLogReply) GetLogs() []byte { @@ -1558,7 +1650,7 @@ type SourceListRequest struct { func (x *SourceListRequest) Reset() { *x = SourceListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[30] + mi := &file_service_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1571,7 +1663,7 @@ func (x *SourceListRequest) String() string { func (*SourceListRequest) ProtoMessage() {} func (x *SourceListRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[30] + mi := &file_service_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1584,7 +1676,7 @@ func (x *SourceListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SourceListRequest.ProtoReflect.Descriptor instead. func (*SourceListRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{30} + return file_service_proto_rawDescGZIP(), []int{32} } func (x *SourceListRequest) GetPath() string { @@ -1607,7 +1699,7 @@ type SourceListReply struct { func (x *SourceListReply) Reset() { *x = SourceListReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[31] + mi := &file_service_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1620,7 +1712,7 @@ func (x *SourceListReply) String() string { func (*SourceListReply) ProtoMessage() {} func (x *SourceListReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[31] + mi := &file_service_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1633,7 +1725,7 @@ func (x *SourceListReply) ProtoReflect() protoreflect.Message { // Deprecated: Use SourceListReply.ProtoReflect.Descriptor instead. func (*SourceListReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{31} + return file_service_proto_rawDescGZIP(), []int{33} } func (x *SourceListReply) GetFile() *SourceFile { @@ -1666,7 +1758,7 @@ type DeviceListRequest struct { func (x *DeviceListRequest) Reset() { *x = DeviceListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[32] + mi := &file_service_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1679,7 +1771,7 @@ func (x *DeviceListRequest) String() string { func (*DeviceListRequest) ProtoMessage() {} func (x *DeviceListRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[32] + mi := &file_service_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1692,7 +1784,7 @@ func (x *DeviceListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceListRequest.ProtoReflect.Descriptor instead. func (*DeviceListRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{32} + return file_service_proto_rawDescGZIP(), []int{34} } type DeviceListReply struct { @@ -1706,7 +1798,7 @@ type DeviceListReply struct { func (x *DeviceListReply) Reset() { *x = DeviceListReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[33] + mi := &file_service_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1719,7 +1811,7 @@ func (x *DeviceListReply) String() string { func (*DeviceListReply) ProtoMessage() {} func (x *DeviceListReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[33] + mi := &file_service_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1732,7 +1824,7 @@ func (x *DeviceListReply) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceListReply.ProtoReflect.Descriptor instead. func (*DeviceListReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{33} + return file_service_proto_rawDescGZIP(), []int{35} } func (x *DeviceListReply) GetDevices() []string { @@ -1753,7 +1845,7 @@ type LibraryExportRequest struct { func (x *LibraryExportRequest) Reset() { *x = LibraryExportRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[34] + mi := &file_service_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1766,7 +1858,7 @@ func (x *LibraryExportRequest) String() string { func (*LibraryExportRequest) ProtoMessage() {} func (x *LibraryExportRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[34] + mi := &file_service_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1779,7 +1871,7 @@ func (x *LibraryExportRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LibraryExportRequest.ProtoReflect.Descriptor instead. func (*LibraryExportRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{34} + return file_service_proto_rawDescGZIP(), []int{36} } func (x *LibraryExportRequest) GetTypes() []LibraryEntityType { @@ -1800,7 +1892,7 @@ type LibraryExportReply struct { func (x *LibraryExportReply) Reset() { *x = LibraryExportReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[35] + mi := &file_service_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1813,7 +1905,7 @@ func (x *LibraryExportReply) String() string { func (*LibraryExportReply) ProtoMessage() {} func (x *LibraryExportReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[35] + mi := &file_service_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1826,7 +1918,7 @@ func (x *LibraryExportReply) ProtoReflect() protoreflect.Message { // Deprecated: Use LibraryExportReply.ProtoReflect.Descriptor instead. func (*LibraryExportReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{35} + return file_service_proto_rawDescGZIP(), []int{37} } func (x *LibraryExportReply) GetJson() []byte { @@ -1848,7 +1940,7 @@ type LibraryTrimRequest struct { func (x *LibraryTrimRequest) Reset() { *x = LibraryTrimRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[36] + mi := &file_service_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1861,7 +1953,7 @@ func (x *LibraryTrimRequest) String() string { func (*LibraryTrimRequest) ProtoMessage() {} func (x *LibraryTrimRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[36] + mi := &file_service_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1874,7 +1966,7 @@ func (x *LibraryTrimRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LibraryTrimRequest.ProtoReflect.Descriptor instead. func (*LibraryTrimRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{36} + return file_service_proto_rawDescGZIP(), []int{38} } func (x *LibraryTrimRequest) GetTrimPosition() bool { @@ -1900,7 +1992,7 @@ type LibraryTrimReply struct { func (x *LibraryTrimReply) Reset() { *x = LibraryTrimReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[37] + mi := &file_service_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1913,7 +2005,7 @@ func (x *LibraryTrimReply) String() string { func (*LibraryTrimReply) ProtoMessage() {} func (x *LibraryTrimReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[37] + mi := &file_service_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1926,7 +2018,7 @@ func (x *LibraryTrimReply) ProtoReflect() protoreflect.Message { // Deprecated: Use LibraryTrimReply.ProtoReflect.Descriptor instead. func (*LibraryTrimReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{37} + return file_service_proto_rawDescGZIP(), []int{39} } var File_service_proto protoreflect.FileDescriptor @@ -2025,145 +2117,160 @@ var file_service_proto_rawDesc = []byte{ 0x62, 0x22, 0x2c, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, - 0x24, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, - 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x49, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x4e, 0x65, - 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x61, 0x72, - 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, - 0x6f, 0x62, 0x4e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x52, 0x05, 0x70, 0x61, 0x72, - 0x61, 0x6d, 0x22, 0x2a, 0x0a, 0x0c, 0x4a, 0x6f, 0x62, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x08, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0x23, - 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x3c, 0x0a, 0x0f, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, - 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x29, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, - 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, - 0x79, 0x22, 0x51, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x06, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x06, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x22, 0x3c, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, - 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x22, 0x27, 0x0a, 0x11, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x93, 0x01, 0x0a, 0x0f, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, - 0x26, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, - 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x11, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, - 0x6e, 0x22, 0x13, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2b, 0x0a, 0x0f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x22, 0x54, 0x0a, 0x14, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, - 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x05, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x62, - 0x72, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x28, 0x0a, 0x12, 0x4c, 0x69, 0x62, - 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, - 0x12, 0x0a, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6a, - 0x73, 0x6f, 0x6e, 0x22, 0x56, 0x0a, 0x12, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, - 0x69, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x69, - 0x6d, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0c, 0x74, 0x72, 0x69, 0x6d, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, - 0x0a, 0x09, 0x74, 0x72, 0x69, 0x6d, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x74, 0x72, 0x69, 0x6d, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4c, - 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, - 0xef, 0x09, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x46, - 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x12, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x15, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, - 0x45, 0x64, 0x69, 0x74, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, - 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, - 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65, - 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4d, - 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x46, - 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, - 0x00, 0x12, 0x53, 0x0a, 0x0f, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, - 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, - 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, - 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x10, - 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x20, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x47, - 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, - 0x65, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x12, - 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, + 0x91, 0x01, 0x0a, 0x13, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, + 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x48, 0x01, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x88, 0x01, 0x01, 0x42, 0x09, + 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x22, 0x13, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x24, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x10, + 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x51, 0x0a, 0x12, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x44, + 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x52, 0x05, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x22, 0x12, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x23, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x44, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3c, 0x0a, 0x0f, + 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x29, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x52, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x22, 0x51, 0x0a, 0x10, 0x4a, 0x6f, + 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, + 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, + 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x88, + 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3c, 0x0a, + 0x0e, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x12, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6c, + 0x6f, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x27, 0x0a, 0x11, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x22, 0x93, 0x01, 0x0a, 0x0f, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, + 0x12, 0x28, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, + 0x69, 0x6c, 0x65, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x68, + 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x13, 0x0a, 0x11, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x2b, 0x0a, 0x0f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x54, 0x0a, 0x14, + 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, + 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x22, 0x28, 0x0a, 0x12, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, + 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6a, 0x73, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x22, 0x56, 0x0a, 0x12, + 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x69, 0x6d, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x74, 0x72, 0x69, 0x6d, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x72, 0x69, 0x6d, 0x5f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x72, 0x69, 0x6d, + 0x46, 0x69, 0x6c, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, + 0x72, 0x69, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xc7, 0x0a, 0x0a, 0x07, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x12, + 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, - 0x00, 0x12, 0x41, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x19, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x4e, 0x65, - 0x78, 0x74, 0x12, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, - 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, - 0x61, 0x79, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, - 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, - 0x6c, 0x61, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x4a, 0x6f, - 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, 0x12, 0x18, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x12, 0x41, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x12, 0x19, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, + 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, + 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0f, 0x46, 0x69, + 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, + 0x3e, 0x0a, 0x08, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, + 0x44, 0x0a, 0x0a, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x10, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, + 0x07, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x4a, 0x6f, + 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, - 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, - 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1a, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, - 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0d, 0x4c, 0x69, 0x62, - 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1d, 0x2e, 0x73, 0x65, 0x72, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4a, 0x0a, + 0x0c, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x4a, 0x6f, 0x62, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0b, + 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, + 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, + 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x4a, + 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, + 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, + 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1a, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0d, 0x4c, 0x69, + 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1d, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, + 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, - 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, - 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0b, 0x4c, 0x69, 0x62, 0x72, - 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, - 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, - 0x00, 0x42, 0x23, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x73, 0x61, 0x6d, 0x75, 0x65, 0x6c, 0x6e, 0x63, 0x75, 0x69, 0x2f, 0x79, 0x61, 0x74, 0x6d, 0x2f, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0b, 0x4c, 0x69, 0x62, + 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x00, 0x42, 0x23, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x73, 0x61, 0x6d, 0x75, 0x65, 0x6c, 0x6e, 0x63, 0x75, 0x69, 0x2f, 0x79, 0x61, 0x74, 0x6d, + 0x2f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2178,7 +2285,7 @@ func file_service_proto_rawDescGZIP() []byte { return file_service_proto_rawDescData } -var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 38) +var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 40) var file_service_proto_goTypes = []interface{}{ (*FileGetRequest)(nil), // 0: service.FileGetRequest (*FileGetReply)(nil), // 1: service.FileGetReply @@ -2202,102 +2309,109 @@ var file_service_proto_goTypes = []interface{}{ (*JobListReply)(nil), // 19: service.JobListReply (*JobCreateRequest)(nil), // 20: service.JobCreateRequest (*JobCreateReply)(nil), // 21: service.JobCreateReply - (*JobDeleteRequest)(nil), // 22: service.JobDeleteRequest - (*JobDeleteReply)(nil), // 23: service.JobDeleteReply - (*JobNextRequest)(nil), // 24: service.JobNextRequest - (*JobNextReply)(nil), // 25: service.JobNextReply - (*JobDisplayRequest)(nil), // 26: service.JobDisplayRequest - (*JobDisplayReply)(nil), // 27: service.JobDisplayReply - (*JobGetLogRequest)(nil), // 28: service.JobGetLogRequest - (*JobGetLogReply)(nil), // 29: service.JobGetLogReply - (*SourceListRequest)(nil), // 30: service.SourceListRequest - (*SourceListReply)(nil), // 31: service.SourceListReply - (*DeviceListRequest)(nil), // 32: service.DeviceListRequest - (*DeviceListReply)(nil), // 33: service.DeviceListReply - (*LibraryExportRequest)(nil), // 34: service.LibraryExportRequest - (*LibraryExportReply)(nil), // 35: service.LibraryExportReply - (*LibraryTrimRequest)(nil), // 36: service.LibraryTrimRequest - (*LibraryTrimReply)(nil), // 37: service.LibraryTrimReply - (*File)(nil), // 38: file.File - (*Position)(nil), // 39: position.Position - (*EditedFile)(nil), // 40: file.EditedFile - (*TapeFilter)(nil), // 41: tape.TapeFilter - (*Tape)(nil), // 42: tape.Tape - (*JobFilter)(nil), // 43: job.JobFilter - (*JobRecentlyUpdateFilter)(nil), // 44: job.JobRecentlyUpdateFilter - (*Job)(nil), // 45: job.Job - (*CreatableJob)(nil), // 46: job.CreatableJob - (*JobNextParam)(nil), // 47: job.JobNextParam - (*JobDisplay)(nil), // 48: job.JobDisplay - (*SourceFile)(nil), // 49: source.SourceFile - (LibraryEntityType)(0), // 50: library_entity_type.LibraryEntityType + (*JobEditStateRequest)(nil), // 22: service.JobEditStateRequest + (*JobEditStateReply)(nil), // 23: service.JobEditStateReply + (*JobDeleteRequest)(nil), // 24: service.JobDeleteRequest + (*JobDeleteReply)(nil), // 25: service.JobDeleteReply + (*JobDispatchRequest)(nil), // 26: service.JobDispatchRequest + (*JobDispatchReply)(nil), // 27: service.JobDispatchReply + (*JobDisplayRequest)(nil), // 28: service.JobDisplayRequest + (*JobDisplayReply)(nil), // 29: service.JobDisplayReply + (*JobGetLogRequest)(nil), // 30: service.JobGetLogRequest + (*JobGetLogReply)(nil), // 31: service.JobGetLogReply + (*SourceListRequest)(nil), // 32: service.SourceListRequest + (*SourceListReply)(nil), // 33: service.SourceListReply + (*DeviceListRequest)(nil), // 34: service.DeviceListRequest + (*DeviceListReply)(nil), // 35: service.DeviceListReply + (*LibraryExportRequest)(nil), // 36: service.LibraryExportRequest + (*LibraryExportReply)(nil), // 37: service.LibraryExportReply + (*LibraryTrimRequest)(nil), // 38: service.LibraryTrimRequest + (*LibraryTrimReply)(nil), // 39: service.LibraryTrimReply + (*File)(nil), // 40: file.File + (*Position)(nil), // 41: position.Position + (*EditedFile)(nil), // 42: file.EditedFile + (*TapeFilter)(nil), // 43: tape.TapeFilter + (*Tape)(nil), // 44: tape.Tape + (*JobFilter)(nil), // 45: job.JobFilter + (*JobRecentlyUpdateFilter)(nil), // 46: job.JobRecentlyUpdateFilter + (*Job)(nil), // 47: job.Job + (*CreatableJob)(nil), // 48: job.CreatableJob + (JobStatus)(0), // 49: job.JobStatus + (*JobState)(nil), // 50: job.JobState + (*JobDispatchParam)(nil), // 51: job.JobDispatchParam + (*JobDisplay)(nil), // 52: job.JobDisplay + (*SourceFile)(nil), // 53: source.SourceFile + (LibraryEntityType)(0), // 54: library_entity_type.LibraryEntityType } var file_service_proto_depIdxs = []int32{ - 38, // 0: service.FileGetReply.file:type_name -> file.File - 39, // 1: service.FileGetReply.positions:type_name -> position.Position - 38, // 2: service.FileGetReply.children:type_name -> file.File - 40, // 3: service.FileEditRequest.file:type_name -> file.EditedFile - 38, // 4: service.FileEditReply.file:type_name -> file.File - 38, // 5: service.FileMkdirReply.file:type_name -> file.File - 38, // 6: service.FileListParentsReply.parents:type_name -> file.File + 40, // 0: service.FileGetReply.file:type_name -> file.File + 41, // 1: service.FileGetReply.positions:type_name -> position.Position + 40, // 2: service.FileGetReply.children:type_name -> file.File + 42, // 3: service.FileEditRequest.file:type_name -> file.EditedFile + 40, // 4: service.FileEditReply.file:type_name -> file.File + 40, // 5: service.FileMkdirReply.file:type_name -> file.File + 40, // 6: service.FileListParentsReply.parents:type_name -> file.File 11, // 7: service.TapeListRequest.mget:type_name -> service.TapeMGetRequest - 41, // 8: service.TapeListRequest.list:type_name -> tape.TapeFilter - 42, // 9: service.TapeListReply.tapes:type_name -> tape.Tape - 39, // 10: service.TapeGetPositionsReply.positions:type_name -> position.Position + 43, // 8: service.TapeListRequest.list:type_name -> tape.TapeFilter + 44, // 9: service.TapeListReply.tapes:type_name -> tape.Tape + 41, // 10: service.TapeGetPositionsReply.positions:type_name -> position.Position 18, // 11: service.JobListRequest.mget:type_name -> service.JobMGetRequest - 43, // 12: service.JobListRequest.list:type_name -> job.JobFilter - 44, // 13: service.JobListRequest.recently_update:type_name -> job.JobRecentlyUpdateFilter - 45, // 14: service.JobListReply.jobs:type_name -> job.Job - 46, // 15: service.JobCreateRequest.job:type_name -> job.CreatableJob - 45, // 16: service.JobCreateReply.job:type_name -> job.Job - 47, // 17: service.JobNextRequest.param:type_name -> job.JobNextParam - 45, // 18: service.JobNextReply.job:type_name -> job.Job - 48, // 19: service.JobDisplayReply.display:type_name -> job.JobDisplay - 49, // 20: service.SourceListReply.file:type_name -> source.SourceFile - 49, // 21: service.SourceListReply.chain:type_name -> source.SourceFile - 49, // 22: service.SourceListReply.children:type_name -> source.SourceFile - 50, // 23: service.LibraryExportRequest.types:type_name -> library_entity_type.LibraryEntityType - 0, // 24: service.Service.FileGet:input_type -> service.FileGetRequest - 2, // 25: service.Service.FileEdit:input_type -> service.FileEditRequest - 4, // 26: service.Service.FileMkdir:input_type -> service.FileMkdirRequest - 6, // 27: service.Service.FileDelete:input_type -> service.FileDeleteRequest - 8, // 28: service.Service.FileListParents:input_type -> service.FileListParentsRequest - 10, // 29: service.Service.TapeList:input_type -> service.TapeListRequest - 13, // 30: service.Service.TapeDelete:input_type -> service.TapeDeleteRequest - 15, // 31: service.Service.TapeGetPositions:input_type -> service.TapeGetPositionsRequest - 17, // 32: service.Service.JobList:input_type -> service.JobListRequest - 20, // 33: service.Service.JobCreate:input_type -> service.JobCreateRequest - 22, // 34: service.Service.JobDelete:input_type -> service.JobDeleteRequest - 24, // 35: service.Service.JobNext:input_type -> service.JobNextRequest - 26, // 36: service.Service.JobDisplay:input_type -> service.JobDisplayRequest - 28, // 37: service.Service.JobGetLog:input_type -> service.JobGetLogRequest - 30, // 38: service.Service.SourceList:input_type -> service.SourceListRequest - 32, // 39: service.Service.DeviceList:input_type -> service.DeviceListRequest - 34, // 40: service.Service.LibraryExport:input_type -> service.LibraryExportRequest - 36, // 41: service.Service.LibraryTrim:input_type -> service.LibraryTrimRequest - 1, // 42: service.Service.FileGet:output_type -> service.FileGetReply - 3, // 43: service.Service.FileEdit:output_type -> service.FileEditReply - 5, // 44: service.Service.FileMkdir:output_type -> service.FileMkdirReply - 7, // 45: service.Service.FileDelete:output_type -> service.FileDeleteReply - 9, // 46: service.Service.FileListParents:output_type -> service.FileListParentsReply - 12, // 47: service.Service.TapeList:output_type -> service.TapeListReply - 14, // 48: service.Service.TapeDelete:output_type -> service.TapeDeleteReply - 16, // 49: service.Service.TapeGetPositions:output_type -> service.TapeGetPositionsReply - 19, // 50: service.Service.JobList:output_type -> service.JobListReply - 21, // 51: service.Service.JobCreate:output_type -> service.JobCreateReply - 23, // 52: service.Service.JobDelete:output_type -> service.JobDeleteReply - 25, // 53: service.Service.JobNext:output_type -> service.JobNextReply - 27, // 54: service.Service.JobDisplay:output_type -> service.JobDisplayReply - 29, // 55: service.Service.JobGetLog:output_type -> service.JobGetLogReply - 31, // 56: service.Service.SourceList:output_type -> service.SourceListReply - 33, // 57: service.Service.DeviceList:output_type -> service.DeviceListReply - 35, // 58: service.Service.LibraryExport:output_type -> service.LibraryExportReply - 37, // 59: service.Service.LibraryTrim:output_type -> service.LibraryTrimReply - 42, // [42:60] is the sub-list for method output_type - 24, // [24:42] is the sub-list for method input_type - 24, // [24:24] is the sub-list for extension type_name - 24, // [24:24] is the sub-list for extension extendee - 0, // [0:24] is the sub-list for field type_name + 45, // 12: service.JobListRequest.list:type_name -> job.JobFilter + 46, // 13: service.JobListRequest.recently_update:type_name -> job.JobRecentlyUpdateFilter + 47, // 14: service.JobListReply.jobs:type_name -> job.Job + 48, // 15: service.JobCreateRequest.job:type_name -> job.CreatableJob + 47, // 16: service.JobCreateReply.job:type_name -> job.Job + 49, // 17: service.JobEditStateRequest.status:type_name -> job.JobStatus + 50, // 18: service.JobEditStateRequest.state:type_name -> job.JobState + 51, // 19: service.JobDispatchRequest.param:type_name -> job.JobDispatchParam + 52, // 20: service.JobDisplayReply.display:type_name -> job.JobDisplay + 53, // 21: service.SourceListReply.file:type_name -> source.SourceFile + 53, // 22: service.SourceListReply.chain:type_name -> source.SourceFile + 53, // 23: service.SourceListReply.children:type_name -> source.SourceFile + 54, // 24: service.LibraryExportRequest.types:type_name -> library_entity_type.LibraryEntityType + 0, // 25: service.Service.FileGet:input_type -> service.FileGetRequest + 2, // 26: service.Service.FileEdit:input_type -> service.FileEditRequest + 4, // 27: service.Service.FileMkdir:input_type -> service.FileMkdirRequest + 6, // 28: service.Service.FileDelete:input_type -> service.FileDeleteRequest + 8, // 29: service.Service.FileListParents:input_type -> service.FileListParentsRequest + 10, // 30: service.Service.TapeList:input_type -> service.TapeListRequest + 13, // 31: service.Service.TapeDelete:input_type -> service.TapeDeleteRequest + 15, // 32: service.Service.TapeGetPositions:input_type -> service.TapeGetPositionsRequest + 17, // 33: service.Service.JobList:input_type -> service.JobListRequest + 20, // 34: service.Service.JobCreate:input_type -> service.JobCreateRequest + 22, // 35: service.Service.JobEditState:input_type -> service.JobEditStateRequest + 24, // 36: service.Service.JobDelete:input_type -> service.JobDeleteRequest + 26, // 37: service.Service.JobDispatch:input_type -> service.JobDispatchRequest + 28, // 38: service.Service.JobDisplay:input_type -> service.JobDisplayRequest + 30, // 39: service.Service.JobGetLog:input_type -> service.JobGetLogRequest + 32, // 40: service.Service.SourceList:input_type -> service.SourceListRequest + 34, // 41: service.Service.DeviceList:input_type -> service.DeviceListRequest + 36, // 42: service.Service.LibraryExport:input_type -> service.LibraryExportRequest + 38, // 43: service.Service.LibraryTrim:input_type -> service.LibraryTrimRequest + 1, // 44: service.Service.FileGet:output_type -> service.FileGetReply + 3, // 45: service.Service.FileEdit:output_type -> service.FileEditReply + 5, // 46: service.Service.FileMkdir:output_type -> service.FileMkdirReply + 7, // 47: service.Service.FileDelete:output_type -> service.FileDeleteReply + 9, // 48: service.Service.FileListParents:output_type -> service.FileListParentsReply + 12, // 49: service.Service.TapeList:output_type -> service.TapeListReply + 14, // 50: service.Service.TapeDelete:output_type -> service.TapeDeleteReply + 16, // 51: service.Service.TapeGetPositions:output_type -> service.TapeGetPositionsReply + 19, // 52: service.Service.JobList:output_type -> service.JobListReply + 21, // 53: service.Service.JobCreate:output_type -> service.JobCreateReply + 23, // 54: service.Service.JobEditState:output_type -> service.JobEditStateReply + 25, // 55: service.Service.JobDelete:output_type -> service.JobDeleteReply + 27, // 56: service.Service.JobDispatch:output_type -> service.JobDispatchReply + 29, // 57: service.Service.JobDisplay:output_type -> service.JobDisplayReply + 31, // 58: service.Service.JobGetLog:output_type -> service.JobGetLogReply + 33, // 59: service.Service.SourceList:output_type -> service.SourceListReply + 35, // 60: service.Service.DeviceList:output_type -> service.DeviceListReply + 37, // 61: service.Service.LibraryExport:output_type -> service.LibraryExportReply + 39, // 62: service.Service.LibraryTrim:output_type -> service.LibraryTrimReply + 44, // [44:63] is the sub-list for method output_type + 25, // [25:44] is the sub-list for method input_type + 25, // [25:25] is the sub-list for extension type_name + 25, // [25:25] is the sub-list for extension extendee + 0, // [0:25] is the sub-list for field type_name } func init() { file_service_proto_init() } @@ -2577,7 +2691,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobDeleteRequest); i { + switch v := v.(*JobEditStateRequest); i { case 0: return &v.state case 1: @@ -2589,7 +2703,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobDeleteReply); i { + switch v := v.(*JobEditStateReply); i { case 0: return &v.state case 1: @@ -2601,7 +2715,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobNextRequest); i { + switch v := v.(*JobDeleteRequest); i { case 0: return &v.state case 1: @@ -2613,7 +2727,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobNextReply); i { + switch v := v.(*JobDeleteReply); i { case 0: return &v.state case 1: @@ -2625,7 +2739,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobDisplayRequest); i { + switch v := v.(*JobDispatchRequest); i { case 0: return &v.state case 1: @@ -2637,7 +2751,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobDisplayReply); i { + switch v := v.(*JobDispatchReply); i { case 0: return &v.state case 1: @@ -2649,7 +2763,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobGetLogRequest); i { + switch v := v.(*JobDisplayRequest); i { case 0: return &v.state case 1: @@ -2661,7 +2775,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobGetLogReply); i { + switch v := v.(*JobDisplayReply); i { case 0: return &v.state case 1: @@ -2673,7 +2787,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SourceListRequest); i { + switch v := v.(*JobGetLogRequest); i { case 0: return &v.state case 1: @@ -2685,7 +2799,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SourceListReply); i { + switch v := v.(*JobGetLogReply); i { case 0: return &v.state case 1: @@ -2697,7 +2811,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceListRequest); i { + switch v := v.(*SourceListRequest); i { case 0: return &v.state case 1: @@ -2709,7 +2823,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceListReply); i { + switch v := v.(*SourceListReply); i { case 0: return &v.state case 1: @@ -2721,7 +2835,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LibraryExportRequest); i { + switch v := v.(*DeviceListRequest); i { case 0: return &v.state case 1: @@ -2733,7 +2847,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LibraryExportReply); i { + switch v := v.(*DeviceListReply); i { case 0: return &v.state case 1: @@ -2745,7 +2859,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LibraryTrimRequest); i { + switch v := v.(*LibraryExportRequest); i { case 0: return &v.state case 1: @@ -2757,6 +2871,30 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LibraryExportReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_service_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LibraryTrimRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_service_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LibraryTrimReply); i { case 0: return &v.state @@ -2779,14 +2917,15 @@ func file_service_proto_init() { (*JobListRequest_List)(nil), (*JobListRequest_RecentlyUpdate)(nil), } - file_service_proto_msgTypes[28].OneofWrappers = []interface{}{} + file_service_proto_msgTypes[22].OneofWrappers = []interface{}{} + file_service_proto_msgTypes[30].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_service_proto_rawDesc, NumEnums: 0, - NumMessages: 38, + NumMessages: 40, NumExtensions: 0, NumServices: 1, }, diff --git a/entity/service.proto b/entity/service.proto index 0f5b6d4..932d0cb 100644 --- a/entity/service.proto +++ b/entity/service.proto @@ -22,8 +22,9 @@ service Service { rpc JobList(JobListRequest) returns (JobListReply) {} rpc JobCreate(JobCreateRequest) returns (JobCreateReply) {} + rpc JobEditState(JobEditStateRequest) returns (JobEditStateReply) {} rpc JobDelete(JobDeleteRequest) returns (JobDeleteReply) {} - rpc JobNext(JobNextRequest) returns (JobNextReply) {} + rpc JobDispatch(JobDispatchRequest) returns (JobDispatchReply) {} rpc JobDisplay(JobDisplayRequest) returns (JobDisplayReply) {} rpc JobGetLog(JobGetLogRequest) returns (JobGetLogReply) {} @@ -133,6 +134,15 @@ message JobCreateReply { job.Job job = 1; } +message JobEditStateRequest { + int64 id = 1; + optional job.JobStatus status = 2; + optional job.JobState state = 3; +} + +message JobEditStateReply { +} + message JobDeleteRequest { repeated int64 ids = 1; } @@ -140,13 +150,12 @@ message JobDeleteRequest { message JobDeleteReply { } -message JobNextRequest { +message JobDispatchRequest { int64 id = 1; - job.JobNextParam param = 2; + job.JobDispatchParam param = 2; } -message JobNextReply { - job.Job job = 1; +message JobDispatchReply { } message JobDisplayRequest { diff --git a/entity/service_grpc.pb.go b/entity/service_grpc.pb.go index 682ffee..09dba25 100644 --- a/entity/service_grpc.pb.go +++ b/entity/service_grpc.pb.go @@ -32,8 +32,9 @@ type ServiceClient interface { TapeGetPositions(ctx context.Context, in *TapeGetPositionsRequest, opts ...grpc.CallOption) (*TapeGetPositionsReply, error) JobList(ctx context.Context, in *JobListRequest, opts ...grpc.CallOption) (*JobListReply, error) JobCreate(ctx context.Context, in *JobCreateRequest, opts ...grpc.CallOption) (*JobCreateReply, error) + JobEditState(ctx context.Context, in *JobEditStateRequest, opts ...grpc.CallOption) (*JobEditStateReply, error) JobDelete(ctx context.Context, in *JobDeleteRequest, opts ...grpc.CallOption) (*JobDeleteReply, error) - JobNext(ctx context.Context, in *JobNextRequest, opts ...grpc.CallOption) (*JobNextReply, error) + JobDispatch(ctx context.Context, in *JobDispatchRequest, opts ...grpc.CallOption) (*JobDispatchReply, error) JobDisplay(ctx context.Context, in *JobDisplayRequest, opts ...grpc.CallOption) (*JobDisplayReply, error) JobGetLog(ctx context.Context, in *JobGetLogRequest, opts ...grpc.CallOption) (*JobGetLogReply, error) SourceList(ctx context.Context, in *SourceListRequest, opts ...grpc.CallOption) (*SourceListReply, error) @@ -140,6 +141,15 @@ func (c *serviceClient) JobCreate(ctx context.Context, in *JobCreateRequest, opt return out, nil } +func (c *serviceClient) JobEditState(ctx context.Context, in *JobEditStateRequest, opts ...grpc.CallOption) (*JobEditStateReply, error) { + out := new(JobEditStateReply) + err := c.cc.Invoke(ctx, "/service.Service/JobEditState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *serviceClient) JobDelete(ctx context.Context, in *JobDeleteRequest, opts ...grpc.CallOption) (*JobDeleteReply, error) { out := new(JobDeleteReply) err := c.cc.Invoke(ctx, "/service.Service/JobDelete", in, out, opts...) @@ -149,9 +159,9 @@ func (c *serviceClient) JobDelete(ctx context.Context, in *JobDeleteRequest, opt return out, nil } -func (c *serviceClient) JobNext(ctx context.Context, in *JobNextRequest, opts ...grpc.CallOption) (*JobNextReply, error) { - out := new(JobNextReply) - err := c.cc.Invoke(ctx, "/service.Service/JobNext", in, out, opts...) +func (c *serviceClient) JobDispatch(ctx context.Context, in *JobDispatchRequest, opts ...grpc.CallOption) (*JobDispatchReply, error) { + out := new(JobDispatchReply) + err := c.cc.Invoke(ctx, "/service.Service/JobDispatch", in, out, opts...) if err != nil { return nil, err } @@ -226,8 +236,9 @@ type ServiceServer interface { TapeGetPositions(context.Context, *TapeGetPositionsRequest) (*TapeGetPositionsReply, error) JobList(context.Context, *JobListRequest) (*JobListReply, error) JobCreate(context.Context, *JobCreateRequest) (*JobCreateReply, error) + JobEditState(context.Context, *JobEditStateRequest) (*JobEditStateReply, error) JobDelete(context.Context, *JobDeleteRequest) (*JobDeleteReply, error) - JobNext(context.Context, *JobNextRequest) (*JobNextReply, error) + JobDispatch(context.Context, *JobDispatchRequest) (*JobDispatchReply, error) JobDisplay(context.Context, *JobDisplayRequest) (*JobDisplayReply, error) JobGetLog(context.Context, *JobGetLogRequest) (*JobGetLogReply, error) SourceList(context.Context, *SourceListRequest) (*SourceListReply, error) @@ -271,11 +282,14 @@ func (UnimplementedServiceServer) JobList(context.Context, *JobListRequest) (*Jo func (UnimplementedServiceServer) JobCreate(context.Context, *JobCreateRequest) (*JobCreateReply, error) { return nil, status.Errorf(codes.Unimplemented, "method JobCreate not implemented") } +func (UnimplementedServiceServer) JobEditState(context.Context, *JobEditStateRequest) (*JobEditStateReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method JobEditState not implemented") +} func (UnimplementedServiceServer) JobDelete(context.Context, *JobDeleteRequest) (*JobDeleteReply, error) { return nil, status.Errorf(codes.Unimplemented, "method JobDelete not implemented") } -func (UnimplementedServiceServer) JobNext(context.Context, *JobNextRequest) (*JobNextReply, error) { - return nil, status.Errorf(codes.Unimplemented, "method JobNext not implemented") +func (UnimplementedServiceServer) JobDispatch(context.Context, *JobDispatchRequest) (*JobDispatchReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method JobDispatch not implemented") } func (UnimplementedServiceServer) JobDisplay(context.Context, *JobDisplayRequest) (*JobDisplayReply, error) { return nil, status.Errorf(codes.Unimplemented, "method JobDisplay not implemented") @@ -488,6 +502,24 @@ func _Service_JobCreate_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Service_JobEditState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(JobEditStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).JobEditState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/service.Service/JobEditState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).JobEditState(ctx, req.(*JobEditStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Service_JobDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(JobDeleteRequest) if err := dec(in); err != nil { @@ -506,20 +538,20 @@ func _Service_JobDelete_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } -func _Service_JobNext_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(JobNextRequest) +func _Service_JobDispatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(JobDispatchRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ServiceServer).JobNext(ctx, in) + return srv.(ServiceServer).JobDispatch(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/service.Service/JobNext", + FullMethod: "/service.Service/JobDispatch", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ServiceServer).JobNext(ctx, req.(*JobNextRequest)) + return srv.(ServiceServer).JobDispatch(ctx, req.(*JobDispatchRequest)) } return interceptor(ctx, in, info, handler) } @@ -679,13 +711,17 @@ var Service_ServiceDesc = grpc.ServiceDesc{ MethodName: "JobCreate", Handler: _Service_JobCreate_Handler, }, + { + MethodName: "JobEditState", + Handler: _Service_JobEditState_Handler, + }, { MethodName: "JobDelete", Handler: _Service_JobDelete_Handler, }, { - MethodName: "JobNext", - Handler: _Service_JobNext_Handler, + MethodName: "JobDispatch", + Handler: _Service_JobDispatch_Handler, }, { MethodName: "JobDisplay", diff --git a/executor/device.go b/executor/device.go new file mode 100644 index 0000000..6dfc266 --- /dev/null +++ b/executor/device.go @@ -0,0 +1,33 @@ +package executor + +import "sort" + +func (e *Executor) ListAvailableDevices() []string { + e.devicesLock.Lock() + defer e.devicesLock.Unlock() + + devices := e.availableDevices.ToSlice() + sort.Slice(devices, func(i, j int) bool { + return devices[i] < devices[j] + }) + + return devices +} + +func (e *Executor) OccupyDevice(dev string) bool { + e.devicesLock.Lock() + defer e.devicesLock.Unlock() + + if !e.availableDevices.Contains(dev) { + return false + } + + e.availableDevices.Remove(dev) + return true +} + +func (e *Executor) ReleaseDevice(dev string) { + e.devicesLock.Lock() + defer e.devicesLock.Unlock() + e.availableDevices.Add(dev) +} diff --git a/executor/executor.go b/executor/executor.go index eeca0bb..899524e 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -3,12 +3,13 @@ package executor import ( "context" "fmt" - "sort" "sync" mapset "github.com/deckarep/golang-set/v2" + "github.com/modern-go/reflect2" "github.com/samuelncui/yatm/entity" "github.com/samuelncui/yatm/library" + "github.com/samuelncui/yatm/tools" "gorm.io/gorm" ) @@ -16,13 +17,14 @@ type Executor struct { db *gorm.DB lib *library.Library - devices []string - devicesLock sync.Mutex + devices []string availableDevices mapset.Set[string] paths Paths scripts Scripts + + jobExecutors *tools.CacheOnce[int64, JobExecutor] } type Paths struct { @@ -43,7 +45,7 @@ func New( db *gorm.DB, lib *library.Library, devices []string, paths Paths, scripts Scripts, ) *Executor { - return &Executor{ + e := &Executor{ db: db, lib: lib, devices: devices, @@ -51,89 +53,76 @@ func New( paths: paths, scripts: scripts, } + e.jobExecutors = tools.NewCacheOnce(e.newJobExecutor) + + return e } func (e *Executor) AutoMigrate() error { return e.db.AutoMigrate(ModelJob) } -func (e *Executor) ListAvailableDevices() []string { - e.devicesLock.Lock() - defer e.devicesLock.Unlock() - - devices := e.availableDevices.ToSlice() - sort.Slice(devices, func(i, j int) bool { - return devices[i] < devices[j] - }) - - return devices -} +func (e *Executor) CreateJob(ctx context.Context, job *Job, param *entity.JobParam) (*Job, error) { + job, err := e.SaveJob(ctx, job) + if err != nil { + return nil, fmt.Errorf("save job fail, err= %w", err) + } -func (e *Executor) occupyDevice(dev string) bool { - e.devicesLock.Lock() - defer e.devicesLock.Unlock() + typ, found := jobTypes[jobParamToTypes[reflect2.RTypeOf(param.GetParam())]] + if !found || typ == nil { + return nil, fmt.Errorf("job type unexpected, state_type= %T", param.GetParam()) + } - if !e.availableDevices.Contains(dev) { - return false + executor, err := typ.GetExecutor(ctx, e, job) + if err != nil { + return nil, fmt.Errorf("get job executor fail, job_id= %d, %w", job.ID, err) + } + if err := executor.Initialize(ctx, param); err != nil { + executor.Logger().WithContext(ctx).WithError(err).Errorf("initialize failed, param= %s", param) + return nil, fmt.Errorf("executor initialize fail, job_id= %d param= %s, %w", job.ID, param, err) + } + if err := executor.Close(ctx); err != nil { + executor.Logger().WithContext(ctx).WithError(err).Errorf("close executor failed, param= %s", param) + return nil, fmt.Errorf("close executor failed, job_id= %d param= %s, %w", job.ID, param, err) } - e.availableDevices.Remove(dev) - return true + return job, nil } -func (e *Executor) releaseDevice(dev string) { - e.devicesLock.Lock() - defer e.devicesLock.Unlock() - e.availableDevices.Add(dev) +func (e *Executor) GetJobExecutor(ctx context.Context, id int64) (JobExecutor, error) { + return e.jobExecutors.Get(ctx, id) } -func (e *Executor) Start(ctx context.Context, job *Job) error { - job.Status = entity.JobStatus_PROCESSING - if _, err := e.SaveJob(ctx, job); err != nil { - return err - } +func (e *Executor) RemoveJobExecutor(ctx context.Context, id int64) { + e.jobExecutors.Remove(id) +} - if state := job.State.GetArchive(); state != nil { - if err := e.startArchive(ctx, job); err != nil { - return err - } - return nil +func (e *Executor) newJobExecutor(ctx context.Context, id int64) (JobExecutor, error) { + job, err := e.GetJob(ctx, id) + if err != nil { + return nil, fmt.Errorf("get job fail, id= %d, %w", id, err) } - if state := job.State.GetRestore(); state != nil { - if err := e.startRestore(ctx, job); err != nil { - return err - } - return nil + + factory, has := jobTypes[reflect2.RTypeOf(job.State.GetState())] + if !has { + return nil, fmt.Errorf("job type unexpected, state_type= %T", job.State.GetState()) } - return fmt.Errorf("unexpected state type, %T", job.State.State) + return factory.GetExecutor(ctx, e, job) } -func (e *Executor) Submit(ctx context.Context, job *Job, param *entity.JobNextParam) error { - if job.Status != entity.JobStatus_PROCESSING { - return fmt.Errorf("target job is not on processing, status= %s", job.Status) +func (e *Executor) Dispatch(ctx context.Context, jobID int64, param *entity.JobDispatchParam) error { + executor, err := e.GetJobExecutor(ctx, jobID) + if err != nil { + return fmt.Errorf("get job executor fail, job_id= %d, %w", jobID, err) } - if state := job.State.GetArchive(); state != nil { - exe, err := e.newArchiveExecutor(ctx, job) - if err != nil { - return err - } - - exe.submit(ctx, param.GetArchive()) - return nil - } - if state := job.State.GetRestore(); state != nil { - exe, err := e.newRestoreExecutor(ctx, job) - if err != nil { - return err - } - - exe.submit(ctx, param.GetRestore()) - return nil + if err := executor.Dispatch(ctx, param); err != nil { + executor.Logger().WithContext(ctx).WithError(err).Errorf("dispatch request fail, req= %s", param) + return fmt.Errorf("dispatch request fail, job_id= %d, req= %s, %w", jobID, param, err) } - return fmt.Errorf("unexpected state type, %T", job.State.State) + return nil } func (e *Executor) Display(ctx context.Context, job *Job) (*entity.JobDisplay, error) { @@ -141,22 +130,16 @@ func (e *Executor) Display(ctx context.Context, job *Job) (*entity.JobDisplay, e return nil, fmt.Errorf("target job is not on processing, status= %s", job.Status) } - if state := job.State.GetArchive(); state != nil { - display, err := e.getArchiveDisplay(ctx, job) - if err != nil { - return nil, err - } - - return &entity.JobDisplay{Display: &entity.JobDisplay_Archive{Archive: display}}, nil + executor, err := e.GetJobExecutor(ctx, job.ID) + if err != nil { + return nil, fmt.Errorf("get job executor fail, job_id= %d, %w", job.ID, err) } - if state := job.State.GetRestore(); state != nil { - display, err := e.getRestoreDisplay(ctx, job) - if err != nil { - return nil, err - } - return &entity.JobDisplay{Display: &entity.JobDisplay_Restore{Restore: display}}, nil + result, err := executor.Display(ctx) + if err != nil { + executor.Logger().WithContext(ctx).WithError(err).Errorf("get display failed") + return nil, err } - return nil, fmt.Errorf("unexpected state type, %T", job.State.State) + return result, nil } diff --git a/executor/job.go b/executor/job.go index 86ffa0f..e614ffc 100644 --- a/executor/job.go +++ b/executor/job.go @@ -33,28 +33,6 @@ func (j *Job) BeforeUpdate(tx *gorm.DB) error { return nil } -func (e *Executor) initJob(ctx context.Context, job *Job, param *entity.JobParam) error { - if p := param.GetArchive(); p != nil { - return e.createArchive(ctx, job, p) - } - if p := param.GetRestore(); p != nil { - return e.createRestore(ctx, job, p) - } - return fmt.Errorf("unexpected param type, %T", param.Param) -} - -func (e *Executor) CreateJob(ctx context.Context, job *Job, param *entity.JobParam) (*Job, error) { - if err := e.initJob(ctx, job, param); err != nil { - return nil, err - } - - if r := e.db.WithContext(ctx).Create(job); r.Error != nil { - return nil, fmt.Errorf("save job fail, err= %w", r.Error) - } - - return job, nil -} - func (e *Executor) DeleteJobs(ctx context.Context, ids ...int64) error { jobs, err := e.MGetJob(ctx, ids...) if err != nil { diff --git a/executor/job_archive.go b/executor/job_archive.go new file mode 100644 index 0000000..bd3cab7 --- /dev/null +++ b/executor/job_archive.go @@ -0,0 +1,3 @@ +package executor + +type jobTypeArchive struct{} diff --git a/executor/job_archive_display.go b/executor/job_archive_display.go deleted file mode 100644 index ebe336f..0000000 --- a/executor/job_archive_display.go +++ /dev/null @@ -1,25 +0,0 @@ -package executor - -import ( - "context" - "sync/atomic" - - "github.com/samuelncui/yatm/entity" -) - -func (e *Executor) getArchiveDisplay(ctx context.Context, job *Job) (*entity.JobArchiveDisplay, error) { - display := new(entity.JobArchiveDisplay) - - if exe := e.getArchiveExecutor(ctx, job); exe != nil && exe.progress != nil { - display.CopiedBytes = atomic.LoadInt64(&exe.progress.bytes) - display.CopiedFiles = atomic.LoadInt64(&exe.progress.files) - display.TotalBytes = atomic.LoadInt64(&exe.progress.totalBytes) - display.TotalFiles = atomic.LoadInt64(&exe.progress.totalFiles) - display.StartTime = exe.progress.startTime.Unix() - - speed := atomic.LoadInt64(&exe.progress.speed) - display.Speed = &speed - } - - return display, nil -} diff --git a/executor/job_archive_exe.go b/executor/job_archive_exe.go index c83b898..119effc 100644 --- a/executor/job_archive_exe.go +++ b/executor/job_archive_exe.go @@ -2,44 +2,30 @@ package executor import ( "context" - "encoding/hex" - "errors" "fmt" "io" "os" - "os/exec" - "path" - "sort" "sync" "sync/atomic" - "time" mapset "github.com/deckarep/golang-set/v2" - "github.com/samber/lo" - "github.com/samuelncui/acp" "github.com/samuelncui/yatm/entity" - "github.com/samuelncui/yatm/library" "github.com/samuelncui/yatm/tools" "github.com/sirupsen/logrus" ) -var ( - runningArchives sync.Map -) +type jobArchiveExecutor struct { + lock sync.Mutex + exe *Executor + job *Job -func (e *Executor) getArchiveExecutor(ctx context.Context, job *Job) *jobArchiveExecutor { - if running, has := runningArchives.Load(job.ID); has { - return running.(*jobArchiveExecutor) - } - return nil + progress *progress + logFile *os.File + logger *logrus.Logger } -func (e *Executor) newArchiveExecutor(ctx context.Context, job *Job) (*jobArchiveExecutor, error) { - if exe := e.getArchiveExecutor(ctx, job); exe != nil { - return exe, nil - } - - logFile, err := e.newLogWriter(job.ID) +func (*jobTypeArchive) GetExecutor(ctx context.Context, exe *Executor, job *Job) (JobExecutor, error) { + logFile, err := exe.newLogWriter(job.ID) if err != nil { return nil, fmt.Errorf("get log writer fail, %w", err) } @@ -47,39 +33,27 @@ func (e *Executor) newArchiveExecutor(ctx context.Context, job *Job) (*jobArchiv logger := logrus.New() logger.SetOutput(io.MultiWriter(os.Stderr, logFile)) - exe := &jobArchiveExecutor{ - exe: e, + e := &jobArchiveExecutor{ + exe: exe, job: job, - state: job.State.GetArchive(), - logFile: logFile, logger: logger, } - runningArchives.Store(job.ID, exe) - return exe, nil + return e, nil } -type jobArchiveExecutor struct { - exe *Executor - job *Job - - stateLock sync.Mutex - state *entity.JobArchiveState - - progress *progress - logFile *os.File - logger *logrus.Logger -} - -func (a *jobArchiveExecutor) submit(ctx context.Context, param *entity.JobArchiveNextParam) { - if err := a.handle(ctx, param); err != nil { - a.logger.WithContext(ctx).WithError(err).Infof("handler param fail, param= %s", param) +func (a *jobArchiveExecutor) Dispatch(ctx context.Context, next *entity.JobDispatchParam) error { + param := next.GetArchive() + if param == nil { + return fmt.Errorf("unexpected next param type, unexpected= JobArchiveDispatchParam, has= %s", next) } + + return a.dispatch(ctx, param) } -func (a *jobArchiveExecutor) handle(ctx context.Context, param *entity.JobArchiveNextParam) error { +func (a *jobArchiveExecutor) dispatch(ctx context.Context, param *entity.JobArchiveDispatchParam) error { if p := param.GetCopying(); p != nil { if err := a.switchStep( ctx, entity.JobArchiveStep_COPYING, entity.JobStatus_PROCESSING, @@ -91,6 +65,7 @@ func (a *jobArchiveExecutor) handle(ctx context.Context, param *entity.JobArchiv tools.Working() go tools.WrapWithLogger(ctx, a.logger, func() { defer tools.Done() + if err := a.makeTape(tools.ShutdownContext, p.Device, p.Barcode, p.Name); err != nil { a.logger.WithContext(ctx).WithError(err).Errorf("make tape has error, barcode= '%s' name= '%s'", p.Barcode, p.Name) } @@ -101,7 +76,7 @@ func (a *jobArchiveExecutor) handle(ctx context.Context, param *entity.JobArchiv if p := param.GetWaitForTape(); p != nil { return a.switchStep( - ctx, entity.JobArchiveStep_WAIT_FOR_TAPE, entity.JobStatus_PROCESSING, + ctx, entity.JobArchiveStep_WAIT_FOR_TAPE, entity.JobStatus_PENDING, mapset.NewThreadUnsafeSet(entity.JobArchiveStep_PENDING, entity.JobArchiveStep_COPYING), ) } @@ -114,322 +89,80 @@ func (a *jobArchiveExecutor) handle(ctx context.Context, param *entity.JobArchiv return err } - a.logFile.Close() - runningArchives.Delete(a.job.ID) - return nil + return a.Close(ctx) } return nil } -func (a *jobArchiveExecutor) makeTape(ctx context.Context, device, barcode, name string) (rerr error) { - if !a.exe.occupyDevice(device) { - return fmt.Errorf("device is using, device= %s", device) - } - defer a.exe.releaseDevice(device) - defer a.makeTapeFinished(tools.WithoutTimeout(ctx)) - - encryption, keyPath, keyRecycle, err := a.exe.newKey() - if err != nil { - return err - } - defer keyRecycle() - - if err := runCmd(a.logger, a.exe.makeEncryptCmd(ctx, device, keyPath, barcode, name)); err != nil { - return fmt.Errorf("run encrypt script fail, %w", err) - } - - mkfsCmd := exec.CommandContext(ctx, a.exe.scripts.Mkfs) - mkfsCmd.Env = append(mkfsCmd.Env, fmt.Sprintf("DEVICE=%s", device), fmt.Sprintf("TAPE_BARCODE=%s", barcode), fmt.Sprintf("TAPE_NAME=%s", name)) - if err := runCmd(a.logger, mkfsCmd); err != nil { - return fmt.Errorf("run mkfs script fail, %w", err) - } - - mountPoint, err := os.MkdirTemp("", "*.ltfs") - if err != nil { - return fmt.Errorf("create temp mountpoint, %w", err) - } - - mountCmd := exec.CommandContext(ctx, a.exe.scripts.Mount) - mountCmd.Env = append(mountCmd.Env, fmt.Sprintf("DEVICE=%s", device), fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) - if err := runCmd(a.logger, mountCmd); err != nil { - return fmt.Errorf("run mount script fail, %w", err) +func (a *jobArchiveExecutor) Display(ctx context.Context) (*entity.JobDisplay, error) { + p := a.progress + if p == nil { + return nil, nil } - defer func() { - umountCmd := exec.CommandContext(tools.WithoutTimeout(ctx), a.exe.scripts.Umount) - umountCmd.Env = append(umountCmd.Env, fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) - if err := runCmd(a.logger, umountCmd); err != nil { - a.logger.WithContext(ctx).WithError(err).Errorf("run umount script fail, %s", mountPoint) - return - } - if err := os.Remove(mountPoint); err != nil { - a.logger.WithContext(ctx).WithError(err).Errorf("remove mount point fail, %s", mountPoint) - return - } - }() - - wildcardJobOpts := make([]acp.WildcardJobOption, 0, 6) - wildcardJobOpts = append(wildcardJobOpts, acp.Target(mountPoint)) - for _, source := range a.state.Sources { - if source.Status == entity.CopyStatus_SUBMITED { - continue - } - wildcardJobOpts = append(wildcardJobOpts, acp.AccurateSource(source.Source.Base, source.Source.Path)) - } - - opts := make([]acp.Option, 0, 4) - opts = append(opts, acp.WildcardJob(wildcardJobOpts...)) - opts = append(opts, acp.WithHash(true)) - opts = append(opts, acp.SetToDevice(acp.LinearDevice(true))) - opts = append(opts, acp.WithLogger(a.logger)) - - reportHander, reportGetter := acp.NewReportGetter() - opts = append(opts, acp.WithEventHandler(reportHander)) - - a.progress = newProgress() - defer func() { a.progress = nil }() - - var dropToReadonly bool - opts = append(opts, acp.WithEventHandler(func(ev acp.Event) { - switch e := ev.(type) { - case *acp.EventUpdateCount: - atomic.StoreInt64(&a.progress.totalBytes, e.Bytes) - atomic.StoreInt64(&a.progress.totalFiles, e.Files) - return - case *acp.EventUpdateProgress: - a.progress.setBytes(e.Bytes) - atomic.StoreInt64(&a.progress.files, e.Files) - return - case *acp.EventReportError: - a.logger.WithContext(ctx).Errorf("acp report error, src= '%s' dst= '%s' err= '%s'", e.Error.Src, e.Error.Dst, e.Error.Err) - return - case *acp.EventUpdateJob: - job := e.Job - src := entity.NewSourceFromACPJob(job) - - var targetStatus entity.CopyStatus - switch job.Status { - case acp.JobStatusPending, acp.JobStatusPreparing: - targetStatus = entity.CopyStatus_PENDING - case acp.JobStatusCopying: - targetStatus = entity.CopyStatus_RUNNING - case acp.JobStatusFinished: - targetStatus = entity.CopyStatus_FAILED - if len(job.SuccessTargets) > 0 { - a.logger.WithContext(ctx).Infof("file '%s' copy success, size= %d", src.RealPath(), job.Size) - targetStatus = entity.CopyStatus_STAGED - break // break from switch - } - - for dst, err := range job.FailTargets { - if err == nil { - continue - } - if errors.Is(err, acp.ErrTargetNoSpace) { - continue - } - - a.logger.WithContext(ctx).WithError(err).Errorf("file '%s' copy fail, dst= '%s'", src.RealPath(), dst) - if errors.Is(err, acp.ErrTargetDropToReadonly) { - dropToReadonly = true - } - } - default: - return - } - - a.stateLock.Lock() - defer a.stateLock.Unlock() - - idx := sort.Search(len(a.state.Sources), func(idx int) bool { - return src.Compare(a.state.Sources[idx].Source) <= 0 - }) - if idx < 0 || idx >= len(a.state.Sources) || src.Compare(a.state.Sources[idx].Source) != 0 { - a.logger.Warnf( - "cannot found target file, real_path= %s found_index= %d tape_file_path= %v", src.RealPath(), idx, - lo.Map(a.state.Sources, func(source *entity.SourceState, _ int) string { return source.Source.RealPath() })) - return - } - - target := a.state.Sources[idx] - if target == nil || !src.Equal(target.Source) { - return - } - target.Status = targetStatus - - if _, err := a.exe.SaveJob(ctx, a.job); err != nil { - logrus.WithContext(ctx).Infof("save job for update file fail, name= %s", job.Base+path.Join(job.Path...)) - } - return - } - })) - - defer func() { - ctx := tools.WithoutTimeout(ctx) - - // if tape drop to readonly, ltfs cannot write index to partition a. - // rollback sources for next try. - if dropToReadonly { - a.logger.WithContext(ctx).Errorf("tape filesystem had droped to readonly, rollback, barcode= '%s'", barcode) - a.rollbackSources(ctx) - return - } - - report := reportGetter() - sort.Slice(report.Jobs, func(i, j int) bool { - return entity.NewSourceFromACPJob(report.Jobs[i]).Compare(entity.NewSourceFromACPJob(report.Jobs[j])) < 0 - }) - reportFile, err := a.exe.newReportWriter(barcode) - if err != nil { - a.logger.WithContext(ctx).WithError(err).Warnf("open report file fail, barcode= '%s'", barcode) - } else { - defer reportFile.Close() - tools.WrapWithLogger(ctx, a.logger, func() { - reportFile.Write([]byte(report.ToJSONString(false))) - }) - } - - filteredJobs := make([]*acp.Job, 0, len(report.Jobs)) - files := make([]*library.TapeFile, 0, len(report.Jobs)) - for _, job := range report.Jobs { - if len(job.SuccessTargets) == 0 { - continue - } - if !job.Mode.IsRegular() { - continue - } + display := new(entity.JobArchiveDisplay) + display.CopiedBytes = atomic.LoadInt64(&p.bytes) + display.CopiedFiles = atomic.LoadInt64(&p.files) + display.TotalBytes = atomic.LoadInt64(&p.totalBytes) + display.TotalFiles = atomic.LoadInt64(&p.totalFiles) + display.StartTime = p.startTime.Unix() - hash, err := hex.DecodeString(job.SHA256) - if err != nil { - a.logger.WithContext(ctx).WithError(err).Warnf("decode sha256 fail, path= '%s'", entity.NewSourceFromACPJob(job).RealPath()) - continue - } + speed := atomic.LoadInt64(&p.speed) + display.Speed = &speed - files = append(files, &library.TapeFile{ - Path: path.Join(job.Path...), - Size: job.Size, - Mode: job.Mode, - ModTime: job.ModTime, - WriteTime: job.WriteTime, - Hash: hash, - }) - filteredJobs = append(filteredJobs, job) - } - - tape, err := a.exe.lib.CreateTape(ctx, &library.Tape{ - Barcode: barcode, - Name: name, - Encryption: encryption, - CreateTime: time.Now(), - }, files) - if err != nil { - rerr = tools.AppendError(rerr, fmt.Errorf("create tape fail, barcode= '%s' name= '%s', %w", barcode, name, err)) - return - } - a.logger.Infof("create tape success, tape_id= %d", tape.ID) - - if err := a.exe.lib.TrimFiles(ctx); err != nil { - a.logger.WithError(err).Warnf("trim library files fail") - } - - if err := a.markSourcesAsSubmited(ctx, filteredJobs); err != nil { - rerr = tools.AppendError(rerr, fmt.Errorf("mark source as submited fail, %w", err)) - return - } - }() - - copyer, err := acp.New(ctx, opts...) - if err != nil { - rerr = fmt.Errorf("start copy fail, %w", err) - return - } - - copyer.Wait() - return + return &entity.JobDisplay{Display: &entity.JobDisplay_Archive{Archive: display}}, nil } -func (a *jobArchiveExecutor) switchStep(ctx context.Context, target entity.JobArchiveStep, status entity.JobStatus, expect mapset.Set[entity.JobArchiveStep]) error { - a.stateLock.Lock() - defer a.stateLock.Unlock() - - if !expect.Contains(a.state.Step) { - return fmt.Errorf("unexpected current step, target= '%s' expect= '%s' has= '%s'", target, expect, a.state.Step) - } - - a.state.Step = target - a.job.Status = status - if _, err := a.exe.SaveJob(ctx, a.job); err != nil { - return fmt.Errorf("switch to step copying, save job fail, %w", err) - } - +func (a *jobArchiveExecutor) Close(ctx context.Context) error { + a.logFile.Close() + a.exe.RemoveJobExecutor(ctx, a.job.ID) return nil } -func (a *jobArchiveExecutor) markSourcesAsSubmited(ctx context.Context, jobs []*acp.Job) error { - a.stateLock.Lock() - defer a.stateLock.Unlock() - - searchableSource := a.state.Sources[:] - for _, job := range jobs { - src := entity.NewSourceFromACPJob(job) - for idx, testSrc := range searchableSource { - if src.Compare(testSrc.Source) <= 0 { - searchableSource = searchableSource[idx:] - break - } - } +func (a *jobArchiveExecutor) Logger() *logrus.Logger { + return a.logger +} - target := searchableSource[0] - if target == nil || !src.Equal(target.Source) { - continue - } +func (a *jobArchiveExecutor) getState() *entity.JobArchiveState { + a.lock.Lock() + defer a.lock.Unlock() - target.Status = entity.CopyStatus_SUBMITED + if a.job.State == nil || a.job.State.GetArchive() == nil { + a.job.State = &entity.JobState{State: &entity.JobState_Archive{Archive: &entity.JobArchiveState{}}} } - if _, err := a.exe.SaveJob(ctx, a.job); err != nil { - return fmt.Errorf("mark sources as submited, save job, %w", err) - } - return nil + return a.job.State.GetArchive() } -func (a *jobArchiveExecutor) rollbackSources(ctx context.Context) error { - a.stateLock.Lock() - defer a.stateLock.Unlock() +func (a *jobArchiveExecutor) updateJob(ctx context.Context, change func(*Job, *entity.JobArchiveState) error) error { + a.lock.Lock() + defer a.lock.Unlock() - for _, source := range a.state.Sources { - if source.Status == entity.CopyStatus_SUBMITED { - continue - } - source.Status = entity.CopyStatus_PENDING + if a.job.State == nil || a.job.State.GetArchive() == nil { + a.job.State = &entity.JobState{State: &entity.JobState_Archive{Archive: &entity.JobArchiveState{}}} } + if err := change(a.job, a.job.State.GetArchive()); err != nil { + a.logger.WithContext(ctx).WithError(err).Warnf("update state failed while exec change callback") + return fmt.Errorf("update state failed while exec change callback, %w", err) + } if _, err := a.exe.SaveJob(ctx, a.job); err != nil { - return fmt.Errorf("mark sources as submited, save job, %w", err) + a.logger.WithContext(ctx).WithError(err).Warnf("update state failed while save job") + return fmt.Errorf("update state failed while save job, %w", err) } + return nil } -func (a *jobArchiveExecutor) getTodoSources() int { - a.stateLock.Lock() - defer a.stateLock.Unlock() - - var todo int - for _, s := range a.state.Sources { - if s.Status == entity.CopyStatus_SUBMITED { - continue +func (a *jobArchiveExecutor) switchStep(ctx context.Context, target entity.JobArchiveStep, status entity.JobStatus, expect mapset.Set[entity.JobArchiveStep]) error { + return a.updateJob(ctx, func(job *Job, state *entity.JobArchiveState) error { + if !expect.Contains(state.Step) { + return fmt.Errorf("unexpected current step, target= '%s' expect= '%s' has= '%s'", target, expect, state.Step) } - todo++ - } - - return todo -} -func (a *jobArchiveExecutor) makeTapeFinished(ctx context.Context) { - if a.getTodoSources() > 0 { - a.submit(ctx, &entity.JobArchiveNextParam{Param: &entity.JobArchiveNextParam_WaitForTape{WaitForTape: &entity.JobArchiveWaitForTapeParam{}}}) - } else { - a.submit(ctx, &entity.JobArchiveNextParam{Param: &entity.JobArchiveNextParam_Finished{Finished: &entity.JobArchiveFinishedParam{}}}) - } + state.Step = target + job.Status = status + return nil + }) } diff --git a/executor/job_archive_init.go b/executor/job_archive_init.go new file mode 100644 index 0000000..f3af596 --- /dev/null +++ b/executor/job_archive_init.go @@ -0,0 +1,95 @@ +package executor + +import ( + "context" + "fmt" + "os" + "path" + "sort" + "strings" + + "github.com/samuelncui/acp" + "github.com/samuelncui/yatm/entity" +) + +func (a *jobArchiveExecutor) Initialize(ctx context.Context, param *entity.JobParam) error { + if err := a.applyParam(ctx, param.GetArchive()); err != nil { + return err + } + + return a.dispatch(ctx, &entity.JobArchiveDispatchParam{Param: &entity.JobArchiveDispatchParam_WaitForTape{ + WaitForTape: &entity.JobArchiveWaitForTapeParam{}, + }}) +} + +func (a *jobArchiveExecutor) applyParam(ctx context.Context, param *entity.JobArchiveParam) error { + if param == nil { + return fmt.Errorf("archive param is nil") + } + + return a.updateJob(ctx, func(_ *Job, state *entity.JobArchiveState) error { + var err error + sources := make([]*entity.SourceState, 0, len(param.Sources)*8) + for _, src := range param.Sources { + src.Base = strings.TrimSpace(src.Base) + if src.Base[0] != '/' { + src.Base = path.Join(a.exe.paths.Source, src.Base) + "/" + } + + sources, err = a.walk(ctx, src, sources) + if err != nil { + return err + } + } + sort.Slice(sources, func(i, j int) bool { + return sources[i].Source.Compare(sources[j].Source) < 0 + }) + + for idx, src := range sources { + if idx > 0 && sources[idx-1].Source.Equal(src.Source) { + return fmt.Errorf("have multi file with same path, path= %s", src.Source.RealPath()) + } + } + + state.Step = entity.JobArchiveStep_PENDING + state.Sources = sources + return nil + }) +} + +func (a *jobArchiveExecutor) walk(ctx context.Context, src *entity.Source, sources []*entity.SourceState) ([]*entity.SourceState, error) { + path := src.RealPath() + + stat, err := os.Stat(path) + if err != nil { + return nil, fmt.Errorf("walk get stat, path= '%s', %w", path, err) + } + + mode := stat.Mode() + if mode.IsRegular() { + if stat.Name() == ".DS_Store" { + return sources, nil + } + return append(sources, &entity.SourceState{ + Source: src, + Size: stat.Size(), + Status: entity.CopyStatus_PENDING, + }), nil + } + if mode&acp.UnexpectFileMode != 0 { + return sources, nil + } + + files, err := os.ReadDir(path) + if err != nil { + return nil, fmt.Errorf("walk read dir, path= '%s', %w", path, err) + } + for _, file := range files { + sources, err = a.walk(ctx, src.Append(file.Name()), sources) + if err != nil { + return nil, err + } + } + + return sources, nil +} diff --git a/executor/job_archive_param.go b/executor/job_archive_param.go deleted file mode 100644 index 2dc920b..0000000 --- a/executor/job_archive_param.go +++ /dev/null @@ -1,81 +0,0 @@ -package executor - -import ( - "context" - "fmt" - "os" - "path" - "sort" - "strings" - - "github.com/samuelncui/acp" - "github.com/samuelncui/yatm/entity" -) - -func (e *Executor) createArchive(ctx context.Context, job *Job, param *entity.JobArchiveParam) error { - var err error - sources := make([]*entity.SourceState, 0, len(param.Sources)*8) - for _, src := range param.Sources { - src.Base = strings.TrimSpace(src.Base) - if src.Base[0] != '/' { - src.Base = path.Join(e.paths.Source, src.Base) + "/" - } - - sources, err = walk(ctx, src, sources) - if err != nil { - return err - } - } - sort.Slice(sources, func(i, j int) bool { - return sources[i].Source.Compare(sources[j].Source) < 0 - }) - - for idx, src := range sources { - if idx > 0 && sources[idx-1].Source.Equal(src.Source) { - return fmt.Errorf("have multi file with same path, path= %s", src.Source.RealPath()) - } - } - - job.State = &entity.JobState{State: &entity.JobState_Archive{Archive: &entity.JobArchiveState{ - Step: entity.JobArchiveStep_PENDING, - Sources: sources, - }}} - return nil -} - -func walk(ctx context.Context, src *entity.Source, sources []*entity.SourceState) ([]*entity.SourceState, error) { - path := src.RealPath() - - stat, err := os.Stat(path) - if err != nil { - return nil, fmt.Errorf("walk get stat, path= '%s', %w", path, err) - } - - mode := stat.Mode() - if mode.IsRegular() { - if stat.Name() == ".DS_Store" { - return sources, nil - } - return append(sources, &entity.SourceState{ - Source: src, - Size: stat.Size(), - Status: entity.CopyStatus_PENDING, - }), nil - } - if mode&acp.UnexpectFileMode != 0 { - return sources, nil - } - - files, err := os.ReadDir(path) - if err != nil { - return nil, fmt.Errorf("walk read dir, path= '%s', %w", path, err) - } - for _, file := range files { - sources, err = walk(ctx, src.Append(file.Name()), sources) - if err != nil { - return nil, err - } - } - - return sources, nil -} diff --git a/executor/job_archive_start.go b/executor/job_archive_start.go deleted file mode 100644 index 7492d1d..0000000 --- a/executor/job_archive_start.go +++ /dev/null @@ -1,15 +0,0 @@ -package executor - -import ( - "context" - - "github.com/samuelncui/yatm/entity" -) - -func (e *Executor) startArchive(ctx context.Context, job *Job) error { - return e.Submit(ctx, job, &entity.JobNextParam{Param: &entity.JobNextParam_Archive{ - Archive: &entity.JobArchiveNextParam{Param: &entity.JobArchiveNextParam_WaitForTape{ - WaitForTape: &entity.JobArchiveWaitForTapeParam{}, - }}, - }}) -} diff --git a/executor/job_archive_tape.go b/executor/job_archive_tape.go new file mode 100644 index 0000000..905dce0 --- /dev/null +++ b/executor/job_archive_tape.go @@ -0,0 +1,313 @@ +package executor + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "os" + "os/exec" + "path" + "sort" + "strings" + "sync/atomic" + "time" + + "github.com/samber/lo" + "github.com/samuelncui/acp" + "github.com/samuelncui/yatm/entity" + "github.com/samuelncui/yatm/library" + "github.com/samuelncui/yatm/tools" +) + +func (a *jobArchiveExecutor) makeTape(ctx context.Context, device, barcode, name string) (rerr error) { + barcode = strings.ToUpper(barcode) + + state := a.getState() + if state == nil { + return fmt.Errorf("cannot found archive state, abort") + } + + if !a.exe.OccupyDevice(device) { + return fmt.Errorf("device is using, device= %s", device) + } + defer a.exe.ReleaseDevice(device) + + defer a.makeTapeFinished(tools.WithoutTimeout(ctx)) + encryption, keyPath, keyRecycle, err := a.exe.newKey() + if err != nil { + return err + } + defer keyRecycle() + + if err := runCmd(a.logger, a.exe.makeEncryptCmd(ctx, device, keyPath, barcode, name)); err != nil { + return fmt.Errorf("run encrypt script fail, %w", err) + } + + mkfsCmd := exec.CommandContext(ctx, a.exe.scripts.Mkfs) + mkfsCmd.Env = append(mkfsCmd.Env, fmt.Sprintf("DEVICE=%s", device), fmt.Sprintf("TAPE_BARCODE=%s", barcode), fmt.Sprintf("TAPE_NAME=%s", name)) + if err := runCmd(a.logger, mkfsCmd); err != nil { + return fmt.Errorf("run mkfs script fail, %w", err) + } + + mountPoint, err := os.MkdirTemp("", "*.ltfs") + if err != nil { + return fmt.Errorf("create temp mountpoint, %w", err) + } + + mountCmd := exec.CommandContext(ctx, a.exe.scripts.Mount) + mountCmd.Env = append(mountCmd.Env, fmt.Sprintf("DEVICE=%s", device), fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) + if err := runCmd(a.logger, mountCmd); err != nil { + return fmt.Errorf("run mount script fail, %w", err) + } + defer func() { + umountCmd := exec.CommandContext(tools.WithoutTimeout(ctx), a.exe.scripts.Umount) + umountCmd.Env = append(umountCmd.Env, fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) + if err := runCmd(a.logger, umountCmd); err != nil { + a.logger.WithContext(ctx).WithError(err).Errorf("run umount script fail, %s", mountPoint) + return + } + if err := os.Remove(mountPoint); err != nil { + a.logger.WithContext(ctx).WithError(err).Errorf("remove mount point fail, %s", mountPoint) + return + } + }() + + wildcardJobOpts := make([]acp.WildcardJobOption, 0, 6) + wildcardJobOpts = append(wildcardJobOpts, acp.Target(mountPoint)) + for _, source := range state.Sources { + if source.Status == entity.CopyStatus_SUBMITED { + continue + } + wildcardJobOpts = append(wildcardJobOpts, acp.AccurateSource(source.Source.Base, source.Source.Path)) + } + + opts := make([]acp.Option, 0, 4) + opts = append(opts, acp.WildcardJob(wildcardJobOpts...)) + opts = append(opts, acp.WithHash(true)) + opts = append(opts, acp.SetToDevice(acp.LinearDevice(true))) + opts = append(opts, acp.WithLogger(a.logger)) + + reportHander, reportGetter := acp.NewReportGetter() + opts = append(opts, acp.WithEventHandler(reportHander)) + + a.progress = newProgress() + defer func() { a.progress = nil }() + + var dropToReadonly bool + opts = append(opts, acp.WithEventHandler(func(ev acp.Event) { + switch e := ev.(type) { + case *acp.EventUpdateCount: + atomic.StoreInt64(&a.progress.totalBytes, e.Bytes) + atomic.StoreInt64(&a.progress.totalFiles, e.Files) + return + case *acp.EventUpdateProgress: + a.progress.setBytes(e.Bytes) + atomic.StoreInt64(&a.progress.files, e.Files) + return + case *acp.EventReportError: + a.logger.WithContext(ctx).Errorf("acp report error, src= '%s' dst= '%s' err= '%s'", e.Error.Src, e.Error.Dst, e.Error.Err) + return + case *acp.EventUpdateJob: + job := e.Job + src := entity.NewSourceFromACPJob(job) + + var targetStatus entity.CopyStatus + switch job.Status { + case acp.JobStatusPending, acp.JobStatusPreparing: + targetStatus = entity.CopyStatus_PENDING + case acp.JobStatusCopying: + targetStatus = entity.CopyStatus_RUNNING + case acp.JobStatusFinished: + targetStatus = entity.CopyStatus_FAILED + if len(job.SuccessTargets) > 0 { + a.logger.WithContext(ctx).Infof("file '%s' copy success, size= %d", src.RealPath(), job.Size) + targetStatus = entity.CopyStatus_STAGED + break // break from switch + } + + for dst, err := range job.FailTargets { + if err == nil { + continue + } + if errors.Is(err, acp.ErrTargetNoSpace) { + continue + } + + a.logger.WithContext(ctx).WithError(err).Errorf("file '%s' copy fail, dst= '%s'", src.RealPath(), dst) + if errors.Is(err, acp.ErrTargetDropToReadonly) { + dropToReadonly = true + } + } + default: + return + } + + a.updateJob(ctx, func(_ *Job, state *entity.JobArchiveState) error { + idx := sort.Search(len(state.Sources), func(idx int) bool { + return src.Compare(state.Sources[idx].Source) <= 0 + }) + if idx < 0 || idx >= len(state.Sources) || src.Compare(state.Sources[idx].Source) != 0 { + return fmt.Errorf( + "cannot found target file, real_path= %s found_index= %d tape_file_path= %v", src.RealPath(), idx, + lo.Map(state.Sources, func(source *entity.SourceState, _ int) string { return source.Source.RealPath() }), + ) + } + + founded := state.Sources[idx] + if founded == nil || !src.Equal(founded.Source) { + return fmt.Errorf( + "founded file not match, real_path= %s found_path= %s tape_file_path= %v", src.RealPath(), founded.Source.RealPath(), + lo.Map(state.Sources, func(source *entity.SourceState, _ int) string { return source.Source.RealPath() }), + ) + } + + founded.Status = targetStatus + return nil + }) + } + })) + + defer func() { + ctx := tools.WithoutTimeout(ctx) + + // if tape drop to readonly, ltfs cannot write index to partition a. + // rollback sources for next try. + if dropToReadonly { + a.logger.WithContext(ctx).Errorf("tape filesystem had droped to readonly, rollback, barcode= '%s'", barcode) + a.rollbackSources(ctx) + return + } + + report := reportGetter() + sort.Slice(report.Jobs, func(i, j int) bool { + return entity.NewSourceFromACPJob(report.Jobs[i]).Compare(entity.NewSourceFromACPJob(report.Jobs[j])) < 0 + }) + + reportFile, err := a.exe.newReportWriter(barcode) + if err != nil { + a.logger.WithContext(ctx).WithError(err).Warnf("open report file fail, barcode= '%s'", barcode) + } else { + defer reportFile.Close() + tools.WrapWithLogger(ctx, a.logger, func() { + reportFile.Write([]byte(report.ToJSONString(false))) + }) + } + + filteredJobs := make([]*acp.Job, 0, len(report.Jobs)) + files := make([]*library.TapeFile, 0, len(report.Jobs)) + for _, job := range report.Jobs { + if len(job.SuccessTargets) == 0 { + continue + } + if !job.Mode.IsRegular() { + continue + } + + hash, err := hex.DecodeString(job.SHA256) + if err != nil { + a.logger.WithContext(ctx).WithError(err).Warnf("decode sha256 fail, path= '%s'", entity.NewSourceFromACPJob(job).RealPath()) + continue + } + + files = append(files, &library.TapeFile{ + Path: path.Join(job.Path...), + Size: job.Size, + Mode: job.Mode, + ModTime: job.ModTime, + WriteTime: job.WriteTime, + Hash: hash, + }) + filteredJobs = append(filteredJobs, job) + } + + tape, err := a.exe.lib.CreateTape(ctx, &library.Tape{ + Barcode: barcode, + Name: name, + Encryption: encryption, + CreateTime: time.Now(), + }, files) + if err != nil { + rerr = tools.AppendError(rerr, fmt.Errorf("create tape fail, barcode= '%s' name= '%s', %w", barcode, name, err)) + return + } + a.logger.Infof("create tape success, tape_id= %d", tape.ID) + + if err := a.exe.lib.TrimFiles(ctx); err != nil { + a.logger.WithError(err).Warnf("trim library files fail") + } + + if err := a.markSourcesAsSubmited(ctx, filteredJobs); err != nil { + rerr = tools.AppendError(rerr, fmt.Errorf("mark source as submited fail, %w", err)) + return + } + }() + + copyer, err := acp.New(ctx, opts...) + if err != nil { + rerr = fmt.Errorf("start copy fail, %w", err) + return + } + + copyer.Wait() + return +} + +func (a *jobArchiveExecutor) markSourcesAsSubmited(ctx context.Context, jobs []*acp.Job) error { + return a.updateJob(ctx, func(_ *Job, state *entity.JobArchiveState) error { + searchableSource := state.Sources[:] + for _, job := range jobs { + src := entity.NewSourceFromACPJob(job) + for idx, testSrc := range searchableSource { + if src.Compare(testSrc.Source) <= 0 { + searchableSource = searchableSource[idx:] + break + } + } + + target := searchableSource[0] + if target == nil || !src.Equal(target.Source) { + continue + } + + target.Status = entity.CopyStatus_SUBMITED + } + + return nil + }) +} + +func (a *jobArchiveExecutor) rollbackSources(ctx context.Context) error { + return a.updateJob(ctx, func(_ *Job, state *entity.JobArchiveState) error { + for _, source := range state.Sources { + if source.Status == entity.CopyStatus_SUBMITED { + continue + } + source.Status = entity.CopyStatus_PENDING + } + + return nil + }) +} + +func (a *jobArchiveExecutor) getTodoSources() int { + state := a.getState() + + var todo int + for _, s := range state.Sources { + if s.Status == entity.CopyStatus_SUBMITED { + continue + } + todo++ + } + + return todo +} + +func (a *jobArchiveExecutor) makeTapeFinished(ctx context.Context) { + if a.getTodoSources() > 0 { + a.dispatch(ctx, &entity.JobArchiveDispatchParam{Param: &entity.JobArchiveDispatchParam_WaitForTape{WaitForTape: &entity.JobArchiveWaitForTapeParam{}}}) + } else { + a.dispatch(ctx, &entity.JobArchiveDispatchParam{Param: &entity.JobArchiveDispatchParam_Finished{Finished: &entity.JobArchiveFinishedParam{}}}) + } +} diff --git a/executor/job_restore.go b/executor/job_restore.go index d756ed9..3c474bf 100644 --- a/executor/job_restore.go +++ b/executor/job_restore.go @@ -1,59 +1,3 @@ package executor -import ( - "context" - "fmt" - "os" - "os/exec" - "time" - - "github.com/samuelncui/yatm/library" - "github.com/sirupsen/logrus" -) - -func (e *jobRestoreExecutor) loadTape(ctx context.Context, device string, tape *library.Tape) error { - if !e.exe.occupyDevice(device) { - return fmt.Errorf("device is using, device= %s", device) - } - defer e.exe.releaseDevice(device) - - keyPath, keyRecycle, err := e.exe.restoreKey(tape.Encryption) - if err != nil { - return err - } - defer func() { - time.Sleep(time.Second) - keyRecycle() - }() - - logger := logrus.StandardLogger() - - if err := runCmd(logger, e.exe.makeEncryptCmd(ctx, device, keyPath, tape.Barcode, tape.Name)); err != nil { - return fmt.Errorf("run encrypt script fail, %w", err) - } - - mountPoint, err := os.MkdirTemp("", "*.ltfs") - if err != nil { - return fmt.Errorf("create temp mountpoint, %w", err) - } - - mountCmd := exec.CommandContext(ctx, e.exe.scripts.Mount) - mountCmd.Env = append(mountCmd.Env, fmt.Sprintf("DEVICE=%s", device), fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) - if err := runCmd(logger, mountCmd); err != nil { - return fmt.Errorf("run mount script fail, %w", err) - } - // defer func() { - // umountCmd := exec.CommandContext(ctx, e.umountScript) - // umountCmd.Env = append(umountCmd.Env, fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) - // if err := runCmd(logger, umountCmd); err != nil { - // logger.WithContext(ctx).WithError(err).Errorf("run umount script fail, %s", mountPoint) - // return - // } - // if err := os.Remove(mountPoint); err != nil { - // logger.WithContext(ctx).WithError(err).Errorf("remove mount point fail, %s", mountPoint) - // return - // } - // }() - - return nil -} +type jobTypeRestore struct{} diff --git a/executor/job_restore_display.go b/executor/job_restore_display.go deleted file mode 100644 index 0cd652d..0000000 --- a/executor/job_restore_display.go +++ /dev/null @@ -1,25 +0,0 @@ -package executor - -import ( - "context" - "sync/atomic" - - "github.com/samuelncui/yatm/entity" -) - -func (e *Executor) getRestoreDisplay(ctx context.Context, job *Job) (*entity.JobRestoreDisplay, error) { - display := new(entity.JobRestoreDisplay) - - if exe := e.getRestoreExecutor(ctx, job); exe != nil && exe.progress != nil { - display.CopiedBytes = atomic.LoadInt64(&exe.progress.bytes) - display.CopiedFiles = atomic.LoadInt64(&exe.progress.files) - display.TotalBytes = atomic.LoadInt64(&exe.progress.totalBytes) - display.TotalFiles = atomic.LoadInt64(&exe.progress.totalFiles) - display.StartTime = exe.progress.startTime.Unix() - - speed := atomic.LoadInt64(&exe.progress.speed) - display.Speed = &speed - } - - return display, nil -} diff --git a/executor/job_restore_exe.go b/executor/job_restore_exe.go index 16ce380..0fab293 100644 --- a/executor/job_restore_exe.go +++ b/executor/job_restore_exe.go @@ -2,44 +2,30 @@ package executor import ( "context" - "encoding/hex" "fmt" "io" "os" - "os/exec" - "path" - "sort" - "strings" "sync" "sync/atomic" - "time" mapset "github.com/deckarep/golang-set/v2" - jsoniter "github.com/json-iterator/go" - "github.com/samber/lo" - "github.com/samuelncui/acp" "github.com/samuelncui/yatm/entity" "github.com/samuelncui/yatm/tools" "github.com/sirupsen/logrus" ) -var ( - runningRestores sync.Map -) +type jobRestoreExecutor struct { + lock sync.Mutex + exe *Executor + job *Job -func (e *Executor) getRestoreExecutor(ctx context.Context, job *Job) *jobRestoreExecutor { - if running, has := runningRestores.Load(job.ID); has { - return running.(*jobRestoreExecutor) - } - return nil + progress *progress + logFile *os.File + logger *logrus.Logger } -func (e *Executor) newRestoreExecutor(ctx context.Context, job *Job) (*jobRestoreExecutor, error) { - if exe := e.getRestoreExecutor(ctx, job); exe != nil { - return exe, nil - } - - logFile, err := e.newLogWriter(job.ID) +func (*jobTypeRestore) GetExecutor(ctx context.Context, exe *Executor, job *Job) (JobExecutor, error) { + logFile, err := exe.newLogWriter(job.ID) if err != nil { return nil, fmt.Errorf("get log writer fail, %w", err) } @@ -47,39 +33,27 @@ func (e *Executor) newRestoreExecutor(ctx context.Context, job *Job) (*jobRestor logger := logrus.New() logger.SetOutput(io.MultiWriter(os.Stderr, logFile)) - exe := &jobRestoreExecutor{ - exe: e, + e := &jobRestoreExecutor{ + exe: exe, job: job, - state: job.State.GetRestore(), - logFile: logFile, logger: logger, } - runningRestores.Store(job.ID, exe) - return exe, nil + return e, nil } -type jobRestoreExecutor struct { - exe *Executor - job *Job - - stateLock sync.Mutex - state *entity.JobRestoreState - - progress *progress - logFile *os.File - logger *logrus.Logger -} - -func (a *jobRestoreExecutor) submit(ctx context.Context, param *entity.JobRestoreNextParam) { - if err := a.handle(ctx, param); err != nil { - a.logger.WithContext(ctx).WithError(err).Infof("handler param fail, param= %s", param) +func (a *jobRestoreExecutor) Dispatch(ctx context.Context, next *entity.JobDispatchParam) error { + param := next.GetRestore() + if param == nil { + return fmt.Errorf("unexpected next param type, unexpected= JobRestoreDispatchParam, has= %s", next) } + + return a.dispatch(ctx, param) } -func (a *jobRestoreExecutor) handle(ctx context.Context, param *entity.JobRestoreNextParam) error { +func (a *jobRestoreExecutor) dispatch(ctx context.Context, param *entity.JobRestoreDispatchParam) error { if p := param.GetCopying(); p != nil { if err := a.switchStep( ctx, entity.JobRestoreStep_COPYING, entity.JobStatus_PROCESSING, @@ -102,7 +76,7 @@ func (a *jobRestoreExecutor) handle(ctx context.Context, param *entity.JobRestor if p := param.GetWaitForTape(); p != nil { return a.switchStep( - ctx, entity.JobRestoreStep_WAIT_FOR_TAPE, entity.JobStatus_PROCESSING, + ctx, entity.JobRestoreStep_WAIT_FOR_TAPE, entity.JobStatus_PENDING, mapset.NewThreadUnsafeSet(entity.JobRestoreStep_PENDING, entity.JobRestoreStep_COPYING), ) } @@ -115,258 +89,80 @@ func (a *jobRestoreExecutor) handle(ctx context.Context, param *entity.JobRestor return err } - a.logFile.Close() - runningRestores.Delete(a.job.ID) - return nil + return a.Close(ctx) } return nil } -func (a *jobRestoreExecutor) restoreTape(ctx context.Context, device string) (rerr error) { - if !a.exe.occupyDevice(device) { - return fmt.Errorf("device is using, device= %s", device) - } - defer a.exe.releaseDevice(device) - defer func() { - if _, found := lo.Find(a.state.Tapes, func(item *entity.RestoreTape) bool { - return item.Status != entity.CopyStatus_SUBMITED - }); found { - a.submit(tools.WithoutTimeout(ctx), &entity.JobRestoreNextParam{ - Param: &entity.JobRestoreNextParam_WaitForTape{WaitForTape: &entity.JobRestoreWaitForTapeParam{}}, - }) - return - } - - a.submit(tools.WithoutTimeout(ctx), &entity.JobRestoreNextParam{ - Param: &entity.JobRestoreNextParam_Finished{Finished: &entity.JobRestoreFinishedParam{}}, - }) - }() - - readInfoCmd := exec.CommandContext(ctx, a.exe.scripts.ReadInfo) - readInfoCmd.Env = append(readInfoCmd.Env, fmt.Sprintf("DEVICE=%s", device)) - infoBuf, err := runCmdWithReturn(a.logger, readInfoCmd) - if err != nil { - return fmt.Errorf("run read info script fail, %w", err) +func (a *jobRestoreExecutor) Display(ctx context.Context) (*entity.JobDisplay, error) { + p := a.progress + if p == nil { + return nil, nil } - barcode := jsoniter.Get(infoBuf, "barcode").ToString() - if len(barcode) > 6 { - barcode = barcode[:6] - } + display := new(entity.JobRestoreDisplay) + display.CopiedBytes = atomic.LoadInt64(&p.bytes) + display.CopiedFiles = atomic.LoadInt64(&p.files) + display.TotalBytes = atomic.LoadInt64(&p.totalBytes) + display.TotalFiles = atomic.LoadInt64(&p.totalFiles) + display.StartTime = p.startTime.Unix() - restoreTape, found := lo.Find(a.state.Tapes, func(t *entity.RestoreTape) bool { - return t.Barcode == barcode - }) - if !found || restoreTape == nil { - expects := lo.Map(a.state.Tapes, func(t *entity.RestoreTape, _ int) string { return t.Barcode }) - return fmt.Errorf("unexpected tape barcode in library, has= '%s' expect= %v", barcode, expects) - } - if restoreTape.Status == entity.CopyStatus_SUBMITED { - return fmt.Errorf("unexpected restore tape state status, tape is restored, status= '%s'", restoreTape.Status) - } + speed := atomic.LoadInt64(&p.speed) + display.Speed = &speed - tape, err := a.exe.lib.GetTape(ctx, restoreTape.TapeId) - if err != nil { - return fmt.Errorf("get tape info fail, barcode= '%s' id= %d, %w", restoreTape.Barcode, restoreTape.TapeId, err) - } + return &entity.JobDisplay{Display: &entity.JobDisplay_Restore{Restore: display}}, nil +} - keyPath, keyRecycle, err := a.exe.restoreKey(tape.Encryption) - if err != nil { - return err - } - defer func() { - time.Sleep(time.Second) - keyRecycle() - }() +func (a *jobRestoreExecutor) Close(ctx context.Context) error { + a.logFile.Close() + a.exe.RemoveJobExecutor(ctx, a.job.ID) + return nil +} - if err := runCmd(a.logger, a.exe.makeEncryptCmd(ctx, device, keyPath, barcode, tape.Name)); err != nil { - return fmt.Errorf("run encrypt script fail, %w", err) - } +func (a *jobRestoreExecutor) Logger() *logrus.Logger { + return a.logger +} - mountPoint, err := os.MkdirTemp("", "*.ltfs") - if err != nil { - return fmt.Errorf("create temp mountpoint, %w", err) - } - sourcePath := tools.Cache(func(p string) string { return path.Join(mountPoint, p) }) +func (a *jobRestoreExecutor) getState() *entity.JobRestoreState { + a.lock.Lock() + defer a.lock.Unlock() - mountCmd := exec.CommandContext(ctx, a.exe.scripts.Mount) - mountCmd.Env = append(mountCmd.Env, fmt.Sprintf("DEVICE=%s", device), fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) - if err := runCmd(a.logger, mountCmd); err != nil { - return fmt.Errorf("run mount script fail, %w", err) + if a.job.State == nil || a.job.State.GetRestore() == nil { + a.job.State = &entity.JobState{State: &entity.JobState_Restore{Restore: &entity.JobRestoreState{}}} } - defer func() { - umountCmd := exec.CommandContext(tools.WithoutTimeout(ctx), a.exe.scripts.Umount) - umountCmd.Env = append(umountCmd.Env, fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) - if err := runCmd(a.logger, umountCmd); err != nil { - a.logger.WithContext(ctx).WithError(err).Errorf("run umount script fail, %s", mountPoint) - return - } - if err := os.Remove(mountPoint); err != nil { - a.logger.WithContext(ctx).WithError(err).Errorf("remove mount point fail, %s", mountPoint) - return - } - }() + return a.job.State.GetRestore() +} - opts := make([]acp.Option, 0, 16) - for _, f := range restoreTape.Files { - if f.Status == entity.CopyStatus_SUBMITED { - continue - } +func (a *jobRestoreExecutor) updateJob(ctx context.Context, change func(*Job, *entity.JobRestoreState) error) error { + a.lock.Lock() + defer a.lock.Unlock() - opts = append(opts, acp.AccurateJob(sourcePath(f.TapePath), []string{path.Join(a.exe.paths.Target, f.TargetPath)})) + if a.job.State == nil || a.job.State.GetRestore() == nil { + a.job.State = &entity.JobState{State: &entity.JobState_Restore{Restore: &entity.JobRestoreState{}}} } - opts = append(opts, acp.WithHash(true)) - opts = append(opts, acp.SetFromDevice(acp.LinearDevice(true))) - opts = append(opts, acp.WithLogger(a.logger)) - - a.progress = newProgress() - defer func() { a.progress = nil }() - - convertPath := tools.Cache(func(p string) string { return strings.ReplaceAll(p, "/", "\x00") }) - opts = append(opts, acp.WithEventHandler(func(ev acp.Event) { - switch e := ev.(type) { - case *acp.EventUpdateCount: - atomic.StoreInt64(&a.progress.totalBytes, e.Bytes) - atomic.StoreInt64(&a.progress.totalFiles, e.Files) - return - case *acp.EventUpdateProgress: - a.progress.setBytes(e.Bytes) - atomic.StoreInt64(&a.progress.files, e.Files) - return - case *acp.EventReportError: - a.logger.WithContext(ctx).Errorf("acp report error, src= '%s' dst= '%s' err= '%s'", e.Error.Src, e.Error.Dst, e.Error.Err) - return - case *acp.EventUpdateJob: - job := e.Job - src := entity.NewSourceFromACPJob(job) - - var targetStatus entity.CopyStatus - switch job.Status { - case "pending": - targetStatus = entity.CopyStatus_PENDING - case "preparing": - targetStatus = entity.CopyStatus_RUNNING - case "finished": - a.logger.WithContext(ctx).Infof("file '%s' copy finished, size= %d", src.RealPath(), job.Size) - - targetStatus = entity.CopyStatus_STAGED - if len(job.FailTargets) > 0 { - targetStatus = entity.CopyStatus_FAILED - } - - for dst, err := range job.FailTargets { - if err == nil { - continue - } - a.logger.WithContext(ctx).WithError(err).Errorf("file '%s' copy fail, dst= '%s'", src.RealPath(), dst) - } - default: - return - } - - a.stateLock.Lock() - defer a.stateLock.Unlock() - - realPath := src.RealPath() - idx := sort.Search(len(restoreTape.Files), func(idx int) bool { - return convertPath(realPath) <= convertPath(sourcePath(restoreTape.Files[idx].TapePath)) - }) - if idx < 0 || idx >= len(restoreTape.Files) { - a.logger.Warnf( - "cannot found target file, real_path= %s found_index= %d tape_file_path= %v", realPath, idx, - lo.Map(restoreTape.Files, func(file *entity.RestoreFile, _ int) string { return sourcePath(file.TapePath) }), - ) - return - } - - targetFile := restoreTape.Files[idx] - if targetFile == nil || realPath != sourcePath(targetFile.TapePath) { - a.logger.Warnf( - "cannot match target file, real_path= %s found_index= %d found_file_path= %s", - realPath, idx, sourcePath(targetFile.TapePath), - ) - return - } - - if targetStatus == entity.CopyStatus_STAGED { - if targetHash := hex.EncodeToString(targetFile.Hash); targetHash != job.SHA256 { - targetStatus = entity.CopyStatus_FAILED - - a.logger.Warnf( - "copy checksum do not match target file hash, real_path= %s target_hash= %s copy_hash= %s", - realPath, targetHash, job.SHA256, - ) - } - if targetSize := targetFile.Size; targetSize != job.Size { - targetStatus = entity.CopyStatus_FAILED - - a.logger.Warnf( - "copy size do not match target file hash, real_path= %s target_size= %d copy_size= %d", - realPath, targetSize, job.Size, - ) - } - } - - targetFile.Status = targetStatus - if _, err := a.exe.SaveJob(ctx, a.job); err != nil { - a.logger.WithContext(ctx).Infof("save job for update file fail, name= %s", job.Base+path.Join(job.Path...)) - } - - return - } - })) - - restoreTape.Status = entity.CopyStatus_RUNNING - if _, err := a.exe.SaveJob(tools.WithoutTimeout(ctx), a.job); err != nil { - a.logger.WithContext(ctx).Infof("save job for submit tape fail, barcode= %s", restoreTape.Barcode) + if err := change(a.job, a.job.State.GetRestore()); err != nil { + a.logger.WithContext(ctx).WithError(err).Warnf("update state failed while exec change callback") + return fmt.Errorf("update state failed while exec change callback, %w", err) } - - defer func() { - a.stateLock.Lock() - defer a.stateLock.Unlock() - - restoreTape.Status = entity.CopyStatus_SUBMITED - for _, file := range restoreTape.Files { - if file.Status == entity.CopyStatus_STAGED { - file.Status = entity.CopyStatus_SUBMITED - } - - if file.Status != entity.CopyStatus_SUBMITED { - restoreTape.Status = entity.CopyStatus_FAILED - } - } - - if _, err := a.exe.SaveJob(tools.WithoutTimeout(ctx), a.job); err != nil { - a.logger.WithContext(ctx).Infof("save job for submit tape fail, barcode= %s", restoreTape.Barcode) - } - }() - - copyer, err := acp.New(ctx, opts...) - if err != nil { - rerr = fmt.Errorf("start copy fail, %w", err) - return + if _, err := a.exe.SaveJob(ctx, a.job); err != nil { + a.logger.WithContext(ctx).WithError(err).Warnf("update state failed while save job") + return fmt.Errorf("update state failed while save job, %w", err) } - copyer.Wait() - return + return nil } func (a *jobRestoreExecutor) switchStep(ctx context.Context, target entity.JobRestoreStep, status entity.JobStatus, expect mapset.Set[entity.JobRestoreStep]) error { - a.stateLock.Lock() - defer a.stateLock.Unlock() - - if !expect.Contains(a.state.Step) { - return fmt.Errorf("unexpected current step, target= '%s' expect= '%s' has= '%s'", target, expect, a.state.Step) - } - - a.state.Step = target - a.job.Status = status - if _, err := a.exe.SaveJob(ctx, a.job); err != nil { - return fmt.Errorf("switch to step copying, save job fail, %w", err) - } + return a.updateJob(ctx, func(job *Job, state *entity.JobRestoreState) error { + if !expect.Contains(state.Step) { + return fmt.Errorf("unexpected current step, target= '%s' expect= '%s' has= '%s'", target, expect, state.Step) + } - return nil + state.Step = target + job.Status = status + return nil + }) } diff --git a/executor/job_restore_init.go b/executor/job_restore_init.go new file mode 100644 index 0000000..58f9287 --- /dev/null +++ b/executor/job_restore_init.go @@ -0,0 +1,249 @@ +package executor + +import ( + "context" + "fmt" + "io/fs" + "sort" + "strings" + + mapset "github.com/deckarep/golang-set/v2" + "github.com/samber/lo" + "github.com/samuelncui/yatm/entity" + "github.com/samuelncui/yatm/library" + "github.com/samuelncui/yatm/tools" + "github.com/sirupsen/logrus" +) + +func (a *jobRestoreExecutor) Initialize(ctx context.Context, param *entity.JobParam) error { + if err := a.applyParam(ctx, param.GetRestore()); err != nil { + return err + } + + return a.dispatch(ctx, &entity.JobRestoreDispatchParam{Param: &entity.JobRestoreDispatchParam_WaitForTape{ + WaitForTape: &entity.JobRestoreWaitForTapeParam{}, + }}) +} + +type restoreFile struct { + *library.File + target string +} + +func (a *jobRestoreExecutor) applyParam(ctx context.Context, param *entity.JobRestoreParam) error { + if param == nil { + return fmt.Errorf("restore param is nil") + } + + return a.updateJob(ctx, func(_ *Job, state *entity.JobRestoreState) error { + exe := a.exe + + files, err := exe.getRestoreFiles(ctx, param.FileIds...) + if err != nil { + return fmt.Errorf("get restore files fail, ids= %v, %w", param.FileIds, err) + } + + fileIDs := make([]int64, 0, len(files)) + for _, file := range files { + fileIDs = append(fileIDs, file.ID) + } + + positions, err := exe.lib.MGetPositionByFileID(ctx, fileIDs...) + if err != nil { + return err + } + + tapeMapping := make(map[int64]mapset.Set[int64], 4) + for _, file := range files { + for _, posi := range positions[file.ID] { + set := tapeMapping[posi.TapeID] + if set == nil { + tapeMapping[posi.TapeID] = mapset.NewThreadUnsafeSet(file.ID) + continue + } + set.Add(file.ID) + } + } + + tapes, err := exe.lib.MGetTape(ctx, lo.Keys(tapeMapping)...) + if err != nil { + return err + } + for tapeID := range tapeMapping { + if tape, has := tapes[tapeID]; has && tape != nil { + continue + } + + logrus.WithContext(ctx).Infof("tape not found, tape_id= %d", tapeID) + delete(tapeMapping, tapeID) + } + + restoreTapes := make([]*entity.RestoreTape, 0, len(tapeMapping)) + for len(tapeMapping) > 0 { + var maxTapeID int64 + for tapeID, files := range tapeMapping { + if maxTapeID == 0 { + maxTapeID = tapeID + continue + } + + diff := files.Cardinality() - tapeMapping[maxTapeID].Cardinality() + if diff > 0 { + maxTapeID = tapeID + continue + } + if diff < 0 { + continue + } + if tapeID < maxTapeID { + maxTapeID = tapeID + continue + } + } + if maxTapeID == 0 { + return fmt.Errorf("max tape not found, tape_ids= %v", lo.Keys(tapeMapping)) + } + + fileIDs := tapeMapping[maxTapeID] + delete(tapeMapping, maxTapeID) + if fileIDs.Cardinality() == 0 { + continue + } + for i, f := range tapeMapping { + tapeMapping[i] = f.Difference(fileIDs) + } + + targets := make([]*entity.RestoreFile, 0, fileIDs.Cardinality()) + for _, fileID := range fileIDs.ToSlice() { + file := files[fileID] + if file == nil { + continue + } + + posi := positions[fileID] + if len(posi) == 0 { + logrus.WithContext(ctx).Infof("file position not found, file_id= %d", fileID) + continue + } + + for _, p := range posi { + if p.TapeID != maxTapeID { + continue + } + + targets = append(targets, &entity.RestoreFile{ + FileId: file.ID, + TapeId: p.TapeID, + PositionId: p.ID, + Status: entity.CopyStatus_PENDING, + Size: file.Size, + Hash: file.Hash, + TapePath: p.Path, + TargetPath: file.target, + }) + break + } + } + + convertPath := tools.ThreadUnsafeCache(func(p string) string { return strings.ReplaceAll(p, "/", "\x00") }) + sort.Slice(targets, func(i, j int) bool { + return convertPath(targets[i].TapePath) < convertPath(targets[j].TapePath) + }) + + restoreTapes = append(restoreTapes, &entity.RestoreTape{ + TapeId: maxTapeID, + Barcode: tapes[maxTapeID].Barcode, + Status: entity.CopyStatus_PENDING, + Files: targets, + }) + } + + state.Step = entity.JobRestoreStep_PENDING + state.Tapes = restoreTapes + return nil + }) +} + +func (e *Executor) getRestoreFiles(ctx context.Context, rootIDs ...int64) (map[int64]*restoreFile, error) { + rootIDSet := mapset.NewThreadUnsafeSet(rootIDs...) + for _, id := range rootIDs { + parents, err := e.lib.ListParents(ctx, id) + if err != nil { + return nil, err + } + if len(parents) <= 1 { + continue + } + + for _, parent := range parents[:len(parents)-1] { + if !rootIDSet.Contains(parent.ID) { + continue + } + + rootIDSet.Remove(id) + break + } + } + + rootIDs = rootIDSet.ToSlice() + mapping, err := e.lib.MGetFile(ctx, rootIDs...) + if err != nil { + return nil, fmt.Errorf("mget file fail, ids= %v, %w", rootIDs, err) + } + + files := make([]*restoreFile, 0, len(rootIDs)*8) + visited := mapset.NewThreadUnsafeSet[int64]() + for _, root := range mapping { + if visited.Contains(root.ID) { + continue + } + + visited.Add(root.ID) + if !fs.FileMode(root.Mode).IsDir() { + files = append(files, &restoreFile{File: root, target: root.Name}) + continue + } + + found, err := e.visitFiles(ctx, root.Name, nil, visited, root.ID) + if err != nil { + return nil, err + } + + files = append(files, found...) + } + + results := make(map[int64]*restoreFile, len(files)) + for _, f := range files { + results[f.ID] = f + } + + return results, nil +} + +func (e *Executor) visitFiles(ctx context.Context, path string, files []*restoreFile, visited mapset.Set[int64], parentID int64) ([]*restoreFile, error) { + children, err := e.lib.List(ctx, parentID) + if err != nil { + return nil, err + } + + for _, child := range children { + if visited.Contains(child.ID) { + continue + } + + visited.Add(child.ID) + + target := path + "/" + child.Name + if !fs.FileMode(child.Mode).IsDir() { + files = append(files, &restoreFile{File: child, target: target}) + continue + } + + files, err = e.visitFiles(ctx, target, files, visited, child.ID) + if err != nil { + return nil, err + } + } + + return files, nil +} diff --git a/executor/job_restore_load.go.bak b/executor/job_restore_load.go.bak new file mode 100644 index 0000000..289417d --- /dev/null +++ b/executor/job_restore_load.go.bak @@ -0,0 +1,59 @@ +package executor + +import ( + "context" + "fmt" + "os" + "os/exec" + "time" + + "github.com/samuelncui/yatm/library" + "github.com/sirupsen/logrus" +) + +func (e *jobRestoreExecutor) loadTape(ctx context.Context, device string, tape *library.Tape) error { + if !e.exe.OccupyDevice(device) { + return fmt.Errorf("device is using, device= %s", device) + } + defer e.exe.ReleaseDevice(device) + + keyPath, keyRecycle, err := e.exe.restoreKey(tape.Encryption) + if err != nil { + return err + } + defer func() { + time.Sleep(time.Second) + keyRecycle() + }() + + logger := logrus.StandardLogger() + + if err := runCmd(logger, e.exe.makeEncryptCmd(ctx, device, keyPath, tape.Barcode, tape.Name)); err != nil { + return fmt.Errorf("run encrypt script fail, %w", err) + } + + mountPoint, err := os.MkdirTemp("", "*.ltfs") + if err != nil { + return fmt.Errorf("create temp mountpoint, %w", err) + } + + mountCmd := exec.CommandContext(ctx, e.exe.scripts.Mount) + mountCmd.Env = append(mountCmd.Env, fmt.Sprintf("DEVICE=%s", device), fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) + if err := runCmd(logger, mountCmd); err != nil { + return fmt.Errorf("run mount script fail, %w", err) + } + // defer func() { + // umountCmd := exec.CommandContext(ctx, e.umountScript) + // umountCmd.Env = append(umountCmd.Env, fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) + // if err := runCmd(logger, umountCmd); err != nil { + // logger.WithContext(ctx).WithError(err).Errorf("run umount script fail, %s", mountPoint) + // return + // } + // if err := os.Remove(mountPoint); err != nil { + // logger.WithContext(ctx).WithError(err).Errorf("remove mount point fail, %s", mountPoint) + // return + // } + // }() + + return nil +} diff --git a/executor/job_restore_param.go b/executor/job_restore_param.go deleted file mode 100644 index d7eff71..0000000 --- a/executor/job_restore_param.go +++ /dev/null @@ -1,233 +0,0 @@ -package executor - -import ( - "context" - "fmt" - "io/fs" - "sort" - "strings" - - mapset "github.com/deckarep/golang-set/v2" - "github.com/samber/lo" - "github.com/samuelncui/yatm/entity" - "github.com/samuelncui/yatm/library" - "github.com/samuelncui/yatm/tools" - "github.com/sirupsen/logrus" -) - -type restoreFile struct { - *library.File - target string -} - -func (e *Executor) createRestore(ctx context.Context, job *Job, param *entity.JobRestoreParam) error { - files, err := e.getRestoreFiles(ctx, param.FileIds...) - if err != nil { - return fmt.Errorf("get restore files fail, ids= %v, %w", param.FileIds, err) - } - - fileIDs := make([]int64, 0, len(files)) - for _, file := range files { - fileIDs = append(fileIDs, file.ID) - } - - positions, err := e.lib.MGetPositionByFileID(ctx, fileIDs...) - if err != nil { - return err - } - - tapeMapping := make(map[int64]mapset.Set[int64], 4) - for _, file := range files { - for _, posi := range positions[file.ID] { - set := tapeMapping[posi.TapeID] - if set == nil { - tapeMapping[posi.TapeID] = mapset.NewThreadUnsafeSet(file.ID) - continue - } - set.Add(file.ID) - } - } - - tapes, err := e.lib.MGetTape(ctx, lo.Keys(tapeMapping)...) - if err != nil { - return err - } - for tapeID := range tapeMapping { - if tape, has := tapes[tapeID]; has && tape != nil { - continue - } - - logrus.WithContext(ctx).Infof("tape not found, tape_id= %d", tapeID) - delete(tapeMapping, tapeID) - } - - restoreTapes := make([]*entity.RestoreTape, 0, len(tapeMapping)) - for len(tapeMapping) > 0 { - var maxTapeID int64 - for tapeID, files := range tapeMapping { - if maxTapeID == 0 { - maxTapeID = tapeID - continue - } - - diff := files.Cardinality() - tapeMapping[maxTapeID].Cardinality() - if diff > 0 { - maxTapeID = tapeID - continue - } - if diff < 0 { - continue - } - if tapeID < maxTapeID { - maxTapeID = tapeID - continue - } - } - if maxTapeID == 0 { - return fmt.Errorf("max tape not found, tape_ids= %v", lo.Keys(tapeMapping)) - } - - fileIDs := tapeMapping[maxTapeID] - delete(tapeMapping, maxTapeID) - if fileIDs.Cardinality() == 0 { - continue - } - for i, f := range tapeMapping { - tapeMapping[i] = f.Difference(fileIDs) - } - - targets := make([]*entity.RestoreFile, 0, fileIDs.Cardinality()) - for _, fileID := range fileIDs.ToSlice() { - file := files[fileID] - if file == nil { - continue - } - - posi := positions[fileID] - if len(posi) == 0 { - logrus.WithContext(ctx).Infof("file position not found, file_id= %d", fileID) - continue - } - - for _, p := range posi { - if p.TapeID != maxTapeID { - continue - } - - targets = append(targets, &entity.RestoreFile{ - FileId: file.ID, - TapeId: p.TapeID, - PositionId: p.ID, - Status: entity.CopyStatus_PENDING, - Size: file.Size, - Hash: file.Hash, - TapePath: p.Path, - TargetPath: file.target, - }) - break - } - } - - convertPath := tools.Cache(func(p string) string { return strings.ReplaceAll(p, "/", "\x00") }) - sort.Slice(targets, func(i, j int) bool { - return convertPath(targets[i].TapePath) < convertPath(targets[j].TapePath) - }) - - restoreTapes = append(restoreTapes, &entity.RestoreTape{ - TapeId: maxTapeID, - Barcode: tapes[maxTapeID].Barcode, - Status: entity.CopyStatus_PENDING, - Files: targets, - }) - } - - job.State = &entity.JobState{State: &entity.JobState_Restore{Restore: &entity.JobRestoreState{ - Step: entity.JobRestoreStep_PENDING, - Tapes: restoreTapes, - }}} - return nil -} - -func (e *Executor) getRestoreFiles(ctx context.Context, rootIDs ...int64) (map[int64]*restoreFile, error) { - rootIDSet := mapset.NewThreadUnsafeSet(rootIDs...) - for _, id := range rootIDs { - parents, err := e.lib.ListParents(ctx, id) - if err != nil { - return nil, err - } - if len(parents) <= 1 { - continue - } - - for _, parent := range parents[:len(parents)-1] { - if !rootIDSet.Contains(parent.ID) { - continue - } - - rootIDSet.Remove(id) - break - } - } - - rootIDs = rootIDSet.ToSlice() - mapping, err := e.lib.MGetFile(ctx, rootIDs...) - if err != nil { - return nil, fmt.Errorf("mget file fail, ids= %v, %w", rootIDs, err) - } - - files := make([]*restoreFile, 0, len(rootIDs)*8) - visited := mapset.NewThreadUnsafeSet[int64]() - for _, root := range mapping { - if visited.Contains(root.ID) { - continue - } - - visited.Add(root.ID) - if !fs.FileMode(root.Mode).IsDir() { - files = append(files, &restoreFile{File: root, target: root.Name}) - continue - } - - found, err := e.visitFiles(ctx, root.Name, nil, visited, root.ID) - if err != nil { - return nil, err - } - - files = append(files, found...) - } - - results := make(map[int64]*restoreFile, len(files)) - for _, f := range files { - results[f.ID] = f - } - - return results, nil -} - -func (e *Executor) visitFiles(ctx context.Context, path string, files []*restoreFile, visited mapset.Set[int64], parentID int64) ([]*restoreFile, error) { - children, err := e.lib.List(ctx, parentID) - if err != nil { - return nil, err - } - - for _, child := range children { - if visited.Contains(child.ID) { - continue - } - - visited.Add(child.ID) - - target := path + "/" + child.Name - if !fs.FileMode(child.Mode).IsDir() { - files = append(files, &restoreFile{File: child, target: target}) - continue - } - - files, err = e.visitFiles(ctx, target, files, visited, child.ID) - if err != nil { - return nil, err - } - } - - return files, nil -} diff --git a/executor/job_restore_start.go b/executor/job_restore_start.go deleted file mode 100644 index 4e3de1b..0000000 --- a/executor/job_restore_start.go +++ /dev/null @@ -1,15 +0,0 @@ -package executor - -import ( - "context" - - "github.com/samuelncui/yatm/entity" -) - -func (e *Executor) startRestore(ctx context.Context, job *Job) error { - return e.Submit(ctx, job, &entity.JobNextParam{Param: &entity.JobNextParam_Restore{ - Restore: &entity.JobRestoreNextParam{Param: &entity.JobRestoreNextParam_WaitForTape{ - WaitForTape: &entity.JobRestoreWaitForTapeParam{}, - }}, - }}) -} diff --git a/executor/job_restore_tape.go b/executor/job_restore_tape.go new file mode 100644 index 0000000..1067f8d --- /dev/null +++ b/executor/job_restore_tape.go @@ -0,0 +1,260 @@ +package executor + +import ( + "context" + "encoding/hex" + "fmt" + "os" + "os/exec" + "path" + "sort" + "strings" + "sync/atomic" + "time" + + jsoniter "github.com/json-iterator/go" + "github.com/samber/lo" + "github.com/samuelncui/acp" + "github.com/samuelncui/yatm/entity" + "github.com/samuelncui/yatm/tools" +) + +func (a *jobRestoreExecutor) restoreTape(ctx context.Context, device string) (rerr error) { + if !a.exe.OccupyDevice(device) { + return fmt.Errorf("device is using, device= %s", device) + } + defer a.exe.ReleaseDevice(device) + + defer func() { + tapes := a.getState().Tapes + if _, found := lo.Find(tapes, func(item *entity.RestoreTape) bool { + return item.Status != entity.CopyStatus_SUBMITED + }); found { + a.dispatch(tools.WithoutTimeout(ctx), &entity.JobRestoreDispatchParam{ + Param: &entity.JobRestoreDispatchParam_WaitForTape{WaitForTape: &entity.JobRestoreWaitForTapeParam{}}, + }) + return + } + + a.dispatch(tools.WithoutTimeout(ctx), &entity.JobRestoreDispatchParam{ + Param: &entity.JobRestoreDispatchParam_Finished{Finished: &entity.JobRestoreFinishedParam{}}, + }) + }() + + readInfoCmd := exec.CommandContext(ctx, a.exe.scripts.ReadInfo) + readInfoCmd.Env = append(readInfoCmd.Env, fmt.Sprintf("DEVICE=%s", device)) + infoBuf, err := runCmdWithReturn(a.logger, readInfoCmd) + if err != nil { + return fmt.Errorf("run read info script fail, %w", err) + } + + barcode := jsoniter.Get(infoBuf, "barcode").ToString() + if len(barcode) > 6 { + barcode = barcode[:6] + } + barcode = strings.ToUpper(barcode) + + tapes := a.getState().Tapes + tape, found := lo.Find(tapes, func(t *entity.RestoreTape) bool { + return t.Barcode == barcode + }) + if !found || tape == nil { + expects := lo.Map(tapes, func(t *entity.RestoreTape, _ int) string { return t.Barcode }) + return fmt.Errorf("unexpected tape barcode in library, has= '%s' expect= %v", barcode, expects) + } + if tape.Status == entity.CopyStatus_SUBMITED { + return fmt.Errorf("unexpected restore tape state status, tape is restored, status= '%s'", tape.Status) + } + + libTape, err := a.exe.lib.GetTape(ctx, tape.TapeId) + if err != nil { + return fmt.Errorf("get tape info fail, barcode= '%s' id= %d, %w", tape.Barcode, tape.TapeId, err) + } + + keyPath, keyRecycle, err := a.exe.restoreKey(libTape.Encryption) + if err != nil { + return err + } + defer func() { + time.Sleep(time.Second) + keyRecycle() + }() + + if err := runCmd(a.logger, a.exe.makeEncryptCmd(ctx, device, keyPath, barcode, libTape.Name)); err != nil { + return fmt.Errorf("run encrypt script fail, %w", err) + } + + mountPoint, err := os.MkdirTemp("", "*.ltfs") + if err != nil { + return fmt.Errorf("create temp mountpoint, %w", err) + } + sourcePath := tools.ThreadUnsafeCache(func(p string) string { return path.Join(mountPoint, p) }) + + mountCmd := exec.CommandContext(ctx, a.exe.scripts.Mount) + mountCmd.Env = append(mountCmd.Env, fmt.Sprintf("DEVICE=%s", device), fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) + if err := runCmd(a.logger, mountCmd); err != nil { + return fmt.Errorf("run mount script fail, %w", err) + } + + defer func() { + umountCmd := exec.CommandContext(tools.WithoutTimeout(ctx), a.exe.scripts.Umount) + umountCmd.Env = append(umountCmd.Env, fmt.Sprintf("MOUNT_POINT=%s", mountPoint)) + if err := runCmd(a.logger, umountCmd); err != nil { + a.logger.WithContext(ctx).WithError(err).Errorf("run umount script fail, %s", mountPoint) + return + } + if err := os.Remove(mountPoint); err != nil { + a.logger.WithContext(ctx).WithError(err).Errorf("remove mount point fail, %s", mountPoint) + return + } + }() + + opts := make([]acp.Option, 0, 16) + for _, f := range tape.Files { + if f.Status == entity.CopyStatus_SUBMITED { + continue + } + + opts = append(opts, acp.AccurateJob(sourcePath(f.TapePath), []string{path.Join(a.exe.paths.Target, f.TargetPath)})) + } + + opts = append(opts, acp.WithHash(true)) + opts = append(opts, acp.SetFromDevice(acp.LinearDevice(true))) + opts = append(opts, acp.WithLogger(a.logger)) + + a.progress = newProgress() + defer func() { a.progress = nil }() + + convertPath := tools.ThreadUnsafeCache(func(p string) string { return strings.ReplaceAll(p, "/", "\x00") }) + opts = append(opts, acp.WithEventHandler(func(ev acp.Event) { + switch e := ev.(type) { + case *acp.EventUpdateCount: + atomic.StoreInt64(&a.progress.totalBytes, e.Bytes) + atomic.StoreInt64(&a.progress.totalFiles, e.Files) + return + case *acp.EventUpdateProgress: + a.progress.setBytes(e.Bytes) + atomic.StoreInt64(&a.progress.files, e.Files) + return + case *acp.EventReportError: + a.logger.WithContext(ctx).Errorf("acp report error, src= '%s' dst= '%s' err= '%s'", e.Error.Src, e.Error.Dst, e.Error.Err) + return + case *acp.EventUpdateJob: + job := e.Job + src := entity.NewSourceFromACPJob(job) + + var targetStatus entity.CopyStatus + switch job.Status { + case "pending": + targetStatus = entity.CopyStatus_PENDING + case "preparing": + targetStatus = entity.CopyStatus_RUNNING + case "finished": + a.logger.WithContext(ctx).Infof("file '%s' copy finished, size= %d", src.RealPath(), job.Size) + + targetStatus = entity.CopyStatus_STAGED + if len(job.FailTargets) > 0 { + targetStatus = entity.CopyStatus_FAILED + } + + for dst, err := range job.FailTargets { + if err == nil { + continue + } + a.logger.WithContext(ctx).WithError(err).Errorf("file '%s' copy fail, dst= '%s'", src.RealPath(), dst) + } + default: + return + } + + realPath := src.RealPath() + a.updateJob(ctx, func(_ *Job, state *entity.JobRestoreState) error { + tape, has := lo.Find(state.Tapes, func(tape *entity.RestoreTape) bool { return tape.Barcode == barcode }) + if !has || tape == nil { + return fmt.Errorf("cannot found tape, barcode= %s", barcode) + } + + idx := sort.Search(len(tape.Files), func(idx int) bool { + return convertPath(realPath) <= convertPath(sourcePath(tape.Files[idx].TapePath)) + }) + if idx < 0 || idx >= len(tape.Files) { + return fmt.Errorf( + "cannot found target file, real_path= %s found_index= %d tape_file_path= %v", realPath, idx, + lo.Map(tape.Files, func(file *entity.RestoreFile, _ int) string { return sourcePath(file.TapePath) }), + ) + } + + found := tape.Files[idx] + if found == nil || realPath != sourcePath(found.TapePath) { + return fmt.Errorf( + "cannot match found file, real_path= %s found_index= %d found_file_path= %s", + realPath, idx, sourcePath(found.TapePath), + ) + } + + if targetStatus == entity.CopyStatus_STAGED { + if targetHash := hex.EncodeToString(found.Hash); targetHash != job.SHA256 { + targetStatus = entity.CopyStatus_FAILED + + a.logger.Warnf( + "copy checksum do not match target file hash, real_path= %s target_hash= %s copy_hash= %s", + realPath, targetHash, job.SHA256, + ) + } + if targetSize := found.Size; targetSize != job.Size { + targetStatus = entity.CopyStatus_FAILED + + a.logger.Warnf( + "copy size do not match target file hash, real_path= %s target_size= %d copy_size= %d", + realPath, targetSize, job.Size, + ) + } + } + + found.Status = targetStatus + return nil + }) + } + })) + + a.updateJob(ctx, func(_ *Job, state *entity.JobRestoreState) error { + tape, has := lo.Find(state.Tapes, func(tape *entity.RestoreTape) bool { return tape.Barcode == barcode }) + if !has || tape == nil { + return fmt.Errorf("cannot found tape, barcode= %s", barcode) + } + + tape.Status = entity.CopyStatus_RUNNING + return nil + }) + + defer func() { + a.updateJob(ctx, func(job *Job, state *entity.JobRestoreState) error { + tape, has := lo.Find(state.Tapes, func(tape *entity.RestoreTape) bool { return tape.Barcode == barcode }) + if !has || tape == nil { + return fmt.Errorf("cannot found tape, barcode= %s", barcode) + } + + tape.Status = entity.CopyStatus_SUBMITED + for _, file := range tape.Files { + if file.Status == entity.CopyStatus_STAGED { + file.Status = entity.CopyStatus_SUBMITED + } + + if file.Status != entity.CopyStatus_SUBMITED { + tape.Status = entity.CopyStatus_FAILED + } + } + + return nil + }) + }() + + copyer, err := acp.New(ctx, opts...) + if err != nil { + rerr = fmt.Errorf("start copy fail, %w", err) + return + } + + copyer.Wait() + return +} diff --git a/executor/job_type.go b/executor/job_type.go new file mode 100644 index 0000000..a6af7f2 --- /dev/null +++ b/executor/job_type.go @@ -0,0 +1,33 @@ +package executor + +import ( + "context" + + "github.com/modern-go/reflect2" + "github.com/samuelncui/yatm/entity" + "github.com/sirupsen/logrus" +) + +var ( + jobParamToTypes = map[uintptr]uintptr{ + reflect2.RTypeOf(&entity.JobParam_Archive{}): reflect2.RTypeOf(&entity.JobState_Archive{}), + reflect2.RTypeOf(&entity.JobParam_Restore{}): reflect2.RTypeOf(&entity.JobState_Restore{}), + } + jobTypes = map[uintptr]JobType{ + reflect2.RTypeOf(&entity.JobState_Archive{}): new(jobTypeArchive), + reflect2.RTypeOf(&entity.JobState_Restore{}): new(jobTypeRestore), + } +) + +type JobType interface { + GetExecutor(ctx context.Context, exe *Executor, job *Job) (JobExecutor, error) +} + +type JobExecutor interface { + Initialize(ctx context.Context, param *entity.JobParam) error + Dispatch(ctx context.Context, param *entity.JobDispatchParam) error + Display(ctx context.Context) (*entity.JobDisplay, error) + Close(ctx context.Context) error + + Logger() *logrus.Logger +} diff --git a/frontend/package.json b/frontend/package.json index a300337..03c451f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,8 +10,6 @@ "gen-proto": "protoc --ts_out ./src/entity --proto_path ../entity/ `ls ../entity/*.proto` && ./src/entity/gen_index.sh" }, "dependencies": { - "@samuelncui/chonky": "^0.2.7", - "@samuelncui/chonky-icon-fontawesome": "^0.2.7", "@emotion/react": "^11.10.4", "@emotion/styled": "^11.10.4", "@fortawesome/fontawesome-svg-core": "^1.2.32", @@ -25,6 +23,8 @@ "@protobuf-ts/grpcweb-transport": "^2.8.2", "@protobuf-ts/runtime": "^2.8.2", "@protobuf-ts/runtime-rpc": "^2.8.2", + "@samuelncui/chonky": "^0.2.7", + "@samuelncui/chonky-icon-fontawesome": "^0.2.7", "fast-text-encoding": "^1.0.6", "filesize": "^10.0.5", "format-duration": "^2.0.0", @@ -37,6 +37,7 @@ "react-dom": "^18.2.0", "react-is": "^18.2.0", "react-router-dom": "^6.4.5", + "react-virtuoso": "^4.6.1", "sort-by": "^1.2.0" }, "devDependencies": { diff --git a/frontend/src/components/job-archive.tsx b/frontend/src/components/job-archive.tsx new file mode 100644 index 0000000..e9d4c43 --- /dev/null +++ b/frontend/src/components/job-archive.tsx @@ -0,0 +1,319 @@ +import { Fragment, ChangeEvent, memo, useState, useMemo, useCallback, useContext } from "react"; +import { assert } from "@protobuf-ts/runtime"; +import format from "format-duration"; + +import { Virtuoso } from "react-virtuoso"; + +import Grid from "@mui/material/Grid"; +import Box from "@mui/material/Box"; +import ListItem from "@mui/material/ListItem"; +import ListItemButton from "@mui/material/ListItemButton"; +import ListItemText from "@mui/material/ListItemText"; +import Typography from "@mui/material/Typography"; +import Button from "@mui/material/Button"; +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; +import Dialog from "@mui/material/Dialog"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import DialogContentText from "@mui/material/DialogContentText"; +import DialogTitle from "@mui/material/DialogTitle"; +import LinearProgress from "@mui/material/LinearProgress"; + +import { cli } from "../api"; +import { Job, JobDispatchRequest, CopyStatus, SourceState, JobStatus } from "../entity"; +import { JobArchiveCopyingParam, JobArchiveStep, JobArchiveDisplay, JobArchiveState } from "../entity"; + +import { formatFilesize } from "../tools"; + +import { JobCard } from "./job-card"; +import { RefreshContext } from "../pages/jobs"; + +export const ArchiveCard = ({ job, state, display }: { job: Job; state: JobArchiveState; display: JobArchiveDisplay | null }): JSX.Element => { + const [fields, progress] = useMemo(() => { + const totalFiles = state.sources.length; + let submitedFiles = 0, + submitedBytes = 0, + totalBytes = 0; + for (const file of state.sources) { + totalBytes += Number(file.size); + if (file.status !== CopyStatus.SUBMITED) { + continue; + } + submitedFiles++; + submitedBytes += Number(file.size); + } + + const copiedFiles = submitedFiles + Number(display?.copiedFiles || 0n); + const copiedBytes = submitedBytes + Number(display?.copiedBytes || 0n); + const avgSpeed = (() => { + if (!display || !display.copiedBytes || !display.startTime) { + return NaN; + } + + const duration = Date.now() / 1000 - Number(display.startTime); + if (duration <= 0) { + return NaN; + } + + return Number(display.copiedBytes) / duration; + })(); + + const progress = (totalBytes > 0 ? copiedBytes / totalBytes : 1) * 100; + const fields = [ + { name: "Current Step", value: JobArchiveStep[state.step] }, + { name: "Current Speed", value: display?.speed ? `${formatFilesize(display?.speed)}/s` : "--" }, + { name: "Average Speed", value: !isNaN(avgSpeed) ? `${formatFilesize(avgSpeed)}/s` : "--" }, + { name: "Estimated Time", value: !isNaN(avgSpeed) ? format(((totalBytes - copiedBytes) * 1000) / avgSpeed) : "--" }, + { name: "Copied Files", value: copiedFiles }, + { name: "Copied Bytes", value: formatFilesize(copiedBytes) }, + { name: "Submited Files", value: submitedFiles }, + { name: "Submited Bytes", value: formatFilesize(submitedBytes) }, + { name: "Total Files", value: totalFiles }, + { name: "Total Bytes", value: formatFilesize(totalBytes) }, + ]; + + return [fields, progress]; + }, [state, display]); + + return ( + + + + + + + {fields.map((field, idx) => ( + + + {field.name}: {field.value} + + + ))} + + } + buttons={ + + {state.step === JobArchiveStep.WAIT_FOR_TAPE && } + {job.status !== JobStatus.PROCESSING && } + + + } + /> + ); +}; + +const NewTapeDialog = ({ job }: { job: Job }) => { + const refresh = useContext(RefreshContext); + + const [devices, setDevices] = useState([]); + const [param, setParam] = useState(null); + const handleClickOpen = async () => { + const reply = await cli.deviceList({}).response; + setDevices(reply.devices); + setParam(JobArchiveCopyingParam.create()); + }; + const handleClose = () => { + setParam(null); + setDevices([]); + }; + const handleChange = (key: keyof JobArchiveCopyingParam) => (event: ChangeEvent) => { + if (param === null) { + return; + } + setParam({ ...param, [key]: event.target.value }); + }; + const handleSubmit = async () => { + if (!param) { + return; + } + + const trimedParam: JobArchiveCopyingParam = { + device: param.device, + barcode: param.barcode.toUpperCase(), + name: param.name, + }; + assert(trimedParam.barcode.length === 6); + + const req = makeArchiveCopyingParam(job.id, trimedParam); + console.log("job dispatch start, request= ", req); + + const reply = await cli.jobDispatch(req).response; + console.log("job dispatch success, reply= ", reply); + await refresh(); + handleClose(); + }; + + return ( + + + {param && ( + + Load Tape + + After load tape into tape drive, click 'Submit' + + {devices.map((device) => ( + + {device} + + ))} + + + + + + + + + + )} + + ); +}; + +const ArchiveViewFilesDialog = ({ sources }: { sources: SourceState[] }) => { + const [open, setOpen] = useState(false); + const handleClickOpen = () => { + setOpen(true); + }; + const handleClose = () => { + setOpen(false); + }; + + return ( + + + {open && ( + + View Files + + { + const src = sources[idx]; + if (!src || !src.source) { + return null; + } + + return ( + + + + ); + }} + /> + + + + + + )} + + ); +}; + +const RollbackDialog = ({ jobID, state }: { jobID: bigint; state: JobArchiveState }) => { + const refresh = useContext(RefreshContext); + + const [open, setOpen] = useState(false); + const handleClickOpen = () => { + setOpen(true); + }; + const handleClose = () => { + setOpen(false); + }; + + const handleClickItem = useCallback( + async (idx: number) => { + const found = state.sources[idx]; + if (!found || !found.source) { + return; + } + + const path = found.source.base + found.source.path.join("/"); + if (!confirm(`Rollback to file '${path}', all files after this file (included) will be set to 'PENDING'.`)) { + return; + } + + const sources = Array.from(state.sources); + for (let i = idx; i < sources.length; i++) { + sources[i].status = CopyStatus.PENDING; + } + + await cli.jobEditState({ id: jobID, state: { state: { oneofKind: "archive", archive: { ...state, sources } } } }); + alert(`Rollback to file '${path}' success!`); + await refresh(); + }, + [state, refresh], + ); + + return ( + + + {open && ( + + Click File to Rollback + + { + const src = state.sources[idx]; + if (!src || !src.source) { + return null; + } + + return ( + + handleClickItem(idx)}> + + + + ); + }} + /> + + + + + + )} + + ); +}; + +function makeArchiveCopyingParam(jobID: bigint, param: JobArchiveCopyingParam): JobDispatchRequest { + return { + id: jobID, + param: { + param: { + oneofKind: "archive", + archive: { + param: { + oneofKind: "copying", + copying: param, + }, + }, + }, + }, + }; +} diff --git a/frontend/src/components/job-card.tsx b/frontend/src/components/job-card.tsx new file mode 100644 index 0000000..d9620da --- /dev/null +++ b/frontend/src/components/job-card.tsx @@ -0,0 +1,50 @@ +import { useCallback, useContext } from "react"; + +import Typography from "@mui/material/Typography"; +import Card from "@mui/material/Card"; +import CardActions from "@mui/material/CardActions"; +import CardContent from "@mui/material/CardContent"; +import Button from "@mui/material/Button"; +import Divider from "@mui/material/Divider"; + +import { cli } from "../api"; +import { Job, JobStatus, JobDeleteRequest } from "../entity"; + +import { ViewLogDialog } from "./job-log"; +import { RefreshContext } from "../pages/jobs"; + +const DeleteJobButton = ({ jobID }: { jobID: bigint }) => { + const refresh = useContext(RefreshContext); + const deleteJob = useCallback(async () => { + await cli.jobDelete(JobDeleteRequest.create({ ids: [jobID] })); + await refresh(); + }, [jobID]); + + return ( + + ); +}; + +export const JobCard = ({ job, detail, buttons }: { job: Job; detail?: JSX.Element; buttons?: JSX.Element }) => { + return ( + + + + {`${JobStatus[job.status]}`} + + {`Job ${job.id} - ${job.state?.state.oneofKind?.toUpperCase()}`} + {detail} + + + +
{buttons}
+
+ + +
+
+
+ ); +}; diff --git a/frontend/src/components/job-log.tsx b/frontend/src/components/job-log.tsx new file mode 100644 index 0000000..96fe20f --- /dev/null +++ b/frontend/src/components/job-log.tsx @@ -0,0 +1,84 @@ +import { Fragment, useState, useRef, useEffect, useCallback } from "react"; + +import Button from "@mui/material/Button"; +import Dialog from "@mui/material/Dialog"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import DialogTitle from "@mui/material/DialogTitle"; + +import { cli } from "../api"; +import { sleep } from "../tools"; + +export const ViewLogDialog = ({ jobID }: { jobID: bigint }) => { + const [open, setOpen] = useState(false); + const handleClickOpen = () => { + setOpen(true); + }; + const handleClose = () => { + setOpen(false); + }; + + return ( + + + {open && ( + + View Log + + + + + + + + )} + + ); +}; + +const LogConsole = ({ jobId }: { jobId: bigint }) => { + const [log, setLog] = useState(""); + const [offset, setOffset] = useState(0n); + const bottom = useRef(null); + + const refresh = useCallback(async () => { + const reply = await cli.jobGetLog({ jobId, offset: offset }).response; + setLog(log + new TextDecoder().decode(reply.logs)); + setOffset(reply.offset); + }, [log, setLog, offset, setOffset, bottom]); + const refreshRef = useRef(refresh); + refreshRef.current = refresh; + + useEffect(() => { + var timer: NodeJS.Timeout; + (async () => { + await refreshRef.current(); + if (bottom.current) { + const bottomElem = bottom.current as HTMLElement; + await sleep(10); + bottomElem.scrollIntoView(true); + await sleep(10); + bottomElem.parentElement?.scrollBy(0, 100); + } + + timer = setInterval(() => refreshRef.current(), 2000); + })(); + + return () => { + if (!timer) { + return; + } + + clearInterval(timer); + }; + }, [refresh]); + + return ( + +
{log || "loading..."}
+
+ + ); +}; diff --git a/frontend/src/components/job-restore.tsx b/frontend/src/components/job-restore.tsx new file mode 100644 index 0000000..b775218 --- /dev/null +++ b/frontend/src/components/job-restore.tsx @@ -0,0 +1,271 @@ +import { Fragment, ChangeEvent, useState, useMemo, useContext } from "react"; +import format from "format-duration"; + +import Grid from "@mui/material/Grid"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import Button from "@mui/material/Button"; +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; +import Chip, { ChipProps } from "@mui/material/Chip"; +import Stack from "@mui/material/Stack"; +import Dialog from "@mui/material/Dialog"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import DialogContentText from "@mui/material/DialogContentText"; +import DialogTitle from "@mui/material/DialogTitle"; +import LinearProgress from "@mui/material/LinearProgress"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; + +import { TreeView, TreeItem } from "@mui/x-tree-view"; + +import { cli } from "../api"; +import { Job, JobDispatchRequest, CopyStatus, JobArchiveStep } from "../entity"; +import { JobRestoreCopyingParam, JobRestoreStep, JobRestoreDisplay, JobRestoreState } from "../entity"; +import { RestoreTape } from "../entity"; + +import { formatFilesize } from "../tools"; + +import { JobCard } from "./job-card"; +import { RefreshContext } from "../pages/jobs"; + +const tapeStatusToColor = (status: CopyStatus): ChipProps["color"] => { + switch (status) { + case CopyStatus.DRAFT: + return "primary"; + case CopyStatus.PENDING: + return "primary"; + case CopyStatus.RUNNING: + return "secondary"; + case CopyStatus.STAGED: + return "warning"; + case CopyStatus.SUBMITED: + return "success"; + case CopyStatus.FAILED: + return "error"; + default: + return "default"; + } +}; + +export const RestoreCard = ({ job, state, display }: { job: Job; state: JobRestoreState; display: JobRestoreDisplay | null }): JSX.Element => { + const [fields, progress] = useMemo(() => { + const totalFiles = state.tapes.reduce((count, tape) => count + tape.files.length, 0); + let successFiles = 0, + successBytes = 0, + copiedFiles = Number(display?.copiedFiles || 0n), + copiedBytes = Number(display?.copiedBytes || 0n), + totalBytes = 0; + for (const tape of state.tapes) { + for (const file of tape.files) { + totalBytes += Number(file.size); + + if (file.status === CopyStatus.SUBMITED || file.status === CopyStatus.STAGED) { + successFiles++; + successBytes += Number(file.size); + } + + if (file.status === CopyStatus.SUBMITED) { + copiedFiles++; + copiedBytes += Number(file.size); + } + } + } + + const avgSpeed = (() => { + if (!display || !display.copiedBytes || !display.startTime) { + return NaN; + } + + const duration = Date.now() / 1000 - Number(display.startTime); + if (duration <= 0) { + return NaN; + } + + return Number(display.copiedBytes) / duration; + })(); + + const progress = (totalBytes > 0 ? copiedBytes / totalBytes : 1) * 100; + const fields = [ + { name: "Current Step", value: JobArchiveStep[state.step] }, + { name: "Current Speed", value: display?.speed ? `${formatFilesize(display?.speed)}/s` : "--" }, + { name: "Average Speed", value: !isNaN(avgSpeed) ? `${formatFilesize(avgSpeed)}/s` : "--" }, + { name: "Estimated Time", value: !isNaN(avgSpeed) ? format(((totalBytes - copiedBytes) * 1000) / avgSpeed) : "--" }, + { name: "Copied Files", value: copiedFiles }, + { name: "Copied Bytes", value: formatFilesize(copiedBytes) }, + { name: "Success Files", value: successFiles }, + { name: "Success Bytes", value: formatFilesize(successBytes) }, + { name: "Total Files", value: totalFiles }, + { name: "Total Bytes", value: formatFilesize(totalBytes) }, + ]; + + return [fields, progress]; + }, [state, display]); + + return ( + + + + + + + {fields.map((field, idx) => ( + + + {field.name}: {field.value} + + + ))} + + + {state.tapes.map((tape) => ( + + ))} + + + + } + buttons={ + + {state.step === JobRestoreStep.WAIT_FOR_TAPE && } + + + } + /> + ); +}; + +const LoadTapeDialog = ({ job }: { job: Job }) => { + const refresh = useContext(RefreshContext); + + const [devices, setDevices] = useState(null); + const [device, setDevice] = useState(null); + const handleClickOpen = async () => { + const reply = await cli.deviceList({}).response; + setDevices(reply.devices); + }; + const handleClose = () => { + setDevices(null); + setDevice(null); + }; + + const handleChange = (event: ChangeEvent) => { + setDevice(event.target.value); + }; + const handleSubmit = async () => { + if (!device) { + return; + } + + const trimedParam: JobRestoreCopyingParam = { + device: device, + }; + + const req = makeRestoreCopyingParam(job.id, trimedParam); + console.log("job dispatch start, request= ", req); + + const reply = await cli.jobDispatch(req).response; + console.log("job dispatch success, reply= ", reply); + await refresh(); + handleClose(); + }; + + return ( + + + {devices && ( + + Load Tape + + After load tape into tape drive, click 'Submit' + + {devices.map((device) => ( + + {device} + + ))} + + + + + + + + )} + + ); +}; + +const RestoreViewFilesDialog = ({ tapes }: { tapes: RestoreTape[] }) => { + const [open, setOpen] = useState(false); + const handleClickOpen = () => { + setOpen(true); + }; + const handleClose = () => { + setOpen(false); + }; + const counts = useMemo(() => tapes.map((tape) => tape.files.length), [tapes]); + + return ( + + + {open && ( + + View Files + + } defaultExpandIcon={}> + {tapes.map((tape) => { + if (!tape.files) { + return null; + } + + return ( + + {tape.files.map((file) => ( + + {file.tapePath} {CopyStatus[file.status]} + + } + nodeId={`file-${file.positionId}`} + /> + ))} + + ); + })} + + + + + + + )} + + ); +}; + +function makeRestoreCopyingParam(jobID: bigint, param: JobRestoreCopyingParam): JobDispatchRequest { + return { + id: jobID, + param: { + param: { + oneofKind: "restore", + restore: { + param: { + oneofKind: "copying", + copying: param, + }, + }, + }, + }, + }; +} diff --git a/frontend/src/entity/job.ts b/frontend/src/entity/job.ts index 7c0d9ea..5bf34ae 100644 --- a/frontend/src/entity/job.ts +++ b/frontend/src/entity/job.ts @@ -13,8 +13,8 @@ import { MESSAGE_TYPE } from "@protobuf-ts/runtime"; import { MessageType } from "@protobuf-ts/runtime"; import { JobRestoreDisplay } from "./job_restore"; import { JobArchiveDisplay } from "./job_archive"; -import { JobRestoreNextParam } from "./job_restore"; -import { JobArchiveNextParam } from "./job_archive"; +import { JobRestoreDispatchParam } from "./job_restore"; +import { JobArchiveDispatchParam } from "./job_archive"; import { JobRestoreState } from "./job_restore"; import { JobArchiveState } from "./job_archive"; import { JobRestoreParam } from "./job_restore"; @@ -95,24 +95,24 @@ export interface JobState { }; } /** - * @generated from protobuf message job.JobNextParam + * @generated from protobuf message job.JobDispatchParam */ -export interface JobNextParam { +export interface JobDispatchParam { /** * @generated from protobuf oneof: param */ param: { oneofKind: "archive"; /** - * @generated from protobuf field: job_archive.JobArchiveNextParam archive = 1; + * @generated from protobuf field: job_archive.JobArchiveDispatchParam archive = 1; */ - archive: JobArchiveNextParam; + archive: JobArchiveDispatchParam; } | { oneofKind: "restore"; /** - * @generated from protobuf field: job_restore.JobRestoreNextParam restore = 2; + * @generated from protobuf field: job_restore.JobRestoreDispatchParam restore = 2; */ - restore: JobRestoreNextParam; + restore: JobRestoreDispatchParam; } | { oneofKind: undefined; }; @@ -423,35 +423,35 @@ class JobState$Type extends MessageType { */ export const JobState = new JobState$Type(); // @generated message type with reflection information, may provide speed optimized methods -class JobNextParam$Type extends MessageType { +class JobDispatchParam$Type extends MessageType { constructor() { - super("job.JobNextParam", [ - { no: 1, name: "archive", kind: "message", oneof: "param", T: () => JobArchiveNextParam }, - { no: 2, name: "restore", kind: "message", oneof: "param", T: () => JobRestoreNextParam } + super("job.JobDispatchParam", [ + { no: 1, name: "archive", kind: "message", oneof: "param", T: () => JobArchiveDispatchParam }, + { no: 2, name: "restore", kind: "message", oneof: "param", T: () => JobRestoreDispatchParam } ]); } - create(value?: PartialMessage): JobNextParam { + create(value?: PartialMessage): JobDispatchParam { const message = { param: { oneofKind: undefined } }; globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobNextParam): JobNextParam { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobDispatchParam): JobDispatchParam { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); switch (fieldNo) { - case /* job_archive.JobArchiveNextParam archive */ 1: + case /* job_archive.JobArchiveDispatchParam archive */ 1: message.param = { oneofKind: "archive", - archive: JobArchiveNextParam.internalBinaryRead(reader, reader.uint32(), options, (message.param as any).archive) + archive: JobArchiveDispatchParam.internalBinaryRead(reader, reader.uint32(), options, (message.param as any).archive) }; break; - case /* job_restore.JobRestoreNextParam restore */ 2: + case /* job_restore.JobRestoreDispatchParam restore */ 2: message.param = { oneofKind: "restore", - restore: JobRestoreNextParam.internalBinaryRead(reader, reader.uint32(), options, (message.param as any).restore) + restore: JobRestoreDispatchParam.internalBinaryRead(reader, reader.uint32(), options, (message.param as any).restore) }; break; default: @@ -465,13 +465,13 @@ class JobNextParam$Type extends MessageType { } return message; } - internalBinaryWrite(message: JobNextParam, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* job_archive.JobArchiveNextParam archive = 1; */ + internalBinaryWrite(message: JobDispatchParam, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* job_archive.JobArchiveDispatchParam archive = 1; */ if (message.param.oneofKind === "archive") - JobArchiveNextParam.internalBinaryWrite(message.param.archive, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); - /* job_restore.JobRestoreNextParam restore = 2; */ + JobArchiveDispatchParam.internalBinaryWrite(message.param.archive, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + /* job_restore.JobRestoreDispatchParam restore = 2; */ if (message.param.oneofKind === "restore") - JobRestoreNextParam.internalBinaryWrite(message.param.restore, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + JobRestoreDispatchParam.internalBinaryWrite(message.param.restore, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -479,9 +479,9 @@ class JobNextParam$Type extends MessageType { } } /** - * @generated MessageType for protobuf message job.JobNextParam + * @generated MessageType for protobuf message job.JobDispatchParam */ -export const JobNextParam = new JobNextParam$Type(); +export const JobDispatchParam = new JobDispatchParam$Type(); // @generated message type with reflection information, may provide speed optimized methods class CreatableJob$Type extends MessageType { constructor() { diff --git a/frontend/src/entity/job_archive.ts b/frontend/src/entity/job_archive.ts index 919198b..4dd9aa6 100644 --- a/frontend/src/entity/job_archive.ts +++ b/frontend/src/entity/job_archive.ts @@ -23,9 +23,9 @@ export interface JobArchiveParam { sources: Source[]; } /** - * @generated from protobuf message job_archive.JobArchiveNextParam + * @generated from protobuf message job_archive.JobArchiveDispatchParam */ -export interface JobArchiveNextParam { +export interface JobArchiveDispatchParam { /** * @generated from protobuf oneof: param */ @@ -189,22 +189,22 @@ class JobArchiveParam$Type extends MessageType { */ export const JobArchiveParam = new JobArchiveParam$Type(); // @generated message type with reflection information, may provide speed optimized methods -class JobArchiveNextParam$Type extends MessageType { +class JobArchiveDispatchParam$Type extends MessageType { constructor() { - super("job_archive.JobArchiveNextParam", [ + super("job_archive.JobArchiveDispatchParam", [ { no: 1, name: "wait_for_tape", kind: "message", oneof: "param", T: () => JobArchiveWaitForTapeParam }, { no: 2, name: "copying", kind: "message", oneof: "param", T: () => JobArchiveCopyingParam }, { no: 255, name: "finished", kind: "message", oneof: "param", T: () => JobArchiveFinishedParam } ]); } - create(value?: PartialMessage): JobArchiveNextParam { + create(value?: PartialMessage): JobArchiveDispatchParam { const message = { param: { oneofKind: undefined } }; globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobArchiveNextParam): JobArchiveNextParam { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobArchiveDispatchParam): JobArchiveDispatchParam { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -238,7 +238,7 @@ class JobArchiveNextParam$Type extends MessageType { } return message; } - internalBinaryWrite(message: JobArchiveNextParam, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: JobArchiveDispatchParam, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* job_archive.JobArchiveWaitForTapeParam wait_for_tape = 1; */ if (message.param.oneofKind === "waitForTape") JobArchiveWaitForTapeParam.internalBinaryWrite(message.param.waitForTape, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); @@ -255,9 +255,9 @@ class JobArchiveNextParam$Type extends MessageType { } } /** - * @generated MessageType for protobuf message job_archive.JobArchiveNextParam + * @generated MessageType for protobuf message job_archive.JobArchiveDispatchParam */ -export const JobArchiveNextParam = new JobArchiveNextParam$Type(); +export const JobArchiveDispatchParam = new JobArchiveDispatchParam$Type(); // @generated message type with reflection information, may provide speed optimized methods class JobArchiveWaitForTapeParam$Type extends MessageType { constructor() { diff --git a/frontend/src/entity/job_restore.ts b/frontend/src/entity/job_restore.ts index db36026..369785b 100644 --- a/frontend/src/entity/job_restore.ts +++ b/frontend/src/entity/job_restore.ts @@ -22,9 +22,9 @@ export interface JobRestoreParam { fileIds: bigint[]; } /** - * @generated from protobuf message job_restore.JobRestoreNextParam + * @generated from protobuf message job_restore.JobRestoreDispatchParam */ -export interface JobRestoreNextParam { +export interface JobRestoreDispatchParam { /** * @generated from protobuf oneof: param */ @@ -246,22 +246,22 @@ class JobRestoreParam$Type extends MessageType { */ export const JobRestoreParam = new JobRestoreParam$Type(); // @generated message type with reflection information, may provide speed optimized methods -class JobRestoreNextParam$Type extends MessageType { +class JobRestoreDispatchParam$Type extends MessageType { constructor() { - super("job_restore.JobRestoreNextParam", [ + super("job_restore.JobRestoreDispatchParam", [ { no: 1, name: "wait_for_tape", kind: "message", oneof: "param", T: () => JobRestoreWaitForTapeParam }, { no: 2, name: "copying", kind: "message", oneof: "param", T: () => JobRestoreCopyingParam }, { no: 255, name: "finished", kind: "message", oneof: "param", T: () => JobRestoreFinishedParam } ]); } - create(value?: PartialMessage): JobRestoreNextParam { + create(value?: PartialMessage): JobRestoreDispatchParam { const message = { param: { oneofKind: undefined } }; globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobRestoreNextParam): JobRestoreNextParam { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobRestoreDispatchParam): JobRestoreDispatchParam { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -295,7 +295,7 @@ class JobRestoreNextParam$Type extends MessageType { } return message; } - internalBinaryWrite(message: JobRestoreNextParam, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: JobRestoreDispatchParam, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* job_restore.JobRestoreWaitForTapeParam wait_for_tape = 1; */ if (message.param.oneofKind === "waitForTape") JobRestoreWaitForTapeParam.internalBinaryWrite(message.param.waitForTape, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); @@ -312,9 +312,9 @@ class JobRestoreNextParam$Type extends MessageType { } } /** - * @generated MessageType for protobuf message job_restore.JobRestoreNextParam + * @generated MessageType for protobuf message job_restore.JobRestoreDispatchParam */ -export const JobRestoreNextParam = new JobRestoreNextParam$Type(); +export const JobRestoreDispatchParam = new JobRestoreDispatchParam$Type(); // @generated message type with reflection information, may provide speed optimized methods class JobRestoreWaitForTapeParam$Type extends MessageType { constructor() { diff --git a/frontend/src/entity/service.client.ts b/frontend/src/entity/service.client.ts index 601f109..c56cd85 100644 --- a/frontend/src/entity/service.client.ts +++ b/frontend/src/entity/service.client.ts @@ -16,10 +16,12 @@ import type { JobGetLogReply } from "./service"; import type { JobGetLogRequest } from "./service"; import type { JobDisplayReply } from "./service"; import type { JobDisplayRequest } from "./service"; -import type { JobNextReply } from "./service"; -import type { JobNextRequest } from "./service"; +import type { JobDispatchReply } from "./service"; +import type { JobDispatchRequest } from "./service"; import type { JobDeleteReply } from "./service"; import type { JobDeleteRequest } from "./service"; +import type { JobEditStateReply } from "./service"; +import type { JobEditStateRequest } from "./service"; import type { JobCreateReply } from "./service"; import type { JobCreateRequest } from "./service"; import type { JobListReply } from "./service"; @@ -87,14 +89,18 @@ export interface IServiceClient { * @generated from protobuf rpc: JobCreate(service.JobCreateRequest) returns (service.JobCreateReply); */ jobCreate(input: JobCreateRequest, options?: RpcOptions): UnaryCall; + /** + * @generated from protobuf rpc: JobEditState(service.JobEditStateRequest) returns (service.JobEditStateReply); + */ + jobEditState(input: JobEditStateRequest, options?: RpcOptions): UnaryCall; /** * @generated from protobuf rpc: JobDelete(service.JobDeleteRequest) returns (service.JobDeleteReply); */ jobDelete(input: JobDeleteRequest, options?: RpcOptions): UnaryCall; /** - * @generated from protobuf rpc: JobNext(service.JobNextRequest) returns (service.JobNextReply); + * @generated from protobuf rpc: JobDispatch(service.JobDispatchRequest) returns (service.JobDispatchReply); */ - jobNext(input: JobNextRequest, options?: RpcOptions): UnaryCall; + jobDispatch(input: JobDispatchRequest, options?: RpcOptions): UnaryCall; /** * @generated from protobuf rpc: JobDisplay(service.JobDisplayRequest) returns (service.JobDisplayReply); */ @@ -199,60 +205,67 @@ export class ServiceClient implements IServiceClient, ServiceInfo { const method = this.methods[9], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } + /** + * @generated from protobuf rpc: JobEditState(service.JobEditStateRequest) returns (service.JobEditStateReply); + */ + jobEditState(input: JobEditStateRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[10], opt = this._transport.mergeOptions(options); + return stackIntercept("unary", this._transport, method, opt, input); + } /** * @generated from protobuf rpc: JobDelete(service.JobDeleteRequest) returns (service.JobDeleteReply); */ jobDelete(input: JobDeleteRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[10], opt = this._transport.mergeOptions(options); + const method = this.methods[11], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** - * @generated from protobuf rpc: JobNext(service.JobNextRequest) returns (service.JobNextReply); + * @generated from protobuf rpc: JobDispatch(service.JobDispatchRequest) returns (service.JobDispatchReply); */ - jobNext(input: JobNextRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[11], opt = this._transport.mergeOptions(options); - return stackIntercept("unary", this._transport, method, opt, input); + jobDispatch(input: JobDispatchRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[12], opt = this._transport.mergeOptions(options); + return stackIntercept("unary", this._transport, method, opt, input); } /** * @generated from protobuf rpc: JobDisplay(service.JobDisplayRequest) returns (service.JobDisplayReply); */ jobDisplay(input: JobDisplayRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[12], opt = this._transport.mergeOptions(options); + const method = this.methods[13], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** * @generated from protobuf rpc: JobGetLog(service.JobGetLogRequest) returns (service.JobGetLogReply); */ jobGetLog(input: JobGetLogRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[13], opt = this._transport.mergeOptions(options); + const method = this.methods[14], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** * @generated from protobuf rpc: SourceList(service.SourceListRequest) returns (service.SourceListReply); */ sourceList(input: SourceListRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[14], opt = this._transport.mergeOptions(options); + const method = this.methods[15], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** * @generated from protobuf rpc: DeviceList(service.DeviceListRequest) returns (service.DeviceListReply); */ deviceList(input: DeviceListRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[15], opt = this._transport.mergeOptions(options); + const method = this.methods[16], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** * @generated from protobuf rpc: LibraryExport(service.LibraryExportRequest) returns (service.LibraryExportReply); */ libraryExport(input: LibraryExportRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[16], opt = this._transport.mergeOptions(options); + const method = this.methods[17], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** * @generated from protobuf rpc: LibraryTrim(service.LibraryTrimRequest) returns (service.LibraryTrimReply); */ libraryTrim(input: LibraryTrimRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[17], opt = this._transport.mergeOptions(options); + const method = this.methods[18], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } } diff --git a/frontend/src/entity/service.ts b/frontend/src/entity/service.ts index 11abbed..79dfa52 100644 --- a/frontend/src/entity/service.ts +++ b/frontend/src/entity/service.ts @@ -15,7 +15,9 @@ import { MessageType } from "@protobuf-ts/runtime"; import { LibraryEntityType } from "./library_entity_type"; import { SourceFile } from "./source"; import { JobDisplay } from "./job"; -import { JobNextParam } from "./job"; +import { JobDispatchParam } from "./job"; +import { JobState } from "./job"; +import { JobStatus } from "./job"; import { CreatableJob } from "./job"; import { Job } from "./job"; import { JobRecentlyUpdateFilter } from "./job"; @@ -269,6 +271,28 @@ export interface JobCreateReply { */ job?: Job; } +/** + * @generated from protobuf message service.JobEditStateRequest + */ +export interface JobEditStateRequest { + /** + * @generated from protobuf field: int64 id = 1; + */ + id: bigint; + /** + * @generated from protobuf field: optional job.JobStatus status = 2; + */ + status?: JobStatus; + /** + * @generated from protobuf field: optional job.JobState state = 3; + */ + state?: JobState; +} +/** + * @generated from protobuf message service.JobEditStateReply + */ +export interface JobEditStateReply { +} /** * @generated from protobuf message service.JobDeleteRequest */ @@ -284,26 +308,22 @@ export interface JobDeleteRequest { export interface JobDeleteReply { } /** - * @generated from protobuf message service.JobNextRequest + * @generated from protobuf message service.JobDispatchRequest */ -export interface JobNextRequest { +export interface JobDispatchRequest { /** * @generated from protobuf field: int64 id = 1; */ id: bigint; /** - * @generated from protobuf field: job.JobNextParam param = 2; + * @generated from protobuf field: job.JobDispatchParam param = 2; */ - param?: JobNextParam; + param?: JobDispatchParam; } /** - * @generated from protobuf message service.JobNextReply + * @generated from protobuf message service.JobDispatchReply */ -export interface JobNextReply { - /** - * @generated from protobuf field: job.Job job = 1; - */ - job?: Job; +export interface JobDispatchReply { } /** * @generated from protobuf message service.JobDisplayRequest @@ -1521,6 +1541,93 @@ class JobCreateReply$Type extends MessageType { */ export const JobCreateReply = new JobCreateReply$Type(); // @generated message type with reflection information, may provide speed optimized methods +class JobEditStateRequest$Type extends MessageType { + constructor() { + super("service.JobEditStateRequest", [ + { no: 1, name: "id", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ }, + { no: 2, name: "status", kind: "enum", opt: true, T: () => ["job.JobStatus", JobStatus] }, + { no: 3, name: "state", kind: "message", T: () => JobState } + ]); + } + create(value?: PartialMessage): JobEditStateRequest { + const message = { id: 0n }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobEditStateRequest): JobEditStateRequest { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* int64 id */ 1: + message.id = reader.int64().toBigInt(); + break; + case /* optional job.JobStatus status */ 2: + message.status = reader.int32(); + break; + case /* optional job.JobState state */ 3: + message.state = JobState.internalBinaryRead(reader, reader.uint32(), options, message.state); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: JobEditStateRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* int64 id = 1; */ + if (message.id !== 0n) + writer.tag(1, WireType.Varint).int64(message.id); + /* optional job.JobStatus status = 2; */ + if (message.status !== undefined) + writer.tag(2, WireType.Varint).int32(message.status); + /* optional job.JobState state = 3; */ + if (message.state) + JobState.internalBinaryWrite(message.state, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message service.JobEditStateRequest + */ +export const JobEditStateRequest = new JobEditStateRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class JobEditStateReply$Type extends MessageType { + constructor() { + super("service.JobEditStateReply", []); + } + create(value?: PartialMessage): JobEditStateReply { + const message = {}; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobEditStateReply): JobEditStateReply { + return target ?? this.create(); + } + internalBinaryWrite(message: JobEditStateReply, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message service.JobEditStateReply + */ +export const JobEditStateReply = new JobEditStateReply$Type(); +// @generated message type with reflection information, may provide speed optimized methods class JobDeleteRequest$Type extends MessageType { constructor() { super("service.JobDeleteRequest", [ @@ -1602,21 +1709,21 @@ class JobDeleteReply$Type extends MessageType { */ export const JobDeleteReply = new JobDeleteReply$Type(); // @generated message type with reflection information, may provide speed optimized methods -class JobNextRequest$Type extends MessageType { +class JobDispatchRequest$Type extends MessageType { constructor() { - super("service.JobNextRequest", [ + super("service.JobDispatchRequest", [ { no: 1, name: "id", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ }, - { no: 2, name: "param", kind: "message", T: () => JobNextParam } + { no: 2, name: "param", kind: "message", T: () => JobDispatchParam } ]); } - create(value?: PartialMessage): JobNextRequest { + create(value?: PartialMessage): JobDispatchRequest { const message = { id: 0n }; globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobNextRequest): JobNextRequest { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobDispatchRequest): JobDispatchRequest { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1624,8 +1731,8 @@ class JobNextRequest$Type extends MessageType { case /* int64 id */ 1: message.id = reader.int64().toBigInt(); break; - case /* job.JobNextParam param */ 2: - message.param = JobNextParam.internalBinaryRead(reader, reader.uint32(), options, message.param); + case /* job.JobDispatchParam param */ 2: + message.param = JobDispatchParam.internalBinaryRead(reader, reader.uint32(), options, message.param); break; default: let u = options.readUnknownField; @@ -1638,13 +1745,13 @@ class JobNextRequest$Type extends MessageType { } return message; } - internalBinaryWrite(message: JobNextRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: JobDispatchRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* int64 id = 1; */ if (message.id !== 0n) writer.tag(1, WireType.Varint).int64(message.id); - /* job.JobNextParam param = 2; */ + /* job.JobDispatchParam param = 2; */ if (message.param) - JobNextParam.internalBinaryWrite(message.param, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + JobDispatchParam.internalBinaryWrite(message.param, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1652,46 +1759,25 @@ class JobNextRequest$Type extends MessageType { } } /** - * @generated MessageType for protobuf message service.JobNextRequest + * @generated MessageType for protobuf message service.JobDispatchRequest */ -export const JobNextRequest = new JobNextRequest$Type(); +export const JobDispatchRequest = new JobDispatchRequest$Type(); // @generated message type with reflection information, may provide speed optimized methods -class JobNextReply$Type extends MessageType { +class JobDispatchReply$Type extends MessageType { constructor() { - super("service.JobNextReply", [ - { no: 1, name: "job", kind: "message", T: () => Job } - ]); + super("service.JobDispatchReply", []); } - create(value?: PartialMessage): JobNextReply { + create(value?: PartialMessage): JobDispatchReply { const message = {}; globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobNextReply): JobNextReply { - let message = target ?? this.create(), end = reader.pos + length; - while (reader.pos < end) { - let [fieldNo, wireType] = reader.tag(); - switch (fieldNo) { - case /* job.Job job */ 1: - message.job = Job.internalBinaryRead(reader, reader.uint32(), options, message.job); - break; - default: - let u = options.readUnknownField; - if (u === "throw") - throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); - let d = reader.skip(wireType); - if (u !== false) - (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); - } - } - return message; + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: JobDispatchReply): JobDispatchReply { + return target ?? this.create(); } - internalBinaryWrite(message: JobNextReply, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* job.Job job = 1; */ - if (message.job) - Job.internalBinaryWrite(message.job, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + internalBinaryWrite(message: JobDispatchReply, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1699,9 +1785,9 @@ class JobNextReply$Type extends MessageType { } } /** - * @generated MessageType for protobuf message service.JobNextReply + * @generated MessageType for protobuf message service.JobDispatchReply */ -export const JobNextReply = new JobNextReply$Type(); +export const JobDispatchReply = new JobDispatchReply$Type(); // @generated message type with reflection information, may provide speed optimized methods class JobDisplayRequest$Type extends MessageType { constructor() { @@ -2281,8 +2367,9 @@ export const Service = new ServiceType("service.Service", [ { name: "TapeGetPositions", options: {}, I: TapeGetPositionsRequest, O: TapeGetPositionsReply }, { name: "JobList", options: {}, I: JobListRequest, O: JobListReply }, { name: "JobCreate", options: {}, I: JobCreateRequest, O: JobCreateReply }, + { name: "JobEditState", options: {}, I: JobEditStateRequest, O: JobEditStateReply }, { name: "JobDelete", options: {}, I: JobDeleteRequest, O: JobDeleteReply }, - { name: "JobNext", options: {}, I: JobNextRequest, O: JobNextReply }, + { name: "JobDispatch", options: {}, I: JobDispatchRequest, O: JobDispatchReply }, { name: "JobDisplay", options: {}, I: JobDisplayRequest, O: JobDisplayReply }, { name: "JobGetLog", options: {}, I: JobGetLogRequest, O: JobGetLogReply }, { name: "SourceList", options: {}, I: SourceListRequest, O: SourceListReply }, diff --git a/frontend/src/pages/jobs.tsx b/frontend/src/pages/jobs.tsx index 2597eec..224f740 100644 --- a/frontend/src/pages/jobs.tsx +++ b/frontend/src/pages/jobs.tsx @@ -1,56 +1,32 @@ import { Fragment, ChangeEvent } from "react"; -import { useState, useRef, useEffect, useMemo, useCallback, FC } from "react"; -import { createContext, useContext } from "react"; -import { assert } from "@protobuf-ts/runtime"; -import format from "format-duration"; +import { useState, useRef, useEffect, useCallback } from "react"; +import { createContext } from "react"; import Grid from "@mui/material/Grid"; import Box from "@mui/material/Box"; import List from "@mui/material/List"; import ListItemButton from "@mui/material/ListItemButton"; import ListItemText from "@mui/material/ListItemText"; -import Typography from "@mui/material/Typography"; - -import Card from "@mui/material/Card"; -import CardActions from "@mui/material/CardActions"; -import CardContent from "@mui/material/CardContent"; - import Button from "@mui/material/Button"; -import TextField from "@mui/material/TextField"; -import MenuItem from "@mui/material/MenuItem"; -import Chip, { ChipProps } from "@mui/material/Chip"; -import Stack from "@mui/material/Stack"; - import Dialog from "@mui/material/Dialog"; import DialogActions from "@mui/material/DialogActions"; -import DialogContent from "@mui/material/DialogContent"; -import DialogContentText from "@mui/material/DialogContentText"; import DialogTitle from "@mui/material/DialogTitle"; +import DialogContent from "@mui/material/DialogContent"; import LinearProgress from "@mui/material/LinearProgress"; -import Divider from "@mui/material/Divider"; - -import { TreeView, TreeItem } from "@mui/x-tree-view"; - -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import { cli, fileBase, JOB_STATUS_VISIBLE } from "../api"; -import { sleep } from "../tools"; -import { Job, JobDisplay, JobListRequest, JobNextRequest, JobStatus, CopyStatus, LibraryEntityType, JobDeleteRequest } from "../entity"; +import { Job, JobDisplay, JobListRequest, JobStatus, LibraryEntityType } from "../entity"; -import { JobArchiveCopyingParam, JobArchiveStep, JobArchiveDisplay, JobArchiveState } from "../entity"; -import { SourceState } from "../entity"; +import { download } from "../tools"; -import { JobRestoreCopyingParam, JobRestoreStep, JobRestoreDisplay, JobRestoreState } from "../entity"; -import { RestoreTape } from "../entity"; - -import { formatFilesize, download } from "../tools"; -import { Nullable } from "tsdef"; +import { JobCard } from "../components/job-card"; +import { ArchiveCard } from "../components/job-archive"; +import { RestoreCard } from "../components/job-restore"; export const JobsType = "jobs"; -type DisplayableJob = Job & Partial; +export const RefreshContext = createContext<() => Promise>(async () => {}); -const RefreshContext = createContext<() => Promise>(async () => {}); +type DisplayableJob = Job & Partial; export const JobsBrowser = () => { const [jobs, setJobs] = useState(null); @@ -58,7 +34,7 @@ export const JobsBrowser = () => { const refresh = useCallback( async (refresh?: boolean) => { - const results = await (async () => { + const [results, updated] = await (async () => { const req: JobListRequest = refresh ? { param: { oneofKind: "list", list: {} } } : { param: { oneofKind: "recentlyUpdate", recentlyUpdate: { updateSinceNs: latestUpdateTimeNs } } }; @@ -66,9 +42,9 @@ export const JobsBrowser = () => { const reply = await cli.jobList(req).response; if (reply.jobs.length === 0) { if (refresh) { - return []; + return [[], true]; } - return Array.from(jobs || []); + return [Array.from(jobs || []), false]; } const latest = reply.jobs.reduce((latest, job) => { @@ -93,25 +69,28 @@ export const JobsBrowser = () => { results.push(job); } - return results.filter((job) => job && job.status < JOB_STATUS_VISIBLE).sort((a, b) => Number(b.createTimeNs - a.createTimeNs)); + return [results.filter((job) => job && job.status < JOB_STATUS_VISIBLE).sort((a, b) => Number(b.createTimeNs - a.createTimeNs)), true]; })(); const displays = new Map(); + const processingJobs = results.filter((job) => job.status === JobStatus.PROCESSING); + if (processingJobs.length === 0) { + if (updated) { + setJobs(results); + } + return; + } + for (const reply of await Promise.all( - results - .filter((job) => job.status === JobStatus.PROCESSING) - .map((job) => cli.jobDisplay({ id: job.id }).response.then((reply) => ({ ...reply, jobID: job.id }))), + processingJobs.map((job) => cli.jobDisplay({ id: job.id }).response.then((reply) => ({ ...reply, jobID: job.id }))), )) { if (!reply.display) { continue; } - displays.set(reply.jobID, reply.display); } - const targets = results.map((job) => ({ ...job, ...displays.get(job.id) })); - console.log("refresh jobs list, set jobs, jobs=", targets); - setJobs(targets); + setJobs(results.map((job) => ({ ...job, ...displays.get(job.id) }))); }, [jobs, setJobs, latestUpdateTimeNs, setLatestUpdateTimeNs], ); @@ -133,6 +112,7 @@ export const JobsBrowser = () => { clearInterval(timer); }; }, []); + useEffect(() => console.log("jobs changed,", jobs), [jobs]); return ( @@ -153,20 +133,19 @@ export const JobsBrowser = () => { // // } > - {/* */} { const resp = await cli.libraryExport({ types: [LibraryEntityType.FILE, LibraryEntityType.TAPE, LibraryEntityType.POSITION] }).response; - download(resp.json, "database.json", "application/json"); + download(resp.json, "library.json", "application/json"); }} > - + -
{jobs ? jobs.map((job) => ) : }
+
{jobs ? jobs.map((job) => ) : }
@@ -211,11 +190,11 @@ const ImportDatabaseDialog = () => { return ( - + {open && ( - Load Tape + Upload Library JSON - {open && ( - - View Files - - {sources.map((src) => { - if (!src.source) { - return null; - } - return ( - - ); - })} - - - - - - )} - - ); -}; - -const ArchiveCard = ({ - job, - state, - display, - refresh, -}: { - job: Job; - state: JobArchiveState; - display: JobArchiveDisplay | null; - refresh: () => Promise; -}): JSX.Element => { - const [fields, progress] = useMemo(() => { - const totalFiles = state.sources.length; - let submitedFiles = 0, - submitedBytes = 0, - totalBytes = 0; - for (const file of state.sources) { - totalBytes += Number(file.size); - if (file.status !== CopyStatus.SUBMITED) { - continue; - } - submitedFiles++; - submitedBytes += Number(file.size); - } - - const copiedFiles = submitedFiles + Number(display?.copiedFiles || 0n); - const copiedBytes = submitedBytes + Number(display?.copiedBytes || 0n); - const avgSpeed = (() => { - if (!display || !display.copiedBytes || !display.startTime) { - return NaN; - } - - const duration = Date.now() / 1000 - Number(display.startTime); - if (duration <= 0) { - return NaN; - } - - return Number(display.copiedBytes) / duration; - })(); - - const progress = (totalBytes > 0 ? copiedBytes / totalBytes : 1) * 100; - const fields = [ - { name: "Current Step", value: JobArchiveStep[state.step] }, - { name: "Current Speed", value: display?.speed ? `${formatFilesize(display?.speed)}/s` : "--" }, - { name: "Average Speed", value: !isNaN(avgSpeed) ? `${formatFilesize(avgSpeed)}/s` : "--" }, - { name: "Estimated Time", value: !isNaN(avgSpeed) ? format(((totalBytes - copiedBytes) * 1000) / avgSpeed) : "--" }, - { name: "Copied Files", value: copiedFiles }, - { name: "Copied Bytes", value: formatFilesize(copiedBytes) }, - { name: "Submited Files", value: submitedFiles }, - { name: "Submited Bytes", value: formatFilesize(submitedBytes) }, - { name: "Total Files", value: totalFiles }, - { name: "Total Bytes", value: formatFilesize(totalBytes) }, - ]; - - return [fields, progress]; - }, [state, display]); - - return ( - - - - - - - {fields.map((field, idx) => ( - - - {field.name}: {field.value} - - - ))} - - } - buttons={ - - {state.step === JobArchiveStep.WAIT_FOR_TAPE && } - - - - } - /> - ); -}; - -const RestoreViewFilesDialog = ({ tapes }: { tapes: RestoreTape[] }) => { - const [open, setOpen] = useState(false); - const handleClickOpen = () => { - setOpen(true); - }; - const handleClose = () => { - setOpen(false); - }; - - return ( - - - {open && ( - - View Files - - } defaultExpandIcon={}> - {tapes.map((tape) => { - if (!tape.files) { - return null; - } - - return ( - - {tape.files.map((file) => ( - - {file.tapePath} {CopyStatus[file.status]} - - } - nodeId={`file-${file.positionId}`} - /> - ))} - - ); - })} - - - - - - - )} - - ); -}; - -const tapeStatusToColor = (status: CopyStatus): ChipProps["color"] => { - switch (status) { - case CopyStatus.DRAFT: - return "primary"; - case CopyStatus.PENDING: - return "primary"; - case CopyStatus.RUNNING: - return "secondary"; - case CopyStatus.STAGED: - return "warning"; - case CopyStatus.SUBMITED: - return "success"; - case CopyStatus.FAILED: - return "error"; - default: - return "default"; - } -}; - -const RestoreCard = ({ - job, - state, - display, - refresh, -}: { - job: Job; - state: JobRestoreState; - display: JobRestoreDisplay | null; - refresh: () => Promise; -}): JSX.Element => { - const [fields, progress] = useMemo(() => { - const totalFiles = state.tapes.reduce((count, tape) => count + tape.files.length, 0); - let successFiles = 0, - successBytes = 0, - copiedFiles = Number(display?.copiedFiles || 0n), - copiedBytes = Number(display?.copiedBytes || 0n), - totalBytes = 0; - for (const tape of state.tapes) { - for (const file of tape.files) { - totalBytes += Number(file.size); - - if (file.status === CopyStatus.SUBMITED || file.status === CopyStatus.STAGED) { - successFiles++; - successBytes += Number(file.size); - } - - if (file.status === CopyStatus.SUBMITED) { - copiedFiles++; - copiedBytes += Number(file.size); - } - } - } - - const avgSpeed = (() => { - if (!display || !display.copiedBytes || !display.startTime) { - return NaN; - } - - const duration = Date.now() / 1000 - Number(display.startTime); - if (duration <= 0) { - return NaN; - } - - return Number(display.copiedBytes) / duration; - })(); - - const progress = (totalBytes > 0 ? copiedBytes / totalBytes : 1) * 100; - const fields = [ - { name: "Current Step", value: JobArchiveStep[state.step] }, - { name: "Current Speed", value: display?.speed ? `${formatFilesize(display?.speed)}/s` : "--" }, - { name: "Average Speed", value: !isNaN(avgSpeed) ? `${formatFilesize(avgSpeed)}/s` : "--" }, - { name: "Estimated Time", value: !isNaN(avgSpeed) ? format(((totalBytes - copiedBytes) * 1000) / avgSpeed) : "--" }, - { name: "Copied Files", value: copiedFiles }, - { name: "Copied Bytes", value: formatFilesize(copiedBytes) }, - { name: "Success Files", value: successFiles }, - { name: "Success Bytes", value: formatFilesize(successBytes) }, - { name: "Total Files", value: totalFiles }, - { name: "Total Bytes", value: formatFilesize(totalBytes) }, - ]; - - return [fields, progress]; - }, [state, display]); - - return ( - - - - - - - {fields.map((field, idx) => ( - - - {field.name}: {field.value} - - - ))} - - - {state.tapes.map((tape) => ( - - ))} - - - - } - buttons={ - - {state.step === JobRestoreStep.WAIT_FOR_TAPE && } - - - - } - /> - ); -}; - -const NewTapeDialog = ({ job, refresh }: { job: Job; refresh: () => Promise }) => { - const [devices, setDevices] = useState([]); - const [param, setParam] = useState(null); - const handleClickOpen = async () => { - const reply = await cli.deviceList({}).response; - setDevices(reply.devices); - setParam(JobArchiveCopyingParam.create()); - }; - const handleClose = () => { - setParam(null); - setDevices([]); - }; - const handleChange = (key: keyof JobArchiveCopyingParam) => (event: ChangeEvent) => { - if (param === null) { - return; - } - setParam({ ...param, [key]: event.target.value }); - }; - const handleSubmit = async () => { - if (!param) { - return; - } - - const trimedParam: JobArchiveCopyingParam = { - device: param.device, - barcode: param.barcode.toUpperCase(), - name: param.name, - }; - assert(trimedParam.barcode.length === 6); - - const reply = await cli.jobNext(makeArchiveCopyingParam(job.id, trimedParam)).response; - console.log("job next reply= ", reply); - await refresh(); - handleClose(); - }; - - return ( - - - {param && ( - - Load Tape - - After load tape into tape drive, click 'Submit' - - {devices.map((device) => ( - - {device} - - ))} - - - - - - - - - - )} - - ); -}; - -const LoadTapeDialog = ({ job, refresh }: { job: Job; refresh: () => Promise }) => { - const [devices, setDevices] = useState(null); - const [device, setDevice] = useState(null); - const handleClickOpen = async () => { - const reply = await cli.deviceList({}).response; - setDevices(reply.devices); - }; - const handleClose = () => { - setDevices(null); - setDevice(null); - }; - - const handleChange = (event: ChangeEvent) => { - setDevice(event.target.value); - }; - const handleSubmit = async () => { - if (!device) { - return; - } - - const trimedParam: JobRestoreCopyingParam = { - device: device, - }; - - const reply = await cli.jobNext(makeRestoreCopyingParam(job.id, trimedParam)).response; - console.log("job next reply= ", reply); - await refresh(); - handleClose(); - }; - - return ( - - - {devices && ( - - Load Tape - - After load tape into tape drive, click 'Submit' - - {devices.map((device) => ( - - {device} - - ))} - - - - - - - - )} - - ); -}; - -const ViewLogDialog = ({ jobID }: { jobID: bigint }) => { - const [open, setOpen] = useState(false); - const handleClickOpen = () => { - setOpen(true); - }; - const handleClose = () => { - setOpen(false); - }; - - return ( - - - {open && ( - - View Log - - - - - - - - )} - - ); -}; - -const LogConsole = ({ jobId }: { jobId: bigint }) => { - const [log, setLog] = useState(""); - const [offset, setOffset] = useState(0n); - const bottom = useRef(null); - - const refresh = useCallback(async () => { - const reply = await cli.jobGetLog({ jobId, offset: offset }).response; - setLog(log + new TextDecoder().decode(reply.logs)); - setOffset(reply.offset); - }, [log, setLog, offset, setOffset, bottom]); - const refreshRef = useRef(refresh); - refreshRef.current = refresh; - - useEffect(() => { - var timer: NodeJS.Timeout; - (async () => { - await refreshRef.current(); - if (bottom.current) { - const bottomElem = bottom.current as HTMLElement; - await sleep(10); - bottomElem.scrollIntoView(true); - await sleep(10); - bottomElem.parentElement?.scrollBy(0, 100); - } - - timer = setInterval(() => refreshRef.current(), 2000); - })(); - - return () => { - if (!timer) { - return; - } - - clearInterval(timer); - }; - }, [refresh]); - - return ( - -
{log || "loading..."}
-
- - ); -}; - -const DeleteJobButton = ({ jobID }: { jobID: bigint }) => { - const refresh = useContext(RefreshContext); - const deleteJob = useCallback(async () => { - await cli.jobDelete(JobDeleteRequest.create({ ids: [jobID] })); - await refresh(); - }, [jobID]); - - return ( - - ); -}; - -const JobCard = ({ job, detail, buttons }: { job: Job; detail?: JSX.Element; buttons?: JSX.Element }) => { - return ( - - - - {`${JobStatus[job.status]}`} - - {`Job ${job.id} - ${job.state?.state.oneofKind?.toUpperCase()}`} - {detail} - - - - {buttons} - - - - ); -}; - -function makeArchiveCopyingParam(jobID: bigint, param: JobArchiveCopyingParam): JobNextRequest { - return { - id: jobID, - param: { - param: { - oneofKind: "archive", - archive: { - param: { - oneofKind: "copying", - copying: param, - }, - }, - }, - }, - }; -} - -function makeRestoreCopyingParam(jobID: bigint, param: JobRestoreCopyingParam): JobNextRequest { - return { - id: jobID, - param: { - param: { - oneofKind: "restore", - restore: { - param: { - oneofKind: "copying", - copying: param, - }, - }, - }, - }, - }; -} diff --git a/library/position.go b/library/position.go index 96aea2d..3a8f93e 100644 --- a/library/position.go +++ b/library/position.go @@ -65,7 +65,7 @@ func (l *Library) ListPositions(ctx context.Context, tapeID int64, prefix string return nil, fmt.Errorf("find position by file id fail, %w", r.Error) } - convertPath := tools.Cache(func(p string) string { return strings.ReplaceAll(p, "/", "\x00") }) + convertPath := tools.ThreadUnsafeCache(func(p string) string { return strings.ReplaceAll(p, "/", "\x00") }) sort.Slice(positions, func(i int, j int) bool { return convertPath(positions[i].Path) < convertPath(positions[j].Path) }) diff --git a/tools/cache.go b/tools/cache.go index b895d4e..c7b95dd 100644 --- a/tools/cache.go +++ b/tools/cache.go @@ -1,6 +1,9 @@ package tools -import "sync" +import ( + "context" + "sync" +) func Cache[K comparable, V any](f func(in K) V) func(in K) V { cache := new(sync.Map) @@ -15,3 +18,66 @@ func Cache[K comparable, V any](f func(in K) V) func(in K) V { return out } } + +func ThreadUnsafeCache[K comparable, V any](f func(in K) V) func(in K) V { + cache := make(map[K]V, 32) + return func(in K) V { + cached, has := cache[in] + if has { + return cached + } + + out := f(in) + cache[in] = out + return out + } +} + +type CacheOnce[K comparable, V any] struct { + cached sync.Map + getter func(context.Context, K) (V, error) +} + +type cacheOnceItem[V any] struct { + once sync.Once + value V +} + +func NewCacheOnce[K comparable, V any](getter func(context.Context, K) (V, error)) *CacheOnce[K, V] { + return &CacheOnce[K, V]{getter: getter} +} + +func (c *CacheOnce[K, V]) Get(ctx context.Context, req K) (V, error) { + if v, have := c.cached.Load(req); have { + item, ok := v.(*cacheOnceItem[V]) + if ok { + return c.get(ctx, item, req) + } + } + + v, _ := c.cached.LoadOrStore(req, &cacheOnceItem[V]{}) + return c.get(ctx, v.(*cacheOnceItem[V]), req) +} + +func (c *CacheOnce[K, V]) Remove(req K) { + c.cached.Delete(req) +} + +func (c *CacheOnce[K, V]) get(ctx context.Context, item *cacheOnceItem[V], key K) (V, error) { + var err error + item.once.Do(func() { + v, e := c.getter(ctx, key) + if e != nil { + err = e + return + } + item.value = v + }) + + if err != nil { + var v V + return v, err + } + + return item.value, nil +}