@@ -204,6 +204,17 @@ def check_peer_status(self, fs_name, fs_id, peer_spec, dir_name, expected_snap_n
204204 self .assertTrue (res [dir_name ]['last_synced_snap' ]['name' ] == expected_snap_name )
205205 self .assertTrue (res [dir_name ]['snaps_synced' ] == expected_snap_count )
206206
207+ def check_peer_status_idle (self , fs_name , fs_id , peer_spec , dir_name , expected_snap_name ,
208+ expected_snap_count ):
209+ peer_uuid = self .get_peer_uuid (peer_spec )
210+ res = self .mirror_daemon_command (f'peer status for fs: { fs_name } ' ,
211+ 'fs' , 'mirror' , 'peer' , 'status' ,
212+ f'{ fs_name } @{ fs_id } ' , peer_uuid )
213+ self .assertTrue (dir_name in res )
214+ self .assertTrue ('idle' == res [dir_name ]['state' ])
215+ self .assertTrue (expected_snap_name == res [dir_name ]['last_synced_snap' ]['name' ])
216+ self .assertTrue (expected_snap_count == res [dir_name ]['snaps_synced' ])
217+
207218 def check_peer_status_deleted_snap (self , fs_name , fs_id , peer_spec , dir_name ,
208219 expected_delete_count ):
209220 peer_uuid = self .get_peer_uuid (peer_spec )
@@ -1499,3 +1510,68 @@ def test_get_set_mirror_dirty_snap_id(self):
14991510 self .mount_b .setfattr ("d1/d2/d3" , "ceph.mirror.dirty_snap_id" , attr )
15001511 val = self .mount_b .getfattr ("d1/d2/d3" , "ceph.mirror.dirty_snap_id" )
15011512 self .assertEqual (attr , val , f"Mismatch for ceph.mirror.dirty_snap_id value: { attr } vs { val } " )
1513+
1514+ def test_cephfs_mirror_remote_snap_corrupt_fails_synced_snapshot (self ):
1515+ """
1516+ That making manual changes to the remote .snap directory shows 'peer status' state: "failed"
1517+ for a synced snapshot and then restores to "idle" when those changes are reverted.
1518+ """
1519+ log .debug ('reconfigure client auth caps' )
1520+ self .get_ceph_cmd_result (
1521+ 'auth' , 'caps' , "client.{0}" .format (self .mount_b .client_id ),
1522+ 'mds' , 'allow rwps' ,
1523+ 'mon' , 'allow r' ,
1524+ 'osd' , 'allow rw pool={0}, allow rw pool={1}' .format (
1525+ self .backup_fs .get_data_pool_name (),
1526+ self .backup_fs .get_data_pool_name ()))
1527+ log .debug (f'mounting filesystem { self .secondary_fs_name } ' )
1528+ self .mount_b .umount_wait ()
1529+ self .mount_b .mount_wait (cephfs_name = self .secondary_fs_name )
1530+
1531+ self .enable_mirroring (self .primary_fs_name , self .primary_fs_id )
1532+ peer_spec = "client.mirror_remote@ceph"
1533+ self .peer_add (self .primary_fs_name , self .primary_fs_id , peer_spec , self .secondary_fs_name )
1534+ dir_name = 'd0'
1535+ self .mount_a .run_shell (['mkdir' , dir_name ])
1536+ self .add_directory (self .primary_fs_name , self .primary_fs_id , f'/{ dir_name } ' )
1537+
1538+ # take a snapshot
1539+ snap_name = "snap_a"
1540+ expected_snap_count = 1
1541+ self .mount_a .run_shell (['mkdir' , f'{ dir_name } /.snap/{ snap_name } ' ])
1542+
1543+ time .sleep (30 )
1544+ # confirm snapshot synced and status 'idle'
1545+ self .check_peer_status_idle (self .primary_fs_name , self .primary_fs_id ,
1546+ peer_spec , f'/{ dir_name } ' , snap_name , expected_snap_count )
1547+
1548+ remote_snap_name = 'snap_b'
1549+ remote_snap_path = f'{ dir_name } /.snap/{ remote_snap_name } '
1550+ failure_reason = f"snapshot '{ remote_snap_name } ' has invalid metadata"
1551+ dir_name = f'/{ dir_name } '
1552+
1553+ # create a directory in the remote fs and check status 'failed'
1554+ self .mount_b .run_shell (['sudo' , 'mkdir' , remote_snap_path ], omit_sudo = False )
1555+ peer_uuid = self .get_peer_uuid (peer_spec )
1556+ with safe_while (sleep = 1 , tries = 60 , action = f'wait for failed status: { peer_spec } ' ) as proceed :
1557+ while proceed ():
1558+ res = self .mirror_daemon_command (f'peer status for fs: { self .primary_fs_name } ' ,
1559+ 'fs' , 'mirror' , 'peer' , 'status' ,
1560+ f'{ self .primary_fs_name } @{ self .primary_fs_id } ' , peer_uuid )
1561+ if ('failed' == res [dir_name ]['state' ] and \
1562+ failure_reason == res .get (dir_name , {}).get ('failure_reason' , {}) and \
1563+ snap_name == res [dir_name ]['last_synced_snap' ]['name' ] and \
1564+ expected_snap_count == res [dir_name ]['snaps_synced' ]):
1565+ break
1566+ # remove the directory in the remote fs and check status restores to 'idle'
1567+ self .mount_b .run_shell (['sudo' , 'rmdir' , remote_snap_path ], omit_sudo = False )
1568+ with safe_while (sleep = 1 , tries = 60 , action = f'wait for idle status: { peer_spec } ' ) as proceed :
1569+ while proceed ():
1570+ res = self .mirror_daemon_command (f'peer status for fs: { self .primary_fs_name } ' ,
1571+ 'fs' , 'mirror' , 'peer' , 'status' ,
1572+ f'{ self .primary_fs_name } @{ self .primary_fs_id } ' , peer_uuid )
1573+ if ('idle' == res [dir_name ]['state' ] and 'failure_reason' not in res and \
1574+ snap_name == res [dir_name ]['last_synced_snap' ]['name' ] and \
1575+ expected_snap_count == res [dir_name ]['snaps_synced' ]):
1576+ break
1577+ self .disable_mirroring (self .primary_fs_name , self .primary_fs_id )
0 commit comments