2023-06-20 13:33:05 +00:00
package object
import (
2024-05-07 09:12:34 +00:00
"bytes"
"cmp"
2023-06-20 13:33:05 +00:00
"context"
"crypto/ecdsa"
2023-07-07 15:52:32 +00:00
"encoding/hex"
2024-05-07 09:12:34 +00:00
"encoding/json"
2023-06-20 13:33:05 +00:00
"errors"
"fmt"
2024-05-07 09:12:34 +00:00
"slices"
2023-06-20 13:33:05 +00:00
"sync"
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
2024-05-03 11:52:28 +00:00
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/policy"
2023-06-20 13:33:05 +00:00
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object_manager/placement"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
)
const (
verifyPresenceAllFlag = "verify-presence-all"
)
2024-03-11 14:55:50 +00:00
var errNoAvailableEndpoint = errors . New ( "failed to create client: no available endpoint" )
2024-05-03 11:52:28 +00:00
type phyObject struct {
containerID cid . ID
objectID oid . ID
storedOnAllContainerNodes bool
ecHeader * ecHeader
}
type ecHeader struct {
index uint32
parent oid . ID
2023-06-20 13:33:05 +00:00
}
2024-05-06 12:52:41 +00:00
type objectPlacement struct {
requiredNodes [ ] netmapSDK . NodeInfo
confirmedNodes [ ] netmapSDK . NodeInfo
}
2024-05-07 09:12:34 +00:00
type objectNodesResult struct {
errors [ ] error
placements map [ oid . ID ] objectPlacement
}
type ObjNodesDataObject struct {
ObjectID string ` json:"object_id" `
RequiredNodes [ ] string ` json:"required_nodes,omitempty" `
ConfirmedNodes [ ] string ` json:"confirmed_nodes,omitempty" `
ECParentObjectID * string ` json:"ec_parent_object_id,omitempty" `
ECIndex * uint32 ` json:"ec_index,omitempty" `
}
type objNodesResultJSON struct {
ObjectID string ` json:"object_id" `
DataObjects [ ] ObjNodesDataObject ` json:"data_objects,omitempty" `
Errors [ ] string ` json:"errors,omitempty" `
}
2023-06-20 13:33:05 +00:00
var objectNodesCmd = & cobra . Command {
Use : "nodes" ,
Short : "List of nodes where the object is stored" ,
Long : ` List of nodes where the object should be stored and where it is actually stored .
Lock objects must exist on all nodes of the container .
2024-05-03 11:52:28 +00:00
For complex and EC objects , a node is considered to store an object if the node stores at least one part of the complex object or one chunk of the EC object .
2023-06-20 13:33:05 +00:00
By default , the actual storage of the object is checked only on the nodes that should store the object . To check all nodes , use the flag -- verify - presence - all . ` ,
Run : objectNodes ,
}
func initObjectNodesCmd ( ) {
commonflags . Init ( objectNodesCmd )
flags := objectNodesCmd . Flags ( )
flags . String ( commonflags . CIDFlag , "" , commonflags . CIDFlagUsage )
_ = objectGetCmd . MarkFlagRequired ( commonflags . CIDFlag )
flags . String ( commonflags . OIDFlag , "" , commonflags . OIDFlagUsage )
_ = objectGetCmd . MarkFlagRequired ( commonflags . OIDFlag )
2024-05-07 09:12:34 +00:00
flags . Bool ( verifyPresenceAllFlag , false , "Verify the actual presence of the object on all netmap nodes." )
flags . Bool ( commonflags . JSON , false , "Print information about the object placement as json." )
2023-06-20 13:33:05 +00:00
}
func objectNodes ( cmd * cobra . Command , _ [ ] string ) {
var cnrID cid . ID
var objID oid . ID
readObjectAddress ( cmd , & cnrID , & objID )
pk := key . GetOrGenerate ( cmd )
cli := internalclient . GetSDKClientByFlag ( cmd , pk , commonflags . RPC )
2024-05-03 11:52:28 +00:00
objects := getPhyObjects ( cmd , cnrID , objID , cli , pk )
2023-06-20 13:33:05 +00:00
placementPolicy , netmap := getPlacementPolicyAndNetmap ( cmd , cnrID , cli )
2024-05-07 09:12:34 +00:00
result := getRequiredPlacement ( cmd , objects , placementPolicy , netmap )
2023-06-20 13:33:05 +00:00
2024-05-07 09:12:34 +00:00
getActualPlacement ( cmd , netmap , pk , objects , result )
2023-06-20 13:33:05 +00:00
2024-05-07 09:12:34 +00:00
printPlacement ( cmd , objID , objects , result )
2023-06-20 13:33:05 +00:00
}
2024-05-03 11:52:28 +00:00
func getPhyObjects ( cmd * cobra . Command , cnrID cid . ID , objID oid . ID , cli * client . Client , pk * ecdsa . PrivateKey ) [ ] phyObject {
2023-06-20 13:33:05 +00:00
var addrObj oid . Address
addrObj . SetContainer ( cnrID )
addrObj . SetObject ( objID )
var prmHead internalclient . HeadObjectPrm
prmHead . SetClient ( cli )
prmHead . SetAddress ( addrObj )
prmHead . SetRawFlag ( true )
Prepare ( cmd , & prmHead )
readSession ( cmd , & prmHead , pk , cnrID , objID )
res , err := internalclient . HeadObject ( cmd . Context ( ) , prmHead )
if err == nil {
2024-05-03 11:52:28 +00:00
obj := phyObject {
2024-05-06 09:37:00 +00:00
containerID : cnrID ,
objectID : objID ,
storedOnAllContainerNodes : res . Header ( ) . Type ( ) == objectSDK . TypeLock ||
res . Header ( ) . Type ( ) == objectSDK . TypeTombstone ||
len ( res . Header ( ) . Children ( ) ) > 0 ,
2024-05-03 11:52:28 +00:00
}
if res . Header ( ) . ECHeader ( ) != nil {
obj . ecHeader = & ecHeader {
index : res . Header ( ) . ECHeader ( ) . Index ( ) ,
parent : res . Header ( ) . ECHeader ( ) . Parent ( ) ,
}
2023-06-20 13:33:05 +00:00
}
2024-05-03 11:52:28 +00:00
return [ ] phyObject { obj }
2023-06-20 13:33:05 +00:00
}
var errSplitInfo * objectSDK . SplitInfoError
2024-05-03 11:52:28 +00:00
if errors . As ( err , & errSplitInfo ) {
return getComplexObjectParts ( cmd , cnrID , objID , cli , prmHead , errSplitInfo )
}
2023-06-20 13:33:05 +00:00
2024-05-03 11:52:28 +00:00
var ecInfoError * objectSDK . ECInfoError
if errors . As ( err , & ecInfoError ) {
return getECObjectChunks ( cmd , cnrID , objID , ecInfoError )
2023-06-20 13:33:05 +00:00
}
2024-05-03 11:52:28 +00:00
commonCmd . ExitOnErr ( cmd , "failed to get object info: %w" , err )
return nil
}
func getComplexObjectParts ( cmd * cobra . Command , cnrID cid . ID , objID oid . ID , cli * client . Client , prmHead internalclient . HeadObjectPrm , errSplitInfo * objectSDK . SplitInfoError ) [ ] phyObject {
members := getCompexObjectMembers ( cmd , cnrID , objID , cli , prmHead , errSplitInfo )
return flattenComplexMembersIfECContainer ( cmd , cnrID , members , prmHead )
}
2023-06-20 13:33:05 +00:00
2024-05-03 11:52:28 +00:00
func getCompexObjectMembers ( cmd * cobra . Command , cnrID cid . ID , objID oid . ID , cli * client . Client , prmHead internalclient . HeadObjectPrm , errSplitInfo * objectSDK . SplitInfoError ) [ ] oid . ID {
2023-06-20 13:33:05 +00:00
splitInfo := errSplitInfo . SplitInfo ( )
2024-05-06 09:37:00 +00:00
if members , ok := tryGetSplitMembersByLinkingObject ( cmd , splitInfo , prmHead , cnrID , false ) ; ok {
2024-05-03 11:52:28 +00:00
return members
2023-06-20 13:33:05 +00:00
}
if members , ok := tryGetSplitMembersBySplitID ( cmd , splitInfo , cli , cnrID ) ; ok {
2024-05-03 11:52:28 +00:00
return members
}
return tryRestoreChainInReverse ( cmd , splitInfo , prmHead , cli , cnrID , objID )
}
func flattenComplexMembersIfECContainer ( cmd * cobra . Command , cnrID cid . ID , members [ ] oid . ID , prmHead internalclient . HeadObjectPrm ) [ ] phyObject {
result := make ( [ ] phyObject , 0 , len ( members ) )
var addrObj oid . Address
addrObj . SetContainer ( cnrID )
prmHead . SetRawFlag ( true ) // to get an error instead of whole object
for _ , partObjID := range members {
addrObj . SetObject ( partObjID )
prmHead . SetAddress ( addrObj )
_ , err := internalclient . HeadObject ( cmd . Context ( ) , prmHead )
var ecInfoError * objectSDK . ECInfoError
if errors . As ( err , & ecInfoError ) {
chunks := getECObjectChunks ( cmd , cnrID , partObjID , ecInfoError )
result = append ( result , chunks ... )
continue
} else if err == nil { // not EC object, so all members must be phy objects
for _ , member := range members {
result = append ( result , phyObject {
containerID : cnrID ,
objectID : member ,
} )
}
break
2023-06-20 13:33:05 +00:00
}
2024-05-03 11:52:28 +00:00
commonCmd . ExitOnErr ( cmd , "failed to read EC chunk of complex object: %w" , err )
2023-06-20 13:33:05 +00:00
}
2024-05-03 11:52:28 +00:00
return result
}
2023-06-20 13:33:05 +00:00
2024-05-03 11:52:28 +00:00
func getECObjectChunks ( cmd * cobra . Command , cnrID cid . ID , objID oid . ID , errECInfo * objectSDK . ECInfoError ) [ ] phyObject {
ecInfo := errECInfo . ECInfo ( )
result := make ( [ ] phyObject , 0 , len ( ecInfo . Chunks ) )
for _ , ch := range ecInfo . Chunks {
var chID oid . ID
err := chID . ReadFromV2 ( ch . ID )
if err != nil {
commonCmd . ExitOnErr ( cmd , "failed to read EC chunk ID %w" , err )
return nil
}
result = append ( result , phyObject {
containerID : cnrID ,
objectID : chID ,
ecHeader : & ecHeader {
index : ch . Index ,
parent : objID ,
} ,
} )
2023-06-20 13:33:05 +00:00
}
2024-05-03 11:52:28 +00:00
return result
2023-06-20 13:33:05 +00:00
}
func getPlacementPolicyAndNetmap ( cmd * cobra . Command , cnrID cid . ID , cli * client . Client ) ( placementPolicy netmapSDK . PlacementPolicy , netmap * netmapSDK . NetMap ) {
eg , egCtx := errgroup . WithContext ( cmd . Context ( ) )
eg . Go ( func ( ) ( e error ) {
placementPolicy , e = getPlacementPolicy ( egCtx , cnrID , cli )
return
} )
eg . Go ( func ( ) ( e error ) {
netmap , e = getNetMap ( egCtx , cli )
return
} )
commonCmd . ExitOnErr ( cmd , "rpc error: %w" , eg . Wait ( ) )
return
}
func getPlacementPolicy ( ctx context . Context , cnrID cid . ID , cli * client . Client ) ( netmapSDK . PlacementPolicy , error ) {
2023-08-10 08:09:36 +00:00
prm := internalclient . GetContainerPrm {
Client : cli ,
ClientParams : client . PrmContainerGet {
ContainerID : & cnrID ,
} ,
}
2023-06-20 13:33:05 +00:00
res , err := internalclient . GetContainer ( ctx , prm )
if err != nil {
return netmapSDK . PlacementPolicy { } , err
}
return res . Container ( ) . PlacementPolicy ( ) , nil
}
func getNetMap ( ctx context . Context , cli * client . Client ) ( * netmapSDK . NetMap , error ) {
var prm internalclient . NetMapSnapshotPrm
prm . SetClient ( cli )
res , err := internalclient . NetMapSnapshot ( ctx , prm )
if err != nil {
return nil , err
}
nm := res . NetMap ( )
return & nm , nil
}
2024-05-07 09:12:34 +00:00
func getRequiredPlacement ( cmd * cobra . Command , objects [ ] phyObject , placementPolicy netmapSDK . PlacementPolicy , netmap * netmapSDK . NetMap ) * objectNodesResult {
2024-05-03 11:52:28 +00:00
if policy . IsECPlacement ( placementPolicy ) {
return getECRequiredPlacement ( cmd , objects , placementPolicy , netmap )
2023-06-20 13:33:05 +00:00
}
2024-05-03 11:52:28 +00:00
return getReplicaRequiredPlacement ( cmd , objects , placementPolicy , netmap )
}
2023-06-20 13:33:05 +00:00
2024-05-07 09:12:34 +00:00
func getReplicaRequiredPlacement ( cmd * cobra . Command , objects [ ] phyObject , placementPolicy netmapSDK . PlacementPolicy , netmap * netmapSDK . NetMap ) * objectNodesResult {
result := & objectNodesResult {
placements : make ( map [ oid . ID ] objectPlacement ) ,
}
2024-05-03 11:52:28 +00:00
placementBuilder := placement . NewNetworkMapBuilder ( netmap )
for _ , object := range objects {
placement , err := placementBuilder . BuildPlacement ( object . containerID , & object . objectID , placementPolicy )
commonCmd . ExitOnErr ( cmd , "failed to get required placement for object: %w" , err )
for repIdx , rep := range placement {
numOfReplicas := placementPolicy . ReplicaDescriptor ( repIdx ) . NumberOfObjects ( )
var nodeIdx uint32
2023-06-20 13:33:05 +00:00
for _ , n := range rep {
2024-05-03 11:52:28 +00:00
if ! object . storedOnAllContainerNodes && nodeIdx == numOfReplicas {
break
}
2024-05-06 12:52:41 +00:00
2024-05-07 09:12:34 +00:00
op := result . placements [ object . objectID ]
2024-05-06 12:52:41 +00:00
op . requiredNodes = append ( op . requiredNodes , n )
2024-05-07 09:12:34 +00:00
result . placements [ object . objectID ] = op
2024-05-06 12:52:41 +00:00
2024-05-03 11:52:28 +00:00
nodeIdx ++
2023-06-20 13:33:05 +00:00
}
}
}
2024-05-07 09:12:34 +00:00
return result
2023-06-20 13:33:05 +00:00
}
2024-05-07 09:12:34 +00:00
func getECRequiredPlacement ( cmd * cobra . Command , objects [ ] phyObject , placementPolicy netmapSDK . PlacementPolicy , netmap * netmapSDK . NetMap ) * objectNodesResult {
result := & objectNodesResult {
placements : make ( map [ oid . ID ] objectPlacement ) ,
}
2024-05-03 11:52:28 +00:00
for _ , object := range objects {
2024-05-07 09:12:34 +00:00
getECRequiredPlacementInternal ( cmd , object , placementPolicy , netmap , result )
2024-05-03 11:52:28 +00:00
}
2024-05-07 09:12:34 +00:00
return result
2024-05-03 11:52:28 +00:00
}
2024-05-07 09:12:34 +00:00
func getECRequiredPlacementInternal ( cmd * cobra . Command , object phyObject , placementPolicy netmapSDK . PlacementPolicy , netmap * netmapSDK . NetMap , result * objectNodesResult ) {
2024-05-03 11:52:28 +00:00
placementObjectID := object . objectID
if object . ecHeader != nil {
placementObjectID = object . ecHeader . parent
}
placementBuilder := placement . NewNetworkMapBuilder ( netmap )
placement , err := placementBuilder . BuildPlacement ( object . containerID , & placementObjectID , placementPolicy )
commonCmd . ExitOnErr ( cmd , "failed to get required placement: %w" , err )
for _ , vector := range placement {
if object . storedOnAllContainerNodes {
for _ , node := range vector {
2024-05-07 09:12:34 +00:00
op := result . placements [ object . objectID ]
2024-05-06 12:52:41 +00:00
op . requiredNodes = append ( op . requiredNodes , node )
2024-05-07 09:12:34 +00:00
result . placements [ object . objectID ] = op
2024-05-03 11:52:28 +00:00
}
continue
}
if object . ecHeader != nil {
chunkIdx := int ( object . ecHeader . index )
nodeIdx := chunkIdx % len ( vector )
node := vector [ nodeIdx ]
2024-05-06 12:52:41 +00:00
2024-05-07 09:12:34 +00:00
op := result . placements [ object . objectID ]
2024-05-06 12:52:41 +00:00
op . requiredNodes = append ( op . requiredNodes , node )
2024-05-07 09:12:34 +00:00
result . placements [ object . objectID ] = op
2024-05-03 11:52:28 +00:00
}
}
}
2024-05-07 09:12:34 +00:00
func getActualPlacement ( cmd * cobra . Command , netmap * netmapSDK . NetMap , pk * ecdsa . PrivateKey , objects [ ] phyObject , result * objectNodesResult ) {
2023-06-20 13:33:05 +00:00
resultMtx := & sync . Mutex { }
2024-05-07 09:12:34 +00:00
candidates := getNodesToCheckObjectExistance ( cmd , netmap , result )
2023-06-20 13:33:05 +00:00
eg , egCtx := errgroup . WithContext ( cmd . Context ( ) )
for _ , cand := range candidates {
cand := cand
eg . Go ( func ( ) error {
2023-07-07 15:52:32 +00:00
cli , err := createClient ( egCtx , cmd , cand , pk )
2023-06-20 13:33:05 +00:00
if err != nil {
resultMtx . Lock ( )
defer resultMtx . Unlock ( )
2024-05-07 09:12:34 +00:00
result . errors = append ( result . errors , fmt . Errorf ( "failed to connect to node %s: %w" , hex . EncodeToString ( cand . PublicKey ( ) ) , err ) )
2023-06-20 13:33:05 +00:00
return nil
}
2024-05-03 11:52:28 +00:00
for _ , object := range objects {
object := object
2023-06-20 13:33:05 +00:00
eg . Go ( func ( ) error {
2024-05-07 09:12:34 +00:00
stored , err := isObjectStoredOnNode ( egCtx , cmd , object . containerID , object . objectID , cli , pk )
2023-06-20 13:33:05 +00:00
resultMtx . Lock ( )
defer resultMtx . Unlock ( )
2024-05-07 09:12:34 +00:00
if err == nil && stored {
op := result . placements [ object . objectID ]
2024-05-06 12:52:41 +00:00
op . confirmedNodes = append ( op . confirmedNodes , cand )
2024-05-07 09:12:34 +00:00
result . placements [ object . objectID ] = op
2024-05-06 12:52:41 +00:00
}
2024-05-07 09:12:34 +00:00
if err != nil {
result . errors = append ( result . errors , fmt . Errorf ( "failed to check object %s existence on node %s: %w" , object . objectID . EncodeToString ( ) , hex . EncodeToString ( cand . PublicKey ( ) ) , err ) )
2023-06-20 13:33:05 +00:00
}
return nil
} )
}
return nil
} )
}
2023-07-07 15:52:32 +00:00
commonCmd . ExitOnErr ( cmd , "failed to get actual placement: %w" , eg . Wait ( ) )
2024-05-07 09:12:34 +00:00
}
func getNodesToCheckObjectExistance ( cmd * cobra . Command , netmap * netmapSDK . NetMap , result * objectNodesResult ) [ ] netmapSDK . NodeInfo {
checkAllNodes , _ := cmd . Flags ( ) . GetBool ( verifyPresenceAllFlag )
if checkAllNodes {
return netmap . Nodes ( )
}
var nodes [ ] netmapSDK . NodeInfo
visited := make ( map [ uint64 ] struct { } )
for _ , p := range result . placements {
for _ , node := range p . requiredNodes {
if _ , ok := visited [ node . Hash ( ) ] ; ! ok {
nodes = append ( nodes , node )
visited [ node . Hash ( ) ] = struct { } { }
}
}
}
return nodes
2023-06-20 13:33:05 +00:00
}
func createClient ( ctx context . Context , cmd * cobra . Command , candidate netmapSDK . NodeInfo , pk * ecdsa . PrivateKey ) ( * client . Client , error ) {
var cli * client . Client
var addresses [ ] string
candidate . IterateNetworkEndpoints ( func ( s string ) bool {
addresses = append ( addresses , s )
return false
} )
2023-07-07 15:52:32 +00:00
addresses = append ( addresses , candidate . ExternalAddresses ( ) ... )
2023-06-20 13:33:05 +00:00
var lastErr error
for _ , address := range addresses {
var networkAddr network . Address
lastErr = networkAddr . FromString ( address )
if lastErr != nil {
continue
}
cli , lastErr = internalclient . GetSDKClient ( ctx , cmd , pk , networkAddr )
if lastErr == nil {
break
}
}
if lastErr != nil {
return nil , lastErr
}
if cli == nil {
2024-03-11 14:55:50 +00:00
return nil , errNoAvailableEndpoint
2023-06-20 13:33:05 +00:00
}
return cli , nil
}
func isObjectStoredOnNode ( ctx context . Context , cmd * cobra . Command , cnrID cid . ID , objID oid . ID , cli * client . Client , pk * ecdsa . PrivateKey ) ( bool , error ) {
var addrObj oid . Address
addrObj . SetContainer ( cnrID )
addrObj . SetObject ( objID )
var prmHead internalclient . HeadObjectPrm
prmHead . SetClient ( cli )
prmHead . SetAddress ( addrObj )
Prepare ( cmd , & prmHead )
prmHead . SetTTL ( 1 )
readSession ( cmd , & prmHead , pk , cnrID , objID )
res , err := internalclient . HeadObject ( ctx , prmHead )
if err == nil && res != nil {
return true , nil
}
var notFound * apistatus . ObjectNotFound
var removed * apistatus . ObjectAlreadyRemoved
if errors . As ( err , & notFound ) || errors . As ( err , & removed ) {
return false , nil
}
2024-05-06 09:37:00 +00:00
cmd . Printf ( "failed to get object %s from client\n" , objID . EncodeToString ( ) )
2023-06-20 13:33:05 +00:00
return false , err
}
2024-05-07 09:12:34 +00:00
func printPlacement ( cmd * cobra . Command , objID oid . ID , objects [ ] phyObject , result * objectNodesResult ) {
normilizeObjectNodesResult ( objects , result )
if json , _ := cmd . Flags ( ) . GetBool ( commonflags . JSON ) ; json {
printObjectNodesAsJSON ( cmd , objID , objects , result )
} else {
printObjectNodesAsText ( cmd , objID , objects , result )
2024-05-06 12:52:41 +00:00
}
2024-05-07 09:12:34 +00:00
}
2024-05-06 12:52:41 +00:00
2024-05-07 09:12:34 +00:00
func normilizeObjectNodesResult ( objects [ ] phyObject , result * objectNodesResult ) {
slices . SortFunc ( objects , func ( lhs , rhs phyObject ) int {
if lhs . ecHeader == nil && rhs . ecHeader == nil {
return bytes . Compare ( lhs . objectID [ : ] , rhs . objectID [ : ] )
}
if lhs . ecHeader == nil {
return - 1
}
if rhs . ecHeader == nil {
return 1
}
if lhs . ecHeader . parent == rhs . ecHeader . parent {
return cmp . Compare ( lhs . ecHeader . index , rhs . ecHeader . index )
}
return bytes . Compare ( lhs . ecHeader . parent [ : ] , rhs . ecHeader . parent [ : ] )
} )
for _ , obj := range objects {
op := result . placements [ obj . objectID ]
slices . SortFunc ( op . confirmedNodes , func ( lhs , rhs netmapSDK . NodeInfo ) int {
return bytes . Compare ( lhs . PublicKey ( ) , rhs . PublicKey ( ) )
} )
slices . SortFunc ( op . requiredNodes , func ( lhs , rhs netmapSDK . NodeInfo ) int {
return bytes . Compare ( lhs . PublicKey ( ) , rhs . PublicKey ( ) )
} )
result . placements [ obj . objectID ] = op
2024-05-06 12:52:41 +00:00
}
2024-05-07 09:12:34 +00:00
}
2024-05-06 12:52:41 +00:00
2024-05-07 09:12:34 +00:00
func printObjectNodesAsText ( cmd * cobra . Command , objID oid . ID , objects [ ] phyObject , result * objectNodesResult ) {
2024-05-06 12:52:41 +00:00
fmt . Fprintf ( cmd . OutOrStdout ( ) , "Object %s stores payload in %d data objects:\n" , objID . EncodeToString ( ) , len ( objects ) )
for _ , object := range objects {
fmt . Fprintf ( cmd . OutOrStdout ( ) , "- %s\n" , object . objectID )
if object . ecHeader != nil {
fmt . Fprintf ( cmd . OutOrStdout ( ) , "\tEC index: %d\n" , object . ecHeader . index )
fmt . Fprintf ( cmd . OutOrStdout ( ) , "\tEC parent: %s\n" , object . ecHeader . parent . EncodeToString ( ) )
}
2024-05-07 09:12:34 +00:00
op , ok := result . placements [ object . objectID ]
2024-05-06 12:52:41 +00:00
if ! ok {
continue
}
if len ( op . requiredNodes ) > 0 {
fmt . Fprintf ( cmd . OutOrStdout ( ) , "\tRequired nodes:\n" )
for _ , node := range op . requiredNodes {
fmt . Fprintf ( cmd . OutOrStdout ( ) , "\t\t- %s\n" , hex . EncodeToString ( node . PublicKey ( ) ) )
}
}
if len ( op . confirmedNodes ) > 0 {
2024-05-07 09:12:34 +00:00
fmt . Fprintf ( cmd . OutOrStdout ( ) , "\tConfirmed nodes:\n" )
2024-05-06 12:52:41 +00:00
for _ , node := range op . confirmedNodes {
fmt . Fprintf ( cmd . OutOrStdout ( ) , "\t\t- %s\n" , hex . EncodeToString ( node . PublicKey ( ) ) )
}
}
2023-06-20 13:33:05 +00:00
}
2024-05-07 09:12:34 +00:00
if len ( result . errors ) == 0 {
return
}
fmt . Fprintf ( cmd . OutOrStdout ( ) , "Errors:\n" )
for _ , err := range result . errors {
fmt . Fprintf ( cmd . OutOrStdout ( ) , "\t%s\n" , err . Error ( ) )
}
}
func printObjectNodesAsJSON ( cmd * cobra . Command , objID oid . ID , objects [ ] phyObject , result * objectNodesResult ) {
jsonResult := & objNodesResultJSON {
ObjectID : objID . EncodeToString ( ) ,
}
for _ , object := range objects {
do := ObjNodesDataObject {
ObjectID : object . objectID . EncodeToString ( ) ,
}
if object . ecHeader != nil {
do . ECIndex = & object . ecHeader . index
ecParent := object . ecHeader . parent . EncodeToString ( )
do . ECParentObjectID = & ecParent
}
op , ok := result . placements [ object . objectID ]
if ! ok {
continue
}
for _ , rn := range op . requiredNodes {
do . RequiredNodes = append ( do . RequiredNodes , hex . EncodeToString ( rn . PublicKey ( ) ) )
}
for _ , cn := range op . confirmedNodes {
do . ConfirmedNodes = append ( do . ConfirmedNodes , hex . EncodeToString ( cn . PublicKey ( ) ) )
}
jsonResult . DataObjects = append ( jsonResult . DataObjects , do )
}
for _ , err := range result . errors {
jsonResult . Errors = append ( jsonResult . Errors , err . Error ( ) )
}
b , err := json . Marshal ( jsonResult )
commonCmd . ExitOnErr ( cmd , "failed to marshal json: %w" , err )
cmd . Println ( string ( b ) )
2023-06-20 13:33:05 +00:00
}