class TestNasFunctions(unittest.TestCase): def mock_rabbitcli(self, exchange, exchange_type, process_message=None): class MockRabbitMQCommonClient(RabbitMQCommonClient): def publish_message(self, message, routing_key=None, reply_to=None, exchange=None, correlation_id=None, on_fail=None): return return MockRabbitMQCommonClient @mock.patch('imgstorage.imgstoragenas.RabbitMQCommonClient') @mock.patch('imgstorage.imgstoragenas.NasDaemon.is_sync_node', return_value=False) def setUp(self, mock_sync_node, mock_rabbit): self.client = NasDaemon() mock_rabbit.publish_message = MagicMock() self.client.process_message = MagicMock() self.client.SQLITE_DB = '/tmp/test_db_%s' % uuid.uuid4() self.client.run() with sqlite3.connect(self.client.SQLITE_DB) as con: cur = con.cursor() cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ', ('vol1', None, None, None)) cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ', ('vol2', 'my_tank', 'iqn.2001-04.com.nas-0-1-vol2', 'compute-0-3')) cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ', ('vol3_busy', 'my_tank', 'iqn.2001-04.com.nas-0-1-vol3_busy', 'compute-0-3')) cur.execute('INSERT INTO zvol_calls VALUES (?,?,?)', ('vol3_busy', 'reply_to', time.time())) cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ', ('vol4_busy', 'my_tank', 'iqn.2001-04.com.nas-0-1-vol4_busy', 'compute-0-3')) cur.execute('INSERT INTO zvol_calls VALUES (?,?,?)', ('vol4_busy', 'reply_to', time.time())) con.commit() def tearDown(self): os.remove(self.client.SQLITE_DB) """ Testing mapping of newly created zvol """ @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('socket.gethostbyname', return_value='10.1.1.1') def test_map_zvol_createnew_success(self, mockGetHostCommand, mockRunCommand): zvol = 'vol3' def my_side_effect(*args, **kwargs): if args[0][0] == 'zfs': return StringIO("") elif args[0][0] == 'tgt-setup-lun': return StringIO(tgt_setup_lun_response % (zvol, zvol)) mockRunCommand.side_effect = my_side_effect self.client.ib_net = 'ibnet' self.client.map_zvol( { 'action': 'map_zvol', 'zpool': 'mytank', 'zvol': zvol, 'remotehost': 'compute-0-1', 'size': '10' }, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'map_zvol', 'zvol': zvol, 'nas': '%s.ibnet' % self.client.NODE_NAME, 'target': 'iqn.2001-04.com.nas-0-1-%s' % (zvol), 'size': '10' }, 'compute-0-1', self.client.NODE_NAME, on_fail=ANY) self.assertTrue(self.check_zvol_busy(zvol)) """ Testing mapping of zvol created before """ @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('socket.gethostbyname', return_value='10.1.1.1') def test_map_zvol_usecreated_success(self, mockGetHostCommand, mockRunCommand): zvol = 'vol1' def my_side_effect(*args, **kwargs): if args[0][0] == 'zfs': return StringIO("") elif args[0][0] == 'tgt-setup-lun': return StringIO(tgt_setup_lun_response % (zvol, zvol)) mockRunCommand.side_effect = my_side_effect self.client.ib_net = 'ibnet' self.client.map_zvol( { 'action': 'map_zvol', 'zpool': 'mytank', 'zvol': zvol, 'remotehost': 'compute-0-1', 'size': '10' }, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'map_zvol', 'zvol': zvol, 'nas': '%s.ibnet' % self.client.NODE_NAME, 'target': 'iqn.2001-04.com.nas-0-1-%s' % (zvol), 'size': '10' }, 'compute-0-1', self.client.NODE_NAME, on_fail=ANY) self.assertTrue(self.check_zvol_busy(zvol)) """ Testing mapping of busy zvol """ @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('socket.gethostbyname', return_value='10.1.1.1') def test_map_zvol_busy(self, mockGetHostCommand, mockRunCommand): zvol = 'vol3_busy' def my_side_effect( *args, **kwargs): # just in case... not used in normal condition if args[0][0] == 'zfs': return StringIO("") elif args[0][0] == 'tgt-setup-lun': return StringIO(tgt_setup_lun_response % (zvol, zvol)) mockRunCommand.side_effect = my_side_effect self.client.ib_net = 'ibnet' self.client.map_zvol( { 'action': 'map_zvol', 'zpool': 'mytank', 'zvol': zvol, 'remotehost': 'compute-0-1', 'size': '10' }, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'zvol_mapped', 'status': 'error', 'error': 'ZVol %s is busy' % zvol }, routing_key='reply_to', exchange='') self.assertTrue(self.check_zvol_busy(zvol)) def test_fail_action(self): self.client.failAction('routing_key', 'action', 'error_message') self.client.queue_connector.publish_message.assert_called_with( { 'action': 'action', 'status': 'error', 'error': 'error_message' }, routing_key='routing_key', exchange='') @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch( 'imgstorage.imgstoragenas.rocks.db.helper.DatabaseHelper.getHostAttr', return_value=False) def test_teardown_success(self, mockHostSyncAttr, mockRunCommand): zvol = 'vol2' mockRunCommand.return_value = StringIO(tgtadm_response % (zvol, zvol)) self.client.unmap_zvol({ 'action': 'unmap_zvol', 'zvol': zvol }, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'unmap_zvol', 'target': u'iqn.2001-04.com.nas-0-1-%s' % zvol, 'zvol': zvol }, u'compute-0-3', self.client.NODE_NAME, on_fail=ANY) self.assertTrue(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch( 'imgstorage.imgstoragenas.rocks.db.helper.DatabaseHelper.getHostAttr', return_value=False) def test_teardown_busy(self, mockHostSyncAttr, mockRunCommand): zvol = 'vol3_busy' mockRunCommand.return_value = StringIO(tgtadm_response % (zvol, zvol)) self.assertFalse( self.client.unmap_zvol({ 'action': 'unmap_zvol', 'zvol': zvol }, BasicProperties(reply_to='reply_to'))) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_teardown_unmapped_volume(self, mockRunCommand): zvol = 'vol1' mockRunCommand.return_value = StringIO(tgtadm_response % (zvol, zvol)) self.client.unmap_zvol({ 'action': 'unmap_zvol', 'zvol': zvol }, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'zvol_unmapped', 'status': 'error', 'error': 'ZVol %s is not mapped' % zvol }, routing_key='reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand', return_value='') def test_del_zvol_success(self, mockRunCommand): zvol = 'vol1' self.client.del_zvol( { 'action': 'del_zvol', 'zpool': 'mytank', 'zvol': zvol }, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'zvol_deleted', 'status': 'success' }, routing_key='reply_to', exchange='') mockRunCommand.assert_called_with( ['zfs', 'destroy', 'mytank/%s' % (zvol), '-r']) self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand', return_value='') def test_del_zvol_not_found(self, mockRunCommand): zvol = 'wrong_vol' self.client.del_zvol( { 'action': 'del_zvol', 'zpool': 'mytank', 'zvol': zvol }, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'zvol_deleted', 'status': 'error', 'error': 'ZVol %s not found in database' % zvol }, routing_key='reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand', return_value='') def test_del_zvol_mapped(self, mockRunCommand): zvol = 'vol2' self.client.del_zvol( { 'action': 'del_zvol', 'zpool': 'mytank', 'zvol': zvol }, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'zvol_deleted', 'status': 'error', 'error': 'Error deleting zvol %s: is mapped' % zvol }, routing_key='reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_find_iscsi_target_num_not_found(self, mockRunCommand): zvol = 'vol1' target = 'not_found_iqn.2001-04.com.nas-0-1-%s' % zvol mockRunCommand.return_value = StringIO(tgtadm_response % (zvol, zvol)) self.assertEqual(self.client.find_iscsi_target_num(target), None) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_find_iscsi_target_num_success(self, mockRunCommand): zvol = 'vol1' target = 'iqn.2001-04.com.nas-0-1-%s' % zvol mockRunCommand.return_value = StringIO(tgtadm_response % (zvol, zvol)) self.assertEqual(self.client.find_iscsi_target_num(target), '1') @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('imgstorage.imgstoragenas.NasDaemon.is_sync_node', return_value=False) def test_zvol_unmapped_success(self, mockIsSyncMode, mockRunCommand): zvol = 'vol3_busy' target = 'iqn.2001-04.com.nas-0-1-%s' % zvol self.client.zvol_unmapped( { 'action': 'zvol_unmapped', 'target': target, 'zvol': zvol, 'status': 'success' }, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'zvol_unmapped', 'status': 'success' }, routing_key=u'reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_zvol_unmapped_got_error(self, mockRunCommand): zvol = 'vol3_busy' target = 'iqn.2001-04.com.nas-0-1-%s' % zvol self.client.zvol_unmapped( { 'action': 'zvol_unmapped', 'target': target, 'zvol': zvol, 'status': 'error', 'error': 'Some error' }, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'zvol_unmapped', 'status': 'error', 'error': 'Error detaching iSCSI target from compute node: Some error' }, routing_key=u'reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.NasDaemon.is_sync_node', return_value=False) def test_zvol_mapped_success(self, mockIsSyncMode): zvol = 'vol4_busy' target = 'iqn.2001-04.com.nas-0-1-%s' % zvol self.client.zvol_mapped( { 'action': 'zvol_mapped', 'target': target, 'bdev': 'sdc', 'status': 'success' }, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'zvol_mapped', 'status': 'success', 'bdev': 'sdc' }, routing_key=u'reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) def test_zvol_mapped_got_error(self): zvol = 'vol4_busy' target = 'iqn.2001-04.com.nas-0-1-%s' % zvol self.client.zvol_mapped( { 'action': 'zvol_mapped', 'target': target, 'status': 'error', 'error': 'Some error' }, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.client.queue_connector.publish_message.assert_called_with( { 'action': 'zvol_mapped', 'status': 'error', 'error': 'Error attaching iSCSI target to compute node: Some error' }, routing_key=u'reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) # TODO IS THIS RIGHT? def check_zvol_busy(self, zvol): with sqlite3.connect(self.client.SQLITE_DB) as con: cur = con.cursor() cur.execute('SELECT count(*) from zvol_calls where zvol = ?', [zvol]) num_rows = cur.fetchone()[0] return num_rows > 0
class TestNasFunctions(unittest.TestCase): def mock_rabbitcli(self, exchange, exchange_type, process_message=None): class MockRabbitMQCommonClient(RabbitMQCommonClient): def publish_message(self, message, routing_key=None, reply_to=None, exchange=None, correlation_id=None, on_fail=None): return return MockRabbitMQCommonClient @mock.patch('imgstorage.imgstoragenas.RabbitMQCommonClient') @mock.patch('imgstorage.imgstoragenas.NasDaemon.is_sync_node', return_value=False) def setUp(self, mock_sync_node, mock_rabbit): self.client = NasDaemon() mock_rabbit.publish_message = MagicMock() self.client.process_message = MagicMock() self.client.SQLITE_DB = '/tmp/test_db_%s'%uuid.uuid4() self.client.run() with sqlite3.connect(self.client.SQLITE_DB) as con: cur = con.cursor() cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ',('vol1',None, None, None)) cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ',('vol2', 'my_tank', 'iqn.2001-04.com.nas-0-1-vol2', 'compute-0-3')) cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ',('vol3_busy', 'my_tank', 'iqn.2001-04.com.nas-0-1-vol3_busy', 'compute-0-3')) cur.execute('INSERT INTO zvol_calls VALUES (?,?,?)',('vol3_busy', 'reply_to', time.time())) cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ',('vol4_busy', 'my_tank', 'iqn.2001-04.com.nas-0-1-vol4_busy', 'compute-0-3')) cur.execute('INSERT INTO zvol_calls VALUES (?,?,?)',('vol4_busy', 'reply_to', time.time())) con.commit() def tearDown(self): os.remove(self.client.SQLITE_DB) """ Testing mapping of newly created zvol """ @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('socket.gethostbyname', return_value='10.1.1.1') def test_map_zvol_createnew_success(self, mockGetHostCommand, mockRunCommand): zvol = 'vol3' def my_side_effect(*args, **kwargs): if args[0][0] == 'zfs': return StringIO("") elif args[0][0] == 'tgt-setup-lun': return StringIO(tgt_setup_lun_response%(zvol, zvol)) mockRunCommand.side_effect = my_side_effect self.client.ib_net = 'ibnet' self.client.map_zvol( {'action': 'map_zvol', 'zpool':'mytank', 'zvol': zvol, 'remotehost': 'compute-0-1', 'size': '10'}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'map_zvol', 'zvol': zvol, 'nas': '%s.ibnet'%self.client.NODE_NAME, 'target': 'iqn.2001-04.com.nas-0-1-%s'%(zvol), 'size':'10'}, 'compute-0-1', self.client.NODE_NAME, on_fail=ANY) self.assertTrue(self.check_zvol_busy(zvol)) """ Testing mapping of zvol created before """ @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('socket.gethostbyname', return_value='10.1.1.1') def test_map_zvol_usecreated_success(self, mockGetHostCommand, mockRunCommand ): zvol = 'vol1' def my_side_effect(*args, **kwargs): if args[0][0] == 'zfs': return StringIO("") elif args[0][0] == 'tgt-setup-lun': return StringIO(tgt_setup_lun_response%(zvol, zvol)) mockRunCommand.side_effect = my_side_effect self.client.ib_net = 'ibnet' self.client.map_zvol( {'action': 'map_zvol', 'zpool':'mytank', 'zvol': zvol, 'remotehost': 'compute-0-1', 'size': '10'}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'map_zvol', 'zvol': zvol, 'nas': '%s.ibnet'%self.client.NODE_NAME, 'target': 'iqn.2001-04.com.nas-0-1-%s'%(zvol), 'size':'10'}, 'compute-0-1', self.client.NODE_NAME, on_fail=ANY) self.assertTrue(self.check_zvol_busy(zvol)) """ Testing mapping of busy zvol """ @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('socket.gethostbyname', return_value='10.1.1.1') def test_map_zvol_busy(self, mockGetHostCommand, mockRunCommand ): zvol = 'vol3_busy' def my_side_effect(*args, **kwargs): # just in case... not used in normal condition if args[0][0] == 'zfs': return StringIO("") elif args[0][0] == 'tgt-setup-lun': return StringIO(tgt_setup_lun_response%(zvol, zvol)) mockRunCommand.side_effect = my_side_effect self.client.ib_net = 'ibnet' self.client.map_zvol( {'action': 'map_zvol', 'zpool':'mytank', 'zvol': zvol, 'remotehost': 'compute-0-1', 'size': '10'}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_mapped', 'status': 'error', 'error': 'ZVol %s is busy'%zvol}, routing_key='reply_to', exchange='') self.assertTrue(self.check_zvol_busy(zvol)) def test_fail_action(self): self.client.failAction('routing_key', 'action', 'error_message') self.client.queue_connector.publish_message.assert_called_with( {'action': 'action', 'status': 'error', 'error': 'error_message'}, routing_key='routing_key', exchange='') @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('imgstorage.imgstoragenas.rocks.db.helper.DatabaseHelper.getHostAttr', return_value=False) def test_teardown_success(self, mockHostSyncAttr, mockRunCommand): zvol = 'vol2' mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.client.unmap_zvol( {'action': 'unmap_zvol', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'unmap_zvol', 'target': u'iqn.2001-04.com.nas-0-1-%s'%zvol, 'zvol':zvol}, u'compute-0-3', self.client.NODE_NAME, on_fail=ANY) self.assertTrue(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('imgstorage.imgstoragenas.rocks.db.helper.DatabaseHelper.getHostAttr', return_value=False) def test_teardown_busy(self, mockHostSyncAttr, mockRunCommand): zvol = 'vol3_busy' mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.assertFalse(self.client.unmap_zvol( {'action': 'unmap_zvol', 'zvol': zvol}, BasicProperties(reply_to='reply_to'))) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_teardown_unmapped_volume(self, mockRunCommand): zvol = 'vol1' mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.client.unmap_zvol( {'action': 'unmap_zvol', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_unmapped', 'status': 'error', 'error': 'ZVol %s is not mapped'%zvol}, routing_key='reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand', return_value='') def test_del_zvol_success(self, mockRunCommand): zvol = 'vol1' self.client.del_zvol( {'action': 'del_zvol', 'zpool':'mytank', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_deleted', 'status': 'success'}, routing_key='reply_to', exchange='') mockRunCommand.assert_called_with(['zfs', 'destroy', 'mytank/%s'%(zvol), '-r']) self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand', return_value='') def test_del_zvol_not_found(self, mockRunCommand): zvol = 'wrong_vol' self.client.del_zvol( {'action': 'del_zvol', 'zpool':'mytank', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_deleted', 'status': 'error', 'error': 'ZVol %s not found in database'%zvol}, routing_key='reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand', return_value='') def test_del_zvol_mapped(self, mockRunCommand): zvol = 'vol2' self.client.del_zvol( {'action': 'del_zvol', 'zpool':'mytank', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_deleted', 'status': 'error', 'error': 'Error deleting zvol %s: is mapped'%zvol}, routing_key='reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_find_iscsi_target_num_not_found(self, mockRunCommand): zvol = 'vol1' target = 'not_found_iqn.2001-04.com.nas-0-1-%s'%zvol mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.assertEqual(self.client.find_iscsi_target_num(target), None) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_find_iscsi_target_num_success(self, mockRunCommand): zvol = 'vol1' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.assertEqual(self.client.find_iscsi_target_num(target), '1') @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('imgstorage.imgstoragenas.NasDaemon.is_sync_node', return_value=False) def test_zvol_unmapped_success(self, mockIsSyncMode, mockRunCommand): zvol = 'vol3_busy' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol self.client.zvol_unmapped( {'action': 'zvol_unmapped', 'target':target, 'zvol':zvol, 'status':'success'}, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_unmapped', 'status': 'success'}, routing_key=u'reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_zvol_unmapped_got_error(self, mockRunCommand): zvol = 'vol3_busy' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol self.client.zvol_unmapped( {'action': 'zvol_unmapped', 'target':target, 'zvol':zvol, 'status':'error', 'error':'Some error'}, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_unmapped', 'status': 'error', 'error': 'Error detaching iSCSI target from compute node: Some error'}, routing_key=u'reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) @mock.patch('imgstorage.imgstoragenas.NasDaemon.is_sync_node', return_value=False) def test_zvol_mapped_success(self, mockIsSyncMode): zvol = 'vol4_busy' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol self.client.zvol_mapped( {'action': 'zvol_mapped', 'target':target, 'bdev': 'sdc', 'status':'success'}, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_mapped', 'status': 'success', 'bdev': 'sdc'}, routing_key=u'reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) def test_zvol_mapped_got_error(self): zvol = 'vol4_busy' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol self.client.zvol_mapped( {'action': 'zvol_mapped', 'target':target, 'status':'error', 'error':'Some error'}, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_mapped', 'status': 'error', 'error': 'Error attaching iSCSI target to compute node: Some error'}, routing_key=u'reply_to', exchange='') self.assertFalse(self.check_zvol_busy(zvol)) # TODO IS THIS RIGHT? def check_zvol_busy(self, zvol): with sqlite3.connect(self.client.SQLITE_DB) as con: cur = con.cursor() cur.execute('SELECT count(*) from zvol_calls where zvol = ?',[zvol]) num_rows = cur.fetchone()[0] return num_rows > 0
class TestSyncFunctions(unittest.TestCase): def mock_rabbitcli(self, exchange, exchange_type, process_message=None): class MockRabbitMQCommonClient(RabbitMQCommonClient): def publish_message(self, message, routing_key=None, reply_to=None, exchange=None, correlation_id=None, on_fail=None): return return MockRabbitMQCommonClient @mock.patch('imgstorage.imgstoragenas.RabbitMQCommonClient') @mock.patch('imgstorage.imgstoragevm.RabbitMQCommonClient') def setUp(self, mock_rabbit_vm, mock_rabbit_sync): self.nas_client = NasDaemon() mock_rabbit_vm.publish_message = MagicMock() mock_rabbit_sync.publish_message = MagicMock() self.nas_client.process_message = MagicMock() self.nas_client.is_sync_node = MagicMock(return_value = True) self.nas_client.get_node_zpool = MagicMock(return_value = 'my_tank') self.nas_client.SQLITE_DB = '/tmp/test_db_%s'%uuid.uuid4() self.nas_client.run() with sqlite3.connect(self.nas_client.SQLITE_DB) as con: cur = con.cursor() cur.execute('CREATE TABLE IF NOT EXISTS zvol_calls(zvol TEXT PRIMARY KEY NOT NULL, reply_to TEXT NOT NULL, time INT NOT NULL)') cur.execute('CREATE TABLE IF NOT EXISTS zvols(zvol TEXT PRIMARY KEY NOT NULL, zpool TEXT NOT NULL, iscsi_target TEXT UNIQUE, remotehost TEXT)') cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ',('vol1', None, None, None)) cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ',('vol2', 'my_tank', None, 'compute-0-1')) cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ',('vol3_busy', 'my_tank', None, 'compute-0-1')) cur.execute('INSERT INTO zvol_calls VALUES (?,?,?)',('vol3_busy', 'reply_to', time.time())) cur.execute('INSERT INTO zvols VALUES (?,?,?,?) ',('vol4_busy', 'my_tank', 'iqn.2001-04.com.nas-0-1-vol4_busy', 'compute-0-1')) cur.execute('INSERT INTO zvol_calls VALUES (?,?,?)',('vol4_busy', 'reply_to', time.time())) con.commit() def tearDown(self): os.remove(self.nas_client.SQLITE_DB) @mock.patch('imgstorage.imgstoragenas.NasDaemon.download_snapshot') @mock.patch('imgstorage.imgstoragenas.runCommand') def test_zvol_unmapped_success(self, mock_run_command, mock_download_snapshot): zvol = 'vol4_busy' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol mock_run_command.return_value = (iscsiadm_session_response%(target, zvol)).splitlines() self.nas_client.zvol_unmapped( {'action': 'zvol_unmapped', 'target':target, 'zvol': zvol, 'status':'success'}, BasicProperties(reply_to='compute-0-3', correlation_id='message_id')) self.nas_client.schedule_next_sync() self.nas_client.pool.close() self.nas_client.pool.join() print mock_download_snapshot.mock_calls self.nas_client.download_snapshot.assert_called_with('my_tank', zvol, 'compute-0-3.ibnet') with sqlite3.connect(self.nas_client.SQLITE_DB) as con: cur = con.cursor() cur.execute('SELECT zvol, iscsi_target, remotehost FROM zvols WHERE zvol = ?',[zvol]) self.assertSequenceEqual(cur.fetchone(), [zvol, None, 'compute-0-1']) @mock.patch('imgstorage.imgstoragevm.VmDaemon.is_sync_enabled', return_value=True) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_zvol_synced_success(self, mock_run_command, mock_sync_enabled): zvol = 'vol2' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol mock_run_command.return_value = (iscsiadm_session_response%(target, zvol)).splitlines() self.nas_client.zvol_synced( {'action': 'zvol_synced', 'status':'success', 'zvol':zvol}, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.assertFalse(self.check_zvol_busy(zvol)) with sqlite3.connect(self.nas_client.SQLITE_DB) as con: cur = con.cursor() cur.execute('SELECT zvol, iscsi_target, remotehost FROM zvols WHERE zvol = ?',[zvol]) self.assertSequenceEqual(cur.fetchone(), [zvol, None, 'compute-0-1']) @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('imgstorage.imgstoragenas.NasDaemon.upload_snapshot') def test_zvol_mapped(self, mock_upload_snapshot, mock_run_command): zvol = 'vol4_busy' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol bdev = 'sdc' mock_run_command.return_value = (iscsiadm_session_response%(target, zvol)).splitlines() self.nas_client.zvol_mapped( {'action': 'zvol_mapped', 'target':target, 'bdev':bdev, 'zvol':zvol, 'status':'success'}, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.nas_client.schedule_next_sync() self.nas_client.pool.close() self.nas_client.pool.join() print mock_upload_snapshot.mock_calls mock_upload_snapshot.assert_called_with('my_tank', zvol, 'reply_to.ibnet') self.assertTrue(self.check_zvol_busy(zvol)) with sqlite3.connect(self.nas_client.SQLITE_DB) as con: cur = con.cursor() cur.execute('SELECT zvol, iscsi_target, remotehost FROM zvols WHERE zvol = ?',[zvol]) self.assertSequenceEqual(cur.fetchone(), [zvol, target, 'compute-0-1']) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_zvol_mapped_finished(self, mock_run_command): zvol = 'vol4_busy' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol bdev = 'sdc' mock_run_command.return_value = (iscsiadm_session_response%(target, zvol)).splitlines() self.nas_client.zvol_mapped( {'action': 'zvol_mapped', 'target':target, 'bdev':bdev, 'zvol':zvol, 'status':'success'}, BasicProperties(reply_to='reply_to', correlation_id='message_id')) self.nas_client.sync_result = MagicMock() self.nas_client.sync_result.ready = MagicMock(return_value=True) self.nas_client.schedule_next_sync() self.nas_client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_mapped', 'bdev':bdev, 'status': 'success'}, routing_key='reply_to', exchange='') self.assertTrue(self.check_zvol_busy(zvol)) with sqlite3.connect(self.nas_client.SQLITE_DB) as con: cur = con.cursor() cur.execute('SELECT zvol, iscsi_target, remotehost FROM zvols WHERE zvol = ?',[zvol]) self.assertSequenceEqual(cur.fetchone(), [zvol, target, 'compute-0-1']) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_zvol_unmapped_finished_when_have_pull_scheduled(self, mock_run_command): zvol = 'vol4_busy' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol bdev = 'sdc' mock_run_command.return_value = (iscsiadm_session_response%(target, zvol)).splitlines() with sqlite3.connect(self.nas_client.SQLITE_DB) as con: cur = con.cursor() cur.execute('INSERT INTO sync_queue VALUES(?,?,?,0,0,1)',[zvol, 'my_tank', 'compute-0-3']) con.commit() self.nas_client.zvol_unmapped( {'action': 'zvol_unmapped', 'target':target, 'zvol':zvol, 'status':'success'}, BasicProperties(reply_to='reply_to', correlation_id='message_id')) cur.execute('SELECT is_delete_remote FROM sync_queue WHERE zvol = ?',[zvol]) self.assertEqual(cur.fetchone()[0], 1) sync_result = MagicMock() sync_result.ready = MagicMock(return_value=True) self.nas_client.results[zvol] = sync_result self.nas_client.schedule_next_sync() self.assertFalse(self.check_zvol_busy(zvol)) print mock_run_command.mock_calls mock_run_command.assert_any_call(['su', 'img-storage', '-c', '/usr/bin/ssh compute-0-3 "/sbin/zfs destroy my_tank/%s -r"'%zvol]) @mock.patch('imgstorage.imgstoragenas.runCommand', return_value= ("my_tank/vm-hpcdev-pub03-1-vol@aaa\n"+ "my_tank/vm-hpcdev-pub03-1-vol@bbb").splitlines()) def test_find_last_snapshot(self, mock_run_command): zvol = 'vm-hpcdev-pub03-1-vol' self.assertEqual(self.nas_client.find_last_snapshot('my_tank', zvol), 'bbb') def check_zvol_busy(self, zvol): with sqlite3.connect(self.nas_client.SQLITE_DB) as con: cur = con.cursor() cur.execute('SELECT count(*) from zvol_calls where zvol = ?',[zvol]) num_rows = cur.fetchone()[0] return num_rows > 0 def assertSequenceEqual(self, it1, it2): self.assertEqual(tuple(it1), tuple(it2))
class TestNasFunctions(unittest.TestCase): def mock_rabbitcli(self, exchange, exchange_type, process_message=None): class MockRabbitMQCommonClient(RabbitMQCommonClient): def publish_message(self, message, routing_key=None, reply_to=None, exchange=None, correlation_id=None, on_fail=None): return return MockRabbitMQCommonClient @mock.patch('imgstorage.imgstoragenas.RabbitMQCommonClient') def setUp(self, mock_rabbit): self.client = NasDaemon() mock_rabbit.publish_message = MagicMock() self.client.process_message = MagicMock() self.client.SQLITE_DB = '/tmp/test_db_%s'%uuid.uuid4() self.client.run() with sqlite3.connect(self.client.SQLITE_DB) as con: cur = con.cursor() cur.execute('INSERT INTO zvols VALUES (?,?,?) ',('vol1', None, None)) cur.execute('INSERT INTO zvols VALUES (?,?,?) ',('vol2', 'iqn.2001-04.com.nas-0-1-vol2', 'nas-0-1')) cur.execute('INSERT INTO zvols VALUES (?,?,?) ',('vol3_busy', 'iqn.2001-04.com.nas-0-1-vol3_busy', 'nas-0-1')) cur.execute('INSERT INTO zvol_calls VALUES (?,?,?)',('vol3_busy', 'reply_to', time.time())) con.commit() def tearDown(self): os.remove(self.client.SQLITE_DB) """ Testing mapping of newly created zvol """ @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('socket.gethostbyname', return_value='10.1.1.1') def test_set_zvol_createnew_success(self, mockGetHostCommand, mockRunCommand): zvol = 'vol3' def my_side_effect(*args, **kwargs): if args[0][0] == 'zfs': return StringIO("") elif args[0][0] == 'tgt-setup-lun': return StringIO(tgt_setup_lun_response%(zvol, zvol)) mockRunCommand.side_effect = my_side_effect self.client.ib_net = 'ibnet' self.client.set_zvol( {'action': 'set_zvol', 'zvol': zvol, 'hosting': 'compute-0-1', 'size': '10gb'}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'set_zvol', 'nas': 'hpcdev-pub02.ibnet', 'target': 'iqn.2001-04.com.nas-0-1-%s'%(zvol)}, 'compute-0-1', 'hpcdev-pub02', on_fail=ANY) """ Testing mapping of zvol created before """ @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('socket.gethostbyname', return_value='10.1.1.1') def test_set_zvol_usecreated_success(self, mockGetHostCommand, mockRunCommand ): zvol = 'vol1' def my_side_effect(*args, **kwargs): if args[0][0] == 'zfs': return StringIO("") elif args[0][0] == 'tgt-setup-lun': return StringIO(tgt_setup_lun_response%(zvol, zvol)) mockRunCommand.side_effect = my_side_effect self.client.ib_net = 'ibnet' self.client.set_zvol( {'action': 'set_zvol', 'zvol': zvol, 'hosting': 'compute-0-1', 'size': '10gb'}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'set_zvol', 'nas': 'hpcdev-pub02.ibnet', 'target': 'iqn.2001-04.com.nas-0-1-%s'%(zvol)}, 'compute-0-1', 'hpcdev-pub02', on_fail=ANY) """ Testing mapping of busy zvol """ @mock.patch('imgstorage.imgstoragenas.runCommand') @mock.patch('socket.gethostbyname', return_value='10.1.1.1') def test_set_zvol_busy(self, mockGetHostCommand, mockRunCommand ): zvol = 'vol3_busy' def my_side_effect(*args, **kwargs): # just in case... not used in normal condition if args[0][0] == 'zfs': return StringIO("") elif args[0][0] == 'tgt-setup-lun': return StringIO(tgt_setup_lun_response%(zvol, zvol)) mockRunCommand.side_effect = my_side_effect self.client.ib_net = 'ibnet' self.client.set_zvol( {'action': 'set_zvol', 'zvol': zvol, 'hosting': 'compute-0-1', 'size': '10gb'}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_attached', 'status': 'error', 'error': 'ZVol %s is busy'%zvol}, routing_key='reply_to', exchange='') def test_fail_action(self): self.client.failAction('routing_key', 'action', 'error_message') self.client.queue_connector.publish_message.assert_called_with( {'action': 'action', 'status': 'error', 'error': 'error_message'}, routing_key='routing_key', exchange='') @mock.patch('imgstorage.imgstoragenas.runCommand') def test_teardown_success(self, mockRunCommand): zvol = 'vol2' mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.client.tear_down( {'action': 'tear_down', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) mockRunCommand.assert_called_with(['tgtadm', '--op', 'show', '--mode', 'target']) self.client.queue_connector.publish_message.assert_called_with( {'action': 'tear_down', 'target': u'iqn.2001-04.com.nas-0-1-%s'%zvol}, u'nas-0-1', 'hpcdev-pub02', on_fail=ANY) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_teardown_busy(self, mockRunCommand): zvol = 'vol3_busy' mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.client.tear_down( {'action': 'tear_down', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_detached', 'status': 'error', 'error': 'ZVol %s is busy'%zvol}, routing_key='reply_to', exchange='') @mock.patch('imgstorage.imgstoragenas.runCommand') def test_teardown_detached_volume(self, mockRunCommand): zvol = 'vol1' mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.client.tear_down( {'action': 'tear_down', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_detached', 'status': 'error', 'error': 'ZVol %s is not attached'%zvol}, routing_key='reply_to', exchange='') @mock.patch('imgstorage.imgstoragenas.runCommand', return_value='') def test_del_zvol_success(self, mockRunCommand): zvol = 'vol1' self.client.del_zvol( {'action': 'del_zvol', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_deleted', 'status': 'success'}, routing_key='reply_to', exchange='') @mock.patch('imgstorage.imgstoragenas.runCommand', return_value='') def test_del_zvol_not_found(self, mockRunCommand): zvol = 'wrong_vol' self.client.del_zvol( {'action': 'del_zvol', 'zvol': zvol}, BasicProperties(reply_to='reply_to')) self.client.queue_connector.publish_message.assert_called_with( {'action': 'zvol_deleted', 'status': 'error', 'error': 'ZVol %s not found in database'%zvol}, routing_key='reply_to', exchange='') @mock.patch('imgstorage.imgstoragenas.runCommand') def test_find_iscsi_target_num_not_found(self, mockRunCommand): zvol = 'vol1' target = 'not_found_iqn.2001-04.com.nas-0-1-%s'%zvol mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.assertEqual(self.client.find_iscsi_target_num(target), None) @mock.patch('imgstorage.imgstoragenas.runCommand') def test_find_iscsi_target_num_success(self, mockRunCommand): zvol = 'vol1' target = 'iqn.2001-04.com.nas-0-1-%s'%zvol mockRunCommand.return_value = StringIO(tgtadm_response%(zvol, zvol)) self.assertEqual(self.client.find_iscsi_target_num(target), '1')