diff --git a/Makefile.am b/Makefile.am index 623a948..2306eac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -135,6 +135,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/0404_inquiry_all_reported_vpd.c \ test-tool/0410_readtoc_basic.c \ test-tool/0420_reserve6_simple.c \ + test-tool/0421_reserve6_lun_reset.c \ + test-tool/0422_reserve6_logout.c \ \ test-tool/1000_cmdsn_invalid.c \ test-tool/1010_datasn_invalid.c \ diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 8ea43f2..3381571 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -112,7 +112,7 @@ enum scsi_xfer_dir { /* * READTOC */ -EXTERN struct scsi_task *scsi_cdb_readtoc(int msf, int format, int track_session, uint32_t xferlen); +EXTERN struct scsi_task *scsi_cdb_readtoc(int msf, int format, int track_session, uint16_t alloc_len); enum scsi_readtoc_fmt { SCSI_READ_TOC = 0, diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 6846ce7..199b4d5 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -308,7 +308,7 @@ scsi_cdb_readcapacity10(int lba, int pmi) * READTOC */ struct scsi_task * -scsi_cdb_readtoc(int msf, int format, int track_session, uint32_t xferlen) +scsi_cdb_readtoc(int msf, int format, int track_session, uint16_t alloc_len) { struct scsi_task *task; @@ -335,13 +335,15 @@ scsi_cdb_readtoc(int msf, int format, int track_session, uint32_t xferlen) task->cdb[6] = 0xff & track_session; } + *(uint16_t *)&task->cdb[7] = htons(alloc_len); + task->cdb_size = 10; - if (xferlen != 0) { + if (alloc_len != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } - task->expxferlen = xferlen; + task->expxferlen = alloc_len; task->params.readtoc.msf = msf; task->params.readtoc.format = format; diff --git a/test-tool/0410_readtoc_basic.c b/test-tool/0410_readtoc_basic.c index 3ba9a4f..e7bb4d4 100644 --- a/test-tool/0410_readtoc_basic.c +++ b/test-tool/0410_readtoc_basic.c @@ -29,16 +29,20 @@ int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, i struct scsi_inquiry_standard *inq; struct scsi_readcapacity10 *rc10; struct scsi_readtoc_list *list, *list1; - int ret, lun, i, toc_device; - int media, full_size; + int ret, lun, i, toc_device, full_size; + int is_blank = 0; + int no_medium = 0; printf("0410_readtoc_basic:\n"); printf("===================\n"); if (show_info) { printf("Test Read TOC command.\n"); printf(" If device does not support, just verify appropriate error returned\n"); - printf("1, Check we can read the TOC: track 0, non-MSF.\n"); + printf("1, Verify we can read the TOC: track 0, non-MSF. (non-MMC devices should return sense)\n"); printf("2, Make sure at least 4 bytes returned as header.\n"); + printf("3, Verify we can read the TOC: track 1, non-MSF.\n"); + printf("4, Make sure at least 4 bytes returned as header.\n"); + printf("5, Verify track 0 and 1 both returned the same data.\n"); printf("\n"); return 0; } @@ -52,6 +56,7 @@ int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, i ret = 0; + printf("Read standard INQUIRY data ... "); /* Submit INQUIRY so we can find out the device type */ task = iscsi_inquiry_sync(iscsi, lun, 0, 0, 255); @@ -92,22 +97,25 @@ int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, i printf("Check device-type is either of DISK, TAPE or CD/DVD ... "); switch (inq->device_type) { + case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC: + toc_device = 1; + break; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS: case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS: toc_device = 0; break; - case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC: - toc_device = 1; - break; default: - printf("[FAILED]\n"); - printf("Device-type is not DISK, TAPE or CD/DVD. Device reported:%s\n", scsi_devtype_to_str(inq->device_type)); - ret = -1; + /* Unknown device type */ + printf("[SKIPPED]\n"); + printf("This test is only available on SBC/SBC/SSC devices\n"); + ret = -2; goto finished; } + scsi_free_scsi_task(task); printf("[OK]\n"); - printf("CD/DVD Device. Check if media present.\n"); + + printf("CD/DVD Device. Check if medium present ... "); task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0); if (task == NULL) { printf("[FAILED]\n"); @@ -115,108 +123,127 @@ int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, i ret = -1; goto finished; } - if (task->status != SCSI_STATUS_GOOD) { + if (task->status == SCSI_STATUS_GOOD) { + rc10 = scsi_datain_unmarshall(task); + if (rc10 == NULL) { + printf("[FAILED]\n"); + printf("failed to unmarshall readcapacity10 data. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + /* LBA will return 0, if the medium is blank. */ + is_blank = rc10->lba ? 0 : 1; + } + /* If we get 'medium not present' there is no medium in the drive */ + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_NOT_READY + && (task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT + || task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN + || task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED)) { + no_medium = 1; + printf("[OK]\n"); + printf("No medium in drive. Medium access commands should fail\n"); + goto test1; + } else if (task->status != SCSI_STATUS_GOOD) { printf("[FAILED]\n"); printf("Readcapacity command: failed with sense. %s\n", iscsi_get_error(iscsi)); ret = -1; scsi_free_scsi_task(task); goto finished; } - rc10 = scsi_datain_unmarshall(task); - if (rc10 == NULL) { - printf("[FAILED]\n"); - printf("failed to unmarshall readcapacity10 data. %s\n", iscsi_get_error(iscsi)); - ret = -1; - scsi_free_scsi_task(task); - goto finished; + + scsi_free_scsi_task(task); + + printf("[OK]\n"); + if (is_blank) { + printf("Blank disk loaded. ReadTOC should fail.\n"); + } else { + printf("There is a disk in the drive. ReadTOC should work.\n"); } - /* LBA will return 0, if there is no media. */ - media = rc10->lba ? 1 : 0; test1: - - printf("Read TOC format 0000b (TOC)\n"); - if (toc_device) { - printf(" On MMC Device\n"); - } else { - printf(" On non-MMC Device\n"); - } + printf("Verify we can READTOC format 0000b (TOC) track 0 (%s) ... ", + toc_device ? "On MMC Device" : "On non-MMC Device" + ); task = iscsi_readtoc_sync(iscsi, lun, 0, 0, 0, 255); if (task == NULL) { printf("[FAILED]\n"); - printf("Failed to send READ TOC command : %s\n", iscsi_get_error(iscsi)); + printf("Failed to send READTOC command : %s\n", iscsi_get_error(iscsi)); ret = -1; goto finished; } + + /* If no medium, just check if we have appropriate error and bail. */ + if (no_medium) { + if (task->status == SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("READTOC Should have failed since no medium is loaded.\n"); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + + if (task->status != SCSI_STATUS_CHECK_CONDITION + || task->sense.key != SCSI_SENSE_NOT_READY + || (task->sense.ascq != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT && task->sense.ascq != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN + && task->sense.ascq != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED)) { + printf("[FAILED]\n"); + printf("READTOC failed but ascq was wrong. Should " + "have failed with MEDIUM_NOT_PRESENT. " + "Sense:%s\n", iscsi_get_error(iscsi)); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + + printf("[OK]\n"); + printf("No disk, we got the correct sense code that medium is not present. Skipping the remainder of the test\n"); + scsi_free_scsi_task(task); + goto finished; + } + + /* If this is a non-MMC device, just verify that that comand failed as expected and then bail */ if (!toc_device) { if (task->status == SCSI_STATUS_GOOD) { printf("[FAILED]\n"); - printf("READ TOC Should have failed\n"); + printf("READTOC Should have failed\n"); ret = -1; } else if (task->status != SCSI_STATUS_CHECK_CONDITION || task->sense.key != SCSI_SENSE_ILLEGAL_REQUEST || task->sense.ascq != SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { printf("[FAILED]\n"); - printf("READ TOC failed but ascq was wrong. Should have failed with ILLEGAL_REQUEST/INVALID OPERATOR. Sense:%s\n", iscsi_get_error(iscsi)); - ret = -1; - } else { - printf("[OK]\n"); - ret = 0; - } - - scsi_free_scsi_task(task); - goto finished; - } - - - /* If no media, just check if we have appropriate error and bail. */ - if (!media) { - printf("No media, check we get appropriate error.\n"); - if (task->status == SCSI_STATUS_GOOD) { - printf("[FAILED]\n"); - printf("READ TOC Should have failed\n"); + printf("READTOC failed but ascq was wrong. Should have failed with ILLEGAL_REQUEST/INVALID OPERATION_CODE. Sense:%s\n", iscsi_get_error(iscsi)); ret = -1; - } else if (task->status != SCSI_STATUS_CHECK_CONDITION - || task->sense.key != SCSI_SENSE_NOT_READY - || (task->sense.ascq - != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT - && task->sense.ascq - != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN - && task->sense.ascq - != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED)) { - printf("[FAILED]\n"); - printf("READ TOC failed but ascq was wrong. Should " - "have failed with MEDIUM_NOT_PRESENT. " - "Sense:%s\n", iscsi_get_error(iscsi)); - ret = -1; } else { printf("[OK]\n"); - ret = 0; + printf("Not an MMC device so READTOC failed as it should. Skipping rest of test\n"); } - scsi_free_scsi_task(task); goto finished; } - - /* We should only still be here if we are an MMC device and - have media. */ if (task->status != SCSI_STATUS_GOOD) { printf("[FAILED]\n"); - printf("READ TOC command failed : %s\n", iscsi_get_error(iscsi)); + printf("READTOC command failed : %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); ret = -1; goto finished; } + printf("[OK]\n"); + + test2: + /* If we get here, there is a disk loaded and it contains data */ + printf("Verify we got at least 4 bytes of data for track 0 ... "); full_size = scsi_datain_getfullsize(task); if (full_size < 4) { printf("[FAILED]\n"); @@ -235,12 +262,13 @@ test2: } printf("[OK]\n"); + test3: - printf("Check lba of 1 is same as lba 0\n"); + printf("Verify we can READTOC format 0000b (TOC) track 1 ... "); task1 = iscsi_readtoc_sync(iscsi, lun, 0, 1, 0, 255); if (task1 == NULL) { printf("[FAILED]\n"); - printf("Failed to send READ TOC command : %s\n", iscsi_get_error(iscsi)); + printf("Failed to send READTOC command : %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); ret = -1; goto finished; @@ -248,14 +276,17 @@ test3: if (task1->status != SCSI_STATUS_GOOD) { printf("[FAILED]\n"); - printf("READ TOC command failed : %s\n", iscsi_get_error(iscsi)); + printf("READTOC command failed : %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); scsi_free_scsi_task(task1); ret = -1; goto finished; } + printf("[OK]\n"); +test4: + printf("Verify we got at least 4 bytes of data for track 1 ... "); full_size = scsi_datain_getfullsize(task1); if (full_size < 4) { printf("[FAILED]\n"); @@ -274,8 +305,10 @@ test3: ret = -1; goto finished; } - scsi_free_scsi_task(task1); + printf("[OK]\n"); +test5: + printf("Verify track 0 and 1 both returned the same data ... "); if (list->num != list1->num || list->first != list1->first || list->last != list1->last) { @@ -283,11 +316,11 @@ test3: printf("Read TOC header of lba 0 != TOC of lba 1.\n"); ret = -1; scsi_free_scsi_task(task); + scsi_free_scsi_task(task1); goto finished; } for (i=0; inum; i++) { - printf(" TOC descriptor %i\n", i); if (list->desc[i].desc.toc.adr != list1->desc[i].desc.toc.adr || list->desc[i].desc.toc.control != list1->desc[i].desc.toc.control || list->desc[i].desc.toc.track != list1->desc[i].desc.toc.track || @@ -296,13 +329,13 @@ test3: printf("Read TOC descriptors of lba 0 != TOC of lba 1.\n"); ret = -1; scsi_free_scsi_task(task); + scsi_free_scsi_task(task1); goto finished; } } - - printf("[OK]\n"); scsi_free_scsi_task(task); + scsi_free_scsi_task(task1); finished: diff --git a/test-tool/0420_reserve6_simple.c b/test-tool/0420_reserve6_simple.c index eab91ee..b5f55b3 100644 --- a/test-tool/0420_reserve6_simple.c +++ b/test-tool/0420_reserve6_simple.c @@ -51,7 +51,7 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, return -1; } - iscsi2 = iscsi_context_login(initiator, url, &lun); + iscsi2 = iscsi_context_login(initiator2, url, &lun); if (iscsi2 == NULL) { printf("Failed to login to target\n"); ret = 1; @@ -60,7 +60,7 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, ret = 0; - printf("Send RESERVE6...\n"); + printf("Send RESERVE6 ... "); task = iscsi_reserve6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -89,7 +89,7 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, scsi_free_scsi_task(task); printf("[OK]\n"); - printf("Send RELEASE6...\n"); + printf("Send RELEASE6 ... "); task = iscsi_release6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -108,9 +108,10 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, scsi_free_scsi_task(task); printf("[OK]\n"); + test2: printf("Test that reservation works.\n"); - printf("Send RESERVE6 from Initiator 1...\n"); + printf("Send RESERVE6 from Initiator 1. ... "); task = iscsi_reserve6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -129,7 +130,6 @@ test2: scsi_free_scsi_task(task); printf("[OK]\n"); - printf("Send another RESERVE6 from Initiator 1...\n"); task = iscsi_reserve6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -149,7 +149,7 @@ test2: printf("[OK]\n"); test3: - printf("Send RESERVE6 from Initiator 2...Expect conflict.\n"); + printf("Send RESERVE6 from Initiator 2. Expect conflict. ... "); task = iscsi_reserve6_sync(iscsi2, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -169,7 +169,7 @@ test3: printf("[OK]\n"); test3a: - printf("Send RELEASE6 from Initiator 2..Expect NO-OP.\n"); + printf("Send RELEASE6 from Initiator 2..Expect NO-OP. ... "); task = iscsi_release6_sync(iscsi2, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -192,7 +192,7 @@ test3a: printf("[OK]\n"); test4: - printf("Send TESTUNITREADY from Initiator 1...\n"); + printf("Send TESTUNITREADY from Initiator 1 ... "); task = iscsi_testunitready_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -212,7 +212,7 @@ test4: printf("[OK]\n"); test5: - printf("Send TESTUNITREADY from Initiator 2...Expect conflict.\n"); + printf("Send TESTUNITREADY from Initiator 2. Expect conflict. ... "); task = iscsi_testunitready_sync(iscsi2, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -232,7 +232,7 @@ test5: test6: printf("Test that release actually works\n"); - printf("Send RELEASE6 from Initiator 1...\n"); + printf("Send RELEASE6 from Initiator 1 ... "); task = iscsi_release6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -251,7 +251,7 @@ test6: scsi_free_scsi_task(task); printf("[OK]\n"); - printf("Send RESERVE6 Initiator 2...\n"); + printf("Send RESERVE6 Initiator 2 ... "); task = iscsi_reserve6_sync(iscsi2, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -270,7 +270,7 @@ test6: scsi_free_scsi_task(task); printf("[OK]\n"); - printf("Send RELEASE6 Initiator 2...\n"); + printf("Send RELEASE6 Initiator 2 ... "); task = iscsi_reserve6_sync(iscsi2, lun); if (task == NULL) { printf("[FAILED]\n"); diff --git a/test-tool/0421_reserve6_lun_reset.c b/test-tool/0421_reserve6_lun_reset.c new file mode 100644 index 0000000..e956d0e --- /dev/null +++ b/test-tool/0421_reserve6_lun_reset.c @@ -0,0 +1,212 @@ +/* + Copyright (C) 2012 by Ronnie Sahlberg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +struct mgmt_task { + uint32_t status; + uint32_t finished; +}; + +static void mgmt_cb(struct iscsi_context *iscsi _U_, int status _U_, + void *command_data, void *private_data) +{ + struct mgmt_task *mgmt_task = (struct mgmt_task *)private_data; + + mgmt_task->status = *(uint32_t *)command_data; + mgmt_task->finished = 1; +} + +int T0421_reserve6_lun_reset(const char *initiator, const char *url, int data_loss, int show_info) +{ + struct iscsi_context *iscsi, *iscsi2; + struct scsi_task *task; + int ret, lun; + struct mgmt_task mgmt_task = {0, 0}; + struct pollfd pfd; + + printf("0421_reserve6_lun_reset:\n"); + printf("========================\n"); + if (show_info) { + printf("Test that a RESERVE6 is dropped by a LUN-reset\n"); + printf(" If device does not support RESERVE6, just skip the test.\n"); + printf("1, Reserve the device from the first initiator.\n"); + printf("2, Verify we can access the LUN from the first initiator\n"); + printf("3, Verify we can NOT access the LUN from the second initiator\n"); + printf("4, Send a LUN-reset to the target\n"); + printf("5, Verify we can access the LUN from the second initiator\n"); + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + iscsi2 = iscsi_context_login(initiator2, url, &lun); + if (iscsi2 == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + ret = 0; + + + + + printf("Send RESERVE6 from the first initiator ... "); + task = iscsi_reserve6_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send RESERVE6 command : %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST + && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { + printf("[SKIPPED]\n"); + printf("RESERVE6 Not Supported\n"); + ret = -2; + scsi_free_scsi_task(task); + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("RESERVE6 failed with sense:%s\n", + iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto test2; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +test2: + printf("Verify we can access the LUN from the first initiator ... "); + task = iscsi_testunitready_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("TEST UNIT READY command: failed with sense %s\n", + iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto test3; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +test3: + printf("Verify we can NOT access the LUN from the second initiator ... "); + task = iscsi_testunitready_sync(iscsi2, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_RESERVATION_CONFLICT) { + printf("[FAILED]\n"); + printf("Expected RESERVATION CONFLICT\n"); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + +test4: + printf("Send a LUN Reset to the target ... "); + iscsi_task_mgmt_lun_reset_async(iscsi, lun, mgmt_cb, &mgmt_task); + while (mgmt_task.finished == 0) { + pfd.fd = iscsi_get_fd(iscsi); + pfd.events = iscsi_which_events(iscsi); + + if (poll(&pfd, 1, -1) < 0) { + printf("Poll failed"); + goto finished; + } + if (iscsi_service(iscsi, pfd.revents) < 0) { + printf("iscsi_service failed with : %s\n", iscsi_get_error(iscsi)); + break; + } + } + if (mgmt_task.status != 0) { + printf("[FAILED]\n"); + printf("Failed to reset the LUN\n"); + goto finished; + } + printf("[OK]\n"); + +test5: + /* We might be getting UNIT_ATTENTION/BUS_RESET after the lun-reset above. + If so just loop and try the TESTUNITREADY again until it clears + */ + printf("Verify we can access the LUN from the second initiator ... "); + task = iscsi_testunitready_sync(iscsi2, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + goto finished; + } + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_UNIT_ATTENTION + && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) { + printf("Got BUS RESET. Retry accessing the LUN\n"); + goto test5; + + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("TEST UNIT READY command: failed with sense %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + scsi_free_scsi_task(task); + goto test3; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +finished: + iscsi_logout_sync(iscsi); + iscsi_destroy_context(iscsi); + iscsi_logout_sync(iscsi2); + iscsi_destroy_context(iscsi2); + return ret; +} diff --git a/test-tool/0422_reserve6_logout.c b/test-tool/0422_reserve6_logout.c new file mode 100644 index 0000000..e30d597 --- /dev/null +++ b/test-tool/0422_reserve6_logout.c @@ -0,0 +1,166 @@ +/* + Copyright (C) 2012 by Ronnie Sahlberg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +int T0422_reserve6_logout(const char *initiator, const char *url, int data_loss, int show_info) +{ + struct iscsi_context *iscsi, *iscsi2; + struct scsi_task *task; + int ret, lun; + + printf("0422_reserve6_logout:\n"); + printf("=====================\n"); + if (show_info) { + printf("Test that a RESERVE6 is dropped when the session is logged out\n"); + printf(" If device does not support RESERVE6, just skip the test.\n"); + printf("1, Reserve the device from the first initiator.\n"); + printf("2, Verify we can access the LUN from the first initiator.\n"); + printf("3, Verify we can NOT access the LUN from the second initiator.\n"); + printf("4, Logout the first initiator.\n"); + printf("5, Verify we can access the LUN from the second initiator.\n"); + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + iscsi2 = iscsi_context_login(initiator2, url, &lun); + if (iscsi2 == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + ret = 0; + + + + + printf("Send RESERVE6 from the first initiator ... "); + task = iscsi_reserve6_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send RESERVE6 command : %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST + && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { + printf("[SKIPPED]\n"); + printf("RESERVE6 Not Supported\n"); + ret = -2; + scsi_free_scsi_task(task); + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("RESERVE6 failed with sense:%s\n", + iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto test2; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +test2: + printf("Verify we can access the LUN from the first initiator ... "); + task = iscsi_testunitready_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("TEST UNIT READY command: failed with sense %s\n", + iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto test3; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +test3: + printf("Verify we can NOT access the LUN from the second initiator ... "); + task = iscsi_testunitready_sync(iscsi2, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_RESERVATION_CONFLICT) { + printf("[FAILED]\n"); + printf("Expected RESERVATION CONFLICT\n"); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + +test4: + printf("Logout the first initiator ... "); + iscsi_logout_sync(iscsi); + iscsi_destroy_context(iscsi); + printf("[OK]\n"); + +test5: + printf("Verify we can access the LUN from the second initiator ... "); + task = iscsi_testunitready_sync(iscsi2, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("TEST UNIT READY command: failed with sense %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +finished: + iscsi_logout_sync(iscsi2); + iscsi_destroy_context(iscsi2); + return ret; +} diff --git a/test-tool/README b/test-tool/README new file mode 100644 index 0000000..8b147d4 --- /dev/null +++ b/test-tool/README @@ -0,0 +1,45 @@ +This directory contains a libiscsi based test tool. +The purpose of this test tool is to validate iscsi and scsi protocol compliance of a target. + + +Initiatornames and LUN-masking +============================== +If the target uses lun-masking or ACLs you need to set the target up to allow +iscsi-test access to the LUNs you want to test. +By default iscsi-test uses the following two initiator names : + iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test + iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test-2 + +Most tests only use the first name but some tests, such as the RESERVE/RELEASE +tests, will use both names. + +Optionally you can use different initiatir names for your testing using the +command line flags : + --initiator-name=iqn. + --initiator-name-2=iqn. + + +Listing all tests and test details: +=================================== +The --list argument is used to show a lost of all major tests that are available + iscsi-test --list + +To list all tests and a description of each test and subtests, use + iscsi-test --list --info + +Running tests: +============== +Running tests you need to specify which test to run using the --test argument. +This can either be the name of one specific test or a set of tests using * as +the wildcard character. + +Example to run one specific test : + iscsi-test --test="T1020_bufferoffset_invalid" iscsi://127.0.0.1/iqn.ronnie.test/1 + +Example to run all READ10 tests : + iscsi-test --test="*read10*" iscsi://127.0.0.1/iqn.ronnie.test/1 + +Or to run every test : + iscsi-test --test="*" iscsi://127.0.0.1/iqn.ronnie.test/1 + + diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index b70e440..1c7c7ea 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -35,6 +35,8 @@ #include "iscsi-test.h" const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test"; +const char *initiator2 = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test-2"; + static int data_loss = 0; static int show_info = 0; @@ -216,6 +218,8 @@ struct scsi_test tests[] = { /* reserve6/release6 */ { "T0420_reserve6_simple", T0420_reserve6_simple }, +{ "T0421_reserve6_lun_reset", T0421_reserve6_lun_reset }, +{ "T0422_reserve6_logout", T0422_reserve6_logout }, /* iSCSI protocol tests */ @@ -242,6 +246,7 @@ void print_help(void) { fprintf(stderr, "Usage: iscsi-test [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); + fprintf(stderr, " -I, --initiator-name-2=iqn-name Second initiatorname to use\n"); fprintf(stderr, " -t, --test=test-name Which test to run. Default is to run all tests.\n"); fprintf(stderr, " -l, --list List all tests.\n"); fprintf(stderr, " --info, Print extra info about a test.\n"); @@ -372,6 +377,7 @@ int main(int argc, const char *argv[]) { "usage", 0, POPT_ARG_NONE, &show_usage, 0, "Display brief usage message", NULL }, { "list", 'l', POPT_ARG_NONE, &list_tests, 0, "List all tests", NULL }, { "initiator-name", 'i', POPT_ARG_STRING, &initiator, 0, "Initiatorname to use", "iqn-name" }, + { "initiator-name-2", 'I', POPT_ARG_STRING, &initiator, 0, "Second initiatorname to use for tests using more two sessions", "iqn-name" }, { "test", 't', POPT_ARG_STRING, &testname, 0, "Which test to run", "testname" }, { "info", 0, POPT_ARG_NONE, &show_info, 0, "Show information about the test", "testname" }, { "dataloss", 0, POPT_ARG_NONE, &data_loss, 0, "Allow destructuve tests", NULL }, diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index db25234..3bd8e1c 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -17,6 +17,9 @@ along with this program; if not, see . */ +extern const char *initiator; +extern const char *initiator2; + struct iscsi_context *iscsi_context_login(const char *initiatorname, const char *url, int *lun); struct iscsi_async_state { @@ -167,6 +170,8 @@ int T0404_inquiry_all_reported_vpd(const char *initiator, const char *url, int d int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, int show_info); int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, int show_info); +int T0421_reserve6_lun_reset(const char *initiator, const char *url, int data_loss, int show_info); +int T0422_reserve6_logout(const char *initiator, const char *url, int data_loss, int show_info); int T1000_cmdsn_invalid(const char *initiator, const char *url, int data_loss, int show_info); int T1010_datasn_invalid(const char *initiator, const char *url, int data_loss, int show_info);