├── .github └── workflows │ ├── pre-commit.yml │ └── pypi.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .travis.yml ├── DEVELOPMENT.md ├── LICENSE ├── LICENSES ├── 0BSD.txt ├── LGPL-2.1-or-later.txt └── MIT.txt ├── README.md ├── examples ├── read16.py ├── read16_with_contextmanger.py ├── read_cd.py ├── read_disc_information.py ├── readcapacity10.py ├── readcapacity16.py ├── reportluns.py └── reportpriority.py ├── mypy.ini ├── pyproject.toml ├── pyscsi ├── __init__.py ├── pyiscsi │ ├── __init__.py │ └── iscsi_device.py ├── pyscsi │ ├── __init__.py │ ├── scsi.py │ ├── scsi_cdb_atapassthrough12.py │ ├── scsi_cdb_atapassthrough16.py │ ├── scsi_cdb_exchangemedium.py │ ├── scsi_cdb_extended_copy_spc4.py │ ├── scsi_cdb_extended_copy_spc5.py │ ├── scsi_cdb_getlbastatus.py │ ├── scsi_cdb_initelementstatus.py │ ├── scsi_cdb_initelementstatuswithrange.py │ ├── scsi_cdb_inquiry.py │ ├── scsi_cdb_modesense10.py │ ├── scsi_cdb_modesense6.py │ ├── scsi_cdb_movemedium.py │ ├── scsi_cdb_openclose_exportimport_element.py │ ├── scsi_cdb_persistentreservein.py │ ├── scsi_cdb_persistentreserveout.py │ ├── scsi_cdb_positiontoelement.py │ ├── scsi_cdb_preventallow_mediumremoval.py │ ├── scsi_cdb_read10.py │ ├── scsi_cdb_read12.py │ ├── scsi_cdb_read16.py │ ├── scsi_cdb_readcapacity10.py │ ├── scsi_cdb_readcapacity16.py │ ├── scsi_cdb_readcd.py │ ├── scsi_cdb_readdiscinformation.py │ ├── scsi_cdb_readelementstatus.py │ ├── scsi_cdb_report_luns.py │ ├── scsi_cdb_report_priority.py │ ├── scsi_cdb_report_target_port_groups.py │ ├── scsi_cdb_synchronize_cache10.py │ ├── scsi_cdb_synchronize_cache16.py │ ├── scsi_cdb_testunitready.py │ ├── scsi_cdb_write10.py │ ├── scsi_cdb_write12.py │ ├── scsi_cdb_write16.py │ ├── scsi_cdb_writesame10.py │ ├── scsi_cdb_writesame16.py │ ├── scsi_command.py │ ├── scsi_device.py │ ├── scsi_enum_command.py │ ├── scsi_enum_getlbastatus.py │ ├── scsi_enum_inquiry.py │ ├── scsi_enum_modesense.py │ ├── scsi_enum_persistentreserve.py │ ├── scsi_enum_readcapacity16.py │ ├── scsi_enum_readcd.py │ ├── scsi_enum_readdiscinformation.py │ ├── scsi_enum_readelementstatus.py │ ├── scsi_enum_report_target_port_groups.py │ ├── scsi_exception.py │ ├── scsi_opcode.py │ └── scsi_sense.py └── utils │ ├── __init__.py │ ├── converter.py │ ├── enum.py │ └── exception.py ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── mock_device.py ├── test_cdb_exchangemedium.py ├── test_cdb_extended_copy_spc4.py ├── test_cdb_extended_copy_spc5.py ├── test_cdb_getlbastatus.py ├── test_cdb_initelementstatus.py ├── test_cdb_initelementstatuswithrange.py ├── test_cdb_inquiry.py ├── test_cdb_modesense10.py ├── test_cdb_modesense6.py ├── test_cdb_movemedium.py ├── test_cdb_openclose_exportimport_element.py ├── test_cdb_persistentreservein.py ├── test_cdb_persistentreserveout.py ├── test_cdb_positiontoelement.py ├── test_cdb_preventallow_mediumremoval.py ├── test_cdb_read10.py ├── test_cdb_read12.py ├── test_cdb_read16.py ├── test_cdb_readcapacity10.py ├── test_cdb_readcapacity16.py ├── test_cdb_readcd.py ├── test_cdb_readelementstatus.py ├── test_cdb_reportpriority.py ├── test_cdb_reporttargetportgroups.py ├── test_cdb_synchronize_cache10.py ├── test_cdb_synchronize_cache16.py ├── test_cdb_testunitready.py ├── test_cdb_write10.py ├── test_cdb_write12.py ├── test_cdb_write16.py ├── test_cdb_writesame10.py ├── test_cdb_writesame16.py ├── test_enum.py ├── test_opcode_mapper.py ├── test_unmarshall_getlbastatus.py ├── test_unmarshall_inquiry.py ├── test_unmarshall_modesense10.py ├── test_unmarshall_modesense6.py ├── test_unmarshall_readcapacity10.py ├── test_unmarshall_readcapacity16.py ├── test_unmarshall_readcd.py └── test_unmarshall_readelementstatus.py └── tools ├── getlbastatus.py ├── inquiry.py ├── mtx.py └── swp.py /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2019 Anthony Sottile 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | name: pre-commit 6 | 7 | on: 8 | pull_request: 9 | push: 10 | branches: [master] 11 | 12 | jobs: 13 | pre-commit: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: actions/setup-python@v2 18 | - uses: pre-commit/action@v3.0.1 19 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 The freestyle-hid Authors 2 | # 3 | # SPDX-License-Identifier: 0BSD 4 | 5 | name: Publish to TestPyPI (and PyPI) 6 | 7 | on: push 8 | 9 | jobs: 10 | build-n-publish: 11 | name: Build package and publish on TestPyPI (and PyPI) 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: 3.x 20 | - name: Install pypa/build 21 | run: | 22 | pip install build 23 | - name: Build a binary wheel and a source tarball 24 | run: | 25 | python -m build --sdist --wheel --outdir dist/ . 26 | - name: Publish package to TestPyPI 27 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 28 | uses: pypa/gh-action-pypi-publish@release/v1 29 | with: 30 | password: ${{ secrets.TEST_PYPI_API_TOKEN }} 31 | repository_url: https://test.pypi.org/legacy/ 32 | - name: Publish package to PyPI 33 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 34 | uses: pypa/gh-action-pypi-publish@release/v1 35 | with: 36 | password: ${{ secrets.PYPI_API_TOKEN }} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | *.pyc 6 | *~ 7 | /*.egg-info/ 8 | /.mypy_cache/ 9 | /build/ 10 | /dist/ 11 | __pycache__/ 12 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.4.0 8 | hooks: 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | - repo: https://github.com/timothycrosley/isort 13 | rev: 5.12.0 14 | hooks: 15 | - id: isort 16 | additional_dependencies: 17 | - toml 18 | - repo: https://github.com/fsfe/reuse-tool 19 | rev: v1.1.2 20 | hooks: 21 | - id: reuse 22 | - repo: https://github.com/psf/black 23 | rev: 23.3.0 24 | hooks: 25 | - id: black 26 | language_version: python3 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | dist: xenial 6 | 7 | language: python 8 | 9 | matrix: 10 | include: 11 | - python: 3.7 12 | - python: 3.8 13 | env: PYTEST_MYPY=--mypy 14 | - python: 3.9-dev 15 | 16 | install: 17 | - pip install .[dev] 18 | 19 | script: 20 | - py.test -vvv $PYTEST_MYPY 21 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Development Information 8 | 9 | You can install all tools needed for developing the package by using 10 | the `dev` extra: 11 | 12 | python-scsi $ pip install -e .[dev] 13 | 14 | This will include test and verification tools, as well as all the 15 | necessary packages to build a new release. 16 | 17 | ## pre-commit 18 | 19 | This repository uses [pre-commit](https://pre-commit.com/) to maintain a 20 | consistent codebase. The tool is installed as part of the `dev` extra. 21 | 22 | To activate the pre-commit hooks, you should use: 23 | 24 | python-scsi $ pre-commit install 25 | 26 | which will set up the hooks for the current repository. 27 | 28 | ## Unit Testing 29 | 30 | The tests directory contain unit tests for python-scsi. 31 | 32 | To run the tests: 33 | 34 | python-scsi $ pip install -e .[dev] 35 | python-scsi $ pytest --mypy 36 | 37 | or use the make file: 38 | 39 | $ cd tests 40 | $ make 41 | 42 | ## Continuous Integration 43 | 44 | [Travis CI](https://travis-ci.com/) is set up to run integration tests 45 | for the repository. The configuration is in the `.travis.yml` file. 46 | 47 | Travis will execute the whole testsuite (unittests and typechecking) 48 | on the master branch as well as on each Pull Request. 49 | 50 | ## Releasing 51 | 52 | [Setuptools](https://setuptools.readthedocs.io/) is used to create the 53 | released packages: 54 | 55 | python-scsi $ pip install -e .[dev] 56 | python-scsi $ git clean -fxd 57 | python-scsi $ git tag -a python-scsi-X.Y.Z 58 | python-scsi $ python3 setup.py sdist bdist_wheel 59 | 60 | The `git tag` command is used to tag the version that will be used by 61 | [setuptools-scm](https://github.com/pypa/setuptools_scm/) to apply the 62 | correct version information in the source and wheel packages. 63 | 64 | The version to tag should be following [Semantic 65 | Versioning](https://semver.org/) (SemVer). 66 | 67 | For more details, see [Generating distribution 68 | archives](https://packaging.python.org/tutorials/packaging-projects/#generating-distribution-archives). 69 | 70 | ## Repository Layout 71 | 72 | The repository follows a (mostly) standard layout for Python repositories: 73 | 74 | * `.gitignore` is part of the repository configuration and is set to 75 | ignore generated files from Python testing and usage. 76 | * `.pre-commit-config.yaml` contains the configuration for 77 | [pre-commit](https://pre-commit.com/) and its hooks. 78 | * `.travis.yml` configures the continuous integration used to 79 | validate pull requests. 80 | * `mypy.ini` contains configuration for 81 | [mypy](https://github.com/python/mypy). 82 | * `setup.py` and `setup.cfg` contain configuration for 83 | [setuptools](https://setuptools.readthedocs.io/). 84 | * `pyproject.toml` contains [PEP 85 | 518](https://www.python.org/dev/peps/pep-0518/) configuration for 86 | various tools. 87 | * `pyscsi` contains the source code of the module that is actually 88 | installed by pip. 89 | * `tools` and `examples` contain Python entrypoints showing usage of 90 | the library. 91 | * `tests` contains the unittest to validate the correctness of the 92 | library. 93 | -------------------------------------------------------------------------------- /LICENSES/0BSD.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) YEAR by AUTHOR EMAIL 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice (including the next 11 | paragraph) shall be included in all copies or substantial portions of the 12 | Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 17 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | python-scsi 8 | =========== 9 | python-scsi is a SCSI initiator for python. 10 | It contains python classes to create and send SCSI commands to devices 11 | accessible via: 12 | 13 | * SGIO: /dev/sg* devices using ioctl(SG_IO) 14 | Depends on [cython-sgio](https://github.com/python-scsi/cython-sgio). 15 | 16 | * iSCSI: iscsi://// 17 | Depends on [cython-iscsi](https://github.com/python-scsi/cython-iscsi). 18 | 19 | These classes also provide interfaces to marshall/unmarshall both CDBs 20 | as well as DATA-IN/OUT buffers. 21 | 22 | 23 | License 24 | ======= 25 | Python-scsi is distributed under LGPLv2.1 26 | Please see the LICENSE file for the full license text. 27 | 28 | 29 | Getting the sources 30 | =================== 31 | The module is hosted at https://github.com/python-scsi/python-scsi 32 | 33 | You can use git to checkout the latest version of the source code using: 34 | 35 | $ git clone git@github.com:python-scsi/python-scsi.git 36 | 37 | It is also available as a downloadable zip archive from: 38 | 39 | https://github.com/python-scsi/python-scsi/archive/master.zip 40 | 41 | 42 | Building and installing 43 | ======================= 44 | 45 | To build and install from the repository: 46 | 47 | python-scsi $ pip install .[iscsi,sgio] 48 | 49 | You can avoid installing the optional dependencies by omitting the "extras": 50 | 51 | python-scsi $ pip install . 52 | 53 | Tools (examples) 54 | ================ 55 | The tools directory contains example programs written against the python-scsi 56 | API. 57 | 58 | inquiry.py 59 | ---------- 60 | An example tool to send INQUIRY commands to a device. 61 | 62 | mtx.py 63 | ------ 64 | An example tool to operate a SCSI media changer. Similar to, but not as 65 | advanced as, the 'mtx' utility. 66 | 67 | 68 | Mailinglist 69 | =========== 70 | A mailinglist for python-scsi is available at: 71 | https://groups.google.com/forum/#!forum/python-scsi 72 | -------------------------------------------------------------------------------- /examples/read16.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | import sys 9 | 10 | from pyscsi.pyscsi.scsi import SCSI 11 | from pyscsi.utils import init_device 12 | 13 | 14 | def ba_to_int(ba): 15 | return sum(b for b in ba) 16 | 17 | 18 | def ba_to_hex(ba): 19 | result = "" 20 | for b in ba: 21 | result += hex(b)[2:] 22 | return result 23 | 24 | 25 | def main(device): 26 | with SCSI(device, blocksize=512) as s: 27 | r = s.read16( 28 | 1, 29 | 1, 30 | ).datain 31 | print("Read16 - GPT Header") 32 | print("==========================================\n") 33 | print("signature: %s" % r[:8]) 34 | print("revision: %.1f" % float(ba_to_int(r[8:12]))) 35 | print("header size: %s byte" % ba_to_int(r[12:16])) 36 | print("crc32 of header: %s" % ba_to_hex(r[16:20])) 37 | print("reserved: %s" % ba_to_int(r[20:24])) 38 | print("current LBA: %s" % ba_to_int(r[24:32])) 39 | print("backup LBA: %s" % ba_to_int(r[32:40])) 40 | print("first usable LBA for partitions: %s" % ba_to_int(r[40:48])) 41 | print("last usable LBA: %s" % ba_to_int(r[48:56])) 42 | print("Disk GUID: %s" % ba_to_hex(r[56:72])) 43 | print("Starting LBA of array of partition entries: %s" % ba_to_int(r[72:80])) 44 | print("number of partition entries in array: %s" % ba_to_int(r[80:84])) 45 | print("size of a single partition entry: %s" % ba_to_int(r[84:88])) 46 | print("crc32 of header: %s" % ba_to_hex(r[88:92])) 47 | 48 | 49 | if __name__ == "__main__": 50 | main(init_device(sys.argv[1])) 51 | -------------------------------------------------------------------------------- /examples/read16_with_contextmanger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | import sys 9 | 10 | from pyscsi.pyscsi.scsi_cdb_read16 import Read16 11 | from pyscsi.pyscsi.scsi_device import SCSIDevice 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils import init_device 14 | 15 | 16 | def ba_to_int(ba): 17 | return sum(b for b in ba) 18 | 19 | 20 | def ba_to_hex(ba): 21 | result = "" 22 | for b in ba: 23 | result += hex(b)[2:] 24 | return result 25 | 26 | 27 | def with_init_device(device): 28 | """ 29 | simple use case for a scsi device without the scsi helper. SCSIDevice has also a contextmanager so we can 30 | use the with statement but we need to take care of the initialization of a SCSICommand and the execution of the 31 | command with the device. 32 | """ 33 | with device as d: 34 | cmd = Read16(sbc.READ_16, blocksize=512, lba=1, tl=1) 35 | d.execute(cmd) 36 | print_output(cmd.datain) 37 | 38 | 39 | def without_init_device(device): 40 | """ 41 | simple use case for a scsi device without the scsi helper. SCSIDevice has also a contextmanager so we can 42 | use the with statement but we need to take care of the initialization of a SCSICommand and the execution of the 43 | command with the device. 44 | """ 45 | with SCSIDevice(device, False) as d: 46 | cmd = Read16(sbc.READ_16, blocksize=512, lba=1, tl=1) 47 | d.execute(cmd) 48 | print_output(cmd.datain) 49 | 50 | 51 | def print_output(r): 52 | print("Read16 - GPT Header") 53 | print("==========================================\n") 54 | print("signature: %s" % r[:8]) 55 | print("revision: %.1f" % float(ba_to_int(r[8:12]))) 56 | print("header size: %s byte" % ba_to_int(r[12:16])) 57 | print("crc32 of header: %s" % ba_to_hex(r[16:20])) 58 | print("reserved: %s" % ba_to_int(r[20:24])) 59 | print("current LBA: %s" % ba_to_int(r[24:32])) 60 | print("backup LBA: %s" % ba_to_int(r[32:40])) 61 | print("first usable LBA for partitions: %s" % ba_to_int(r[40:48])) 62 | print("last usable LBA: %s" % ba_to_int(r[48:56])) 63 | print("Disk GUID: %s" % ba_to_hex(r[56:72])) 64 | print("Starting LBA of array of partition entries: %s" % ba_to_int(r[72:80])) 65 | print("number of partition entries in array: %s" % ba_to_int(r[80:84])) 66 | print("size of a single partition entry: %s" % ba_to_int(r[84:88])) 67 | print("crc32 of header: %s" % ba_to_hex(r[88:92])) 68 | 69 | 70 | if __name__ == "__main__": 71 | print("we run with init_device helper") 72 | with_init_device(init_device(sys.argv[1])) 73 | print("we run without init_device helper") 74 | without_init_device(sys.argv[1]) 75 | -------------------------------------------------------------------------------- /examples/read_cd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-FileCopyrightText: 2021 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | 9 | import sys 10 | 11 | # from pyscsi.pyscsi import scsi_enum_readcd as READCD 12 | from pyscsi.pyscsi.scsi import SCSI 13 | from pyscsi.pyscsi.scsi_sense import SCSICheckCondition 14 | from pyscsi.utils import init_device 15 | 16 | 17 | def usage(): 18 | print( 19 | "Usage: read_cd.py [-lba ] [-tl ] [-est ] [-dap ] [-mcsb ] [-c2ei ] [-scsb ] " 20 | ) 21 | 22 | 23 | def atoi(s): 24 | if s[:2] == "0x": 25 | return int(s, 16) 26 | else: 27 | return int(s, 10) 28 | 29 | 30 | def main(): 31 | i = 1 32 | lba = 0 33 | tl = 1 34 | est = 0 35 | dap = 0 36 | mcsb = 0x02 37 | c2ei = 0 38 | scsb = 0 39 | while i < len(sys.argv): 40 | if sys.argv[i] == "--help": 41 | return usage() 42 | if sys.argv[i] == "-lba": 43 | del sys.argv[i] 44 | lba = atoi(sys.argv[i]) 45 | del sys.argv[i] 46 | continue 47 | if sys.argv[i] == "-tl": 48 | del sys.argv[i] 49 | tl = atoi(sys.argv[i]) 50 | del sys.argv[i] 51 | continue 52 | if sys.argv[i] == "-est": 53 | del sys.argv[i] 54 | est = atoi(sys.argv[i]) 55 | del sys.argv[i] 56 | continue 57 | if sys.argv[i] == "-dap": 58 | del sys.argv[i] 59 | dap = atoi(sys.argv[i]) 60 | del sys.argv[i] 61 | continue 62 | if sys.argv[i] == "-mcsb": 63 | del sys.argv[i] 64 | mcsb = atoi(sys.argv[i]) 65 | del sys.argv[i] 66 | continue 67 | if sys.argv[i] == "-c2ei": 68 | del sys.argv[i] 69 | c2ei = atoi(sys.argv[i]) 70 | del sys.argv[i] 71 | continue 72 | if sys.argv[i] == "-scsb": 73 | del sys.argv[i] 74 | scsb = atoi(sys.argv[i]) 75 | del sys.argv[i] 76 | continue 77 | i += 1 78 | 79 | if len(sys.argv) < 2: 80 | return usage() 81 | 82 | device = init_device(sys.argv[1]) 83 | 84 | s = SCSI(device) 85 | 86 | cmd = s.readcd(lba=lba, tl=tl, est=est, dap=dap, mcsb=mcsb, c2ei=c2ei, scsb=scsb) 87 | print(cmd.result) 88 | 89 | 90 | if __name__ == "__main__": 91 | main() 92 | -------------------------------------------------------------------------------- /examples/read_disc_information.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-FileCopyrightText: 2021 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | 9 | import sys 10 | 11 | from pyscsi.pyscsi.scsi import SCSI 12 | from pyscsi.pyscsi.scsi_sense import SCSICheckCondition 13 | from pyscsi.utils import init_device 14 | 15 | 16 | def usage(): 17 | print("Usage: read_disc_information.py ") 18 | 19 | 20 | def pe(cmd, res, name): 21 | e = getattr(cmd, name) 22 | print(name + ": %s (%d)" % (e[res[name.lower()]], res[name.lower()])) 23 | 24 | 25 | def pi(res, name): 26 | print(name + ": %d" % (res[name.lower()])) 27 | 28 | 29 | def main(): 30 | i = 1 31 | data_type = 0 32 | 33 | while i < len(sys.argv): 34 | if sys.argv[i] == "--help": 35 | return usage() 36 | if sys.argv[i] == "--data-type": 37 | del sys.argv[i] 38 | if sys.argv[i][:2] == "0x": 39 | data_type = int(sys.argv[i], 16) 40 | else: 41 | data_type = int(sys.argv[i], 10) 42 | del sys.argv[i] 43 | continue 44 | i += 1 45 | 46 | if len(sys.argv) < 2: 47 | return usage() 48 | 49 | device = init_device(sys.argv[1]) 50 | 51 | with SCSI(device) as s: 52 | try: 53 | s.testunitready() 54 | 55 | cmd = s.readdiscinformation(data_type, alloc_len=512) 56 | i = cmd.result 57 | pe(cmd, i, "DISC_INFORMATION_DATA_TYPE") 58 | if ( 59 | i["disc_information_data_type"] 60 | == cmd.DISC_INFORMATION_DATA_TYPE.STANDARD_DISC_INFORMATION 61 | ): 62 | pi(i, "ERASABLE") 63 | pe(cmd, i, "STATE_OF_LAST_SESSION") 64 | pe(cmd, i, "DISC_STATUS") 65 | pi(i, "NUMBER_OF_FIRST_TRACK_ON_DISC") 66 | pi(i, "NUMBER_OF_SESSIONS") 67 | pi(i, "FIRST_TRACK_NUMBER_IN_LAST_SESSION") 68 | pi(i, "LAST_TRACK_NUMBER_IN_LAST_SESSION") 69 | pi(i, "DID_V") 70 | pi(i, "DBC_V") 71 | pi(i, "URU") 72 | pi(i, "DAC_V") 73 | pi(i, "LEGACY") 74 | pi(i, "BG_FORMAT_STATUS") 75 | pe(cmd, i, "DISC_TYPE") 76 | pi(i, "DISC_IDENTIFICATION") 77 | print( 78 | "LAST_SESSION_LEAD_IN_START_ADDRESS", 79 | i["last_session_lead_in_start_address"], 80 | ) 81 | print( 82 | "LAST_POSSIBLE_LEAD_OUT_START_ADDRESS", 83 | i["last_possible_lead_out_start_address"], 84 | ) 85 | print("DISC_BAR_CODE", i["disc_bar_code"]) 86 | else: 87 | for k, v in i.items(): 88 | print("%s - %s" % (k, v)) 89 | 90 | except SCSICheckCondition as ex: 91 | # if you want a print out of the sense data dict uncomment the next line 92 | # ex.show_data = True 93 | print(ex) 94 | 95 | 96 | if __name__ == "__main__": 97 | main() 98 | -------------------------------------------------------------------------------- /examples/readcapacity10.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | 9 | import sys 10 | 11 | from pyscsi.pyscsi.scsi import SCSI 12 | from pyscsi.utils import init_device 13 | 14 | 15 | def main(device): 16 | with SCSI(device) as s: 17 | print("ReadCapacity10") 18 | print("==========================================\n") 19 | r = s.readcapacity10().result 20 | for k, v in r.items(): 21 | print("%s - %s" % (k, v)) 22 | 23 | 24 | if __name__ == "__main__": 25 | main(init_device(sys.argv[1])) 26 | -------------------------------------------------------------------------------- /examples/readcapacity16.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | 9 | import sys 10 | 11 | from pyscsi.pyscsi.scsi import SCSI 12 | from pyscsi.utils import init_device 13 | 14 | 15 | def main(device): 16 | with SCSI(device) as s: 17 | print("ReadCapacity16") 18 | print("==========================================\n") 19 | r = s.readcapacity16().result 20 | for k, v in r.items(): 21 | print("%s - %s" % (k, v)) 22 | 23 | 24 | if __name__ == "__main__": 25 | main(init_device(sys.argv[1])) 26 | -------------------------------------------------------------------------------- /examples/reportluns.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | 9 | # 10 | # A simple example to get a dict with reported luns 11 | # 12 | import sys 13 | 14 | from pyscsi.pyscsi.scsi import SCSI 15 | from pyscsi.utils import init_device 16 | 17 | 18 | def main(device): 19 | with SCSI(device) as s: 20 | print("ReportLuns") 21 | print("==========================================\n") 22 | r = s.reportluns().result 23 | for k, v in r.items(): 24 | print("%s - %s" % (k, v)) 25 | 26 | 27 | if __name__ == "__main__": 28 | main(init_device(sys.argv[1])) 29 | -------------------------------------------------------------------------------- /examples/reportpriority.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | 9 | # 10 | # A simple example to get a dict with reported luns 11 | # 12 | import sys 13 | 14 | from pyscsi.pyscsi.scsi import SCSI 15 | from pyscsi.utils import init_device 16 | 17 | 18 | def main(device): 19 | with SCSI(device) as s: 20 | s.testunitready() 21 | print("ReportPriority") 22 | print("==========================================\n") 23 | r = s.reportpriority().result 24 | for k, v in r.items(): 25 | print("%s - %s" % (k, v)) 26 | 27 | 28 | if __name__ == "__main__": 29 | main(init_device(sys.argv[1])) 30 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | ; SPDX-FileCopyrightText: 2020 The python-scsi Authors 2 | ; 3 | ; SPDX-License-Identifier: MIT 4 | 5 | [mypy] 6 | python_version = 3.7 7 | 8 | [mypy-setuptools] 9 | ignore_missing_imports = True 10 | 11 | [mypy-setuptools_scm] 12 | ignore_missing_imports = True 13 | 14 | [mypy-sgio] 15 | ignore_missing_imports = True 16 | 17 | [mypy-iscsi] 18 | ignore_missing_imports = True 19 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | [build-system] 6 | requires = [ 7 | 'setuptools >= 42', 8 | 'wheel', 9 | 'setuptools_scm[toml]>=3.4', 10 | ] 11 | 12 | [tool.isort] 13 | # These settings ensure that black and isort don't disagree on the imports. 14 | line_length = 88 15 | multi_line_output = 3 16 | include_trailing_comma = true 17 | 18 | known_third_party = ['iscsi', 'sgio'] 19 | 20 | [tool.setuptools_scm] 21 | -------------------------------------------------------------------------------- /pyscsi/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | from .pyscsi import * 6 | from .utils import * 7 | -------------------------------------------------------------------------------- /pyscsi/pyiscsi/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | __all__ = ["iscsi_device"] 6 | -------------------------------------------------------------------------------- /pyscsi/pyiscsi/iscsi_device.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2018 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | import pyscsi.pyscsi.scsi_enum_command as scsi_enum_command 9 | from pyscsi.pyscsi.scsi_exception import SCSIDeviceCommandExceptionMeta as ExMETA 10 | 11 | try: 12 | import iscsi 13 | 14 | _has_iscsi = True 15 | except ImportError as e: 16 | _has_iscsi = False 17 | 18 | 19 | class ISCSIDevice(metaclass=ExMETA): 20 | """ 21 | The iscsi device class 22 | 23 | By default it gets the SPC opcodes assigned so it's always possible to issue 24 | a inquiry command to the device. This is important since the the Command will 25 | figure out the opcode from the SCSIDevice first to use it for building the cdb. 26 | This means after the that it's possible to use the proper OpCodes for the device. 27 | A basic workflow for using a device would be: 28 | - try to open the device passed by the device arg 29 | - create a Inquiry instance, with the default opcodes of the device 30 | - execute the inquiry with the device 31 | - unmarshall the datain from the inquiry command to figure out the device type 32 | - assign the proper Opcode for the device type (it would also work just to use the 33 | opcodes without assigning them to the device since the command builds the cdb 34 | and the device just executes) 35 | 36 | Note: The workflow above is already implemented in the SCSI class 37 | """ 38 | 39 | def __init__(self, device, initiator_name=""): 40 | """ 41 | initialize a new instance of a ISCSIDevice 42 | 43 | :param device: a url string 44 | :param initiator_name: an initiator_name string 45 | """ 46 | self._opcodes = scsi_enum_command.spc 47 | self._file_name = device 48 | self._iscsi = None 49 | self._iscsi_url = None 50 | self._initiator_name = initiator_name 51 | if _has_iscsi and device[:8] == "iscsi://": 52 | self.open(device) 53 | else: 54 | raise NotImplementedError("No backend implemented for %s" % device) 55 | 56 | def __enter__(self): 57 | return self 58 | 59 | def __exit__(self, exc_type, exc_val, exc_tb): 60 | """ 61 | 62 | :param exc_type: 63 | :param exc_val: 64 | :param exc_tb: 65 | :return: 66 | """ 67 | # we may need to do more teardown here ? 68 | self.close() 69 | 70 | def open(self, device): 71 | if len(self._initiator_name): 72 | self._iscsi = iscsi.Context(self._initiator_name) 73 | else: 74 | self._iscsi = iscsi.Context(device) 75 | self._iscsi_url = iscsi.URL(self._iscsi, self._file_name) 76 | self._iscsi.set_targetname(self._iscsi_url.target) 77 | self._iscsi.set_session_type(iscsi.ISCSI_SESSION_NORMAL) 78 | self._iscsi.set_header_digest(iscsi.ISCSI_HEADER_DIGEST_NONE_CRC32C) 79 | self._iscsi.connect(self._iscsi_url.portal, self._iscsi_url.lun) 80 | 81 | def close(self): 82 | self._iscsi.disconnect() 83 | 84 | def execute(self, cmd, en_raw_sense=False): 85 | """ 86 | execute a scsi command 87 | :param cmd: a scsi command 88 | """ 89 | dir = iscsi.SCSI_XFER_NONE 90 | xferlen = 0 91 | if len(cmd.datain): 92 | dir = iscsi.SCSI_XFER_READ 93 | xferlen = len(cmd.datain) 94 | if len(cmd.dataout): 95 | dir = iscsi.SCSI_XFER_WRITE 96 | xferlen = len(cmd.dataout) 97 | task = iscsi.Task(cmd.cdb, dir, xferlen) 98 | self._iscsi.command(self._iscsi_url.lun, task, cmd.dataout, cmd.datain) 99 | if task.status == scsi_enum_command.SCSI_STATUS.CHECK_CONDITION: 100 | if not cmd.sense: 101 | try: 102 | cmd.sense = task.raw_sense 103 | except AttributeError: 104 | pass 105 | # Match recent addition to SCSIDevice 106 | if en_raw_sense: 107 | cmd.raw_sense_data = cmd.sense 108 | # No sense information propagated. 109 | raise self.CheckCondition(cmd.sense) 110 | if task.status == scsi_enum_command.SCSI_STATUS.GOOD: 111 | return 112 | if task.status == scsi_enum_command.SCSI_STATUS.RESERVATION_CONFLICT: 113 | raise self.ReservationConflict() 114 | if task.status == scsi_enum_command.SCSI_STATUS.TASK_ABORTED: 115 | raise self.TaskAborted() 116 | if task.status == scsi_enum_command.SCSI_STATUS.BUSY: 117 | raise self.BusyStatus() 118 | if task.status == scsi_enum_command.SCSI_STATUS.TASK_SET_FULL: 119 | raise self.TaskSetFull() 120 | if task.status == scsi_enum_command.SCSI_STATUS.ACA_ACTIVE: 121 | raise self.ACAActive() 122 | if task.status == scsi_enum_command.SCSI_STATUS.CONDITIONS_MET: 123 | raise self.ConditionsMet() 124 | raise RuntimeError 125 | 126 | @property 127 | def opcodes(self): 128 | return self._opcodes 129 | 130 | @opcodes.setter 131 | def opcodes(self, value): 132 | self._opcodes = value 133 | 134 | @property 135 | def devicetype(self): 136 | return self._devicetype 137 | 138 | @devicetype.setter 139 | def devicetype(self, value): 140 | self._devicetype = value 141 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | __all__ = [ 6 | "scsi", 7 | "scsi_cdb_exchangemedium", 8 | "scsi_cdb_getlbastatus", 9 | "scsi_cdb_initelementstatus", 10 | "scsi_cdb_initelementstatuswithrange", 11 | "scsi_cdb_inquiry", 12 | "scsi_cdb_modesense6", 13 | "scsi_cdb_modesense10", 14 | "scsi_cdb_movemedium", 15 | "scsi_cdb_openclose_exportimport_element", 16 | "scsi_cdb_positiontoelement", 17 | "scsi_cdb_preventallow_mediumremoval", 18 | "scsi_cdb_read10", 19 | "scsi_cdb_read12", 20 | "scsi_cdb_read16", 21 | "scsi_cdb_readcapacity16", 22 | "scsi_cdb_readcapacity10", 23 | "scsi_cdb_readcd", 24 | "scsi_cdb_readelementstatus", 25 | "scsi_cdb_readdiscinformation", 26 | "scsi_cdb_report_luns", 27 | "scsi_cdb_report_priority", 28 | "scsi_cdb_synchronize_cache10", 29 | "scsi_cdb_synchronize_cache16", 30 | "scsi_cdb_testunitready", 31 | "scsi_cdb_write10", 32 | "scsi_cdb_write12", 33 | "scsi_cdb_write16", 34 | "scsi_cdb_writesame10", 35 | "scsi_cdb_writesame16", 36 | "scsi_command", 37 | "scsi_device", 38 | "scsi_exception", 39 | "scsi_sense", 40 | ] 41 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_atapassthrough12.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2022 by Erick 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI ata-pass-through command and definitions 12 | # 13 | 14 | 15 | class ATAPassThrough12(SCSICommand): 16 | """ 17 | A class to send a ATAPassThrough12 command to a ata device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "protocol": [0x1E, 1], 23 | "t_length": [0x03, 2], 24 | "byte_block": [0x04, 2], 25 | "t_dir": [0x08, 2], 26 | "t_type": [0x10, 2], 27 | "ck_cond": [0x20, 2], 28 | "off_line": [0xC0, 2], 29 | "fetures": [0xFF, 3], 30 | "count": [0xFF, 4], 31 | "lba": [0xFFFFFF, 5], 32 | "device": [0xFF, 8], 33 | "command": [0xFF, 9], 34 | "control": [0xFF, 11], 35 | } 36 | 37 | def __init__( 38 | self, 39 | opcode, 40 | protocal, 41 | t_length, 42 | byte_block, 43 | t_dir, 44 | t_type, 45 | off_line, 46 | fetures, 47 | count, 48 | lba, 49 | command, 50 | blocksize=0, 51 | extra_tl=None, 52 | ck_cond=0, 53 | device=0x00, 54 | control=0, 55 | data=None, 56 | ): 57 | """ 58 | initialize a new instance 59 | 60 | :param opcode: a OpCode instance 61 | :param protocal: ATAPassthrough12 PROTOCOL field 62 | :param t_length: ATAPassthrough12 t_length field 63 | :param byte_block: ATAPassthrough12 byte_block field 64 | :param t_dir: ATAPassthrough12 t_dir field 65 | :param t_type: ATAPassthrough12 t_type field 66 | :param off_line: ATAPassThrough12 off_line field 67 | :param fetures: ATAPassThrough12 fetures field 68 | :param count: ATAPassThrough12 count field 69 | :param lba: ATAPassThrough12 lba field 70 | :param command: ATAPassThrough12 command field 71 | :param blocksize=0: a blocksize 72 | :param extra_tl=None: if t_length=3, can fix the transfer length in this option 73 | :param ck_cond=0: ATAPassThrough12 ck_cond field 74 | :param device=0: ATAPassThrough12 device field 75 | :param control=0: ATAPassThrough12 control field 76 | :param data=None: a byte array with data, if command need data-in OR data-out 77 | """ 78 | tl = 0 79 | if t_length == 1: 80 | # The transfer length is an unsigned integer specified in the FEATURES (7:0) field and, 81 | # for the ATA PASS-THROUGH (16) command and the ATA PASS-THROUGH (32) command, the 82 | # FEATURES (15:8) field. 83 | tl = fetures 84 | elif t_length == 2: 85 | # The transfer length is an unsigned integer specified in the COUNT (7:0) field and, for 86 | # the ATA PASS-THROUGH(16) command and the ATA PASS-THROUGH (32) command, the COUNT(15:8) 87 | # field. 88 | tl = count 89 | elif t_length == 3: 90 | # The transfer length is an unsigned integer specified in the TPSIU. 91 | # It's not the tool's job to check transfer length in different commands, can be set it by 92 | # extra_tl 93 | if extra_tl is not None: 94 | tl = extra_tl 95 | 96 | if byte_block and (not t_type) and t_length: 97 | # fix the blocksize to 512 98 | blocksize = 512 99 | elif byte_block and t_type and t_length: 100 | # The number of ATA logical sector size blocks to be transferred, set it in param blocksize 101 | if blocksize == 0: 102 | raise SCSICommand.MissingBlocksizeException # pylint: disable=maybe-no-member 103 | elif (not byte_block) and t_length: 104 | blocksize = 1 105 | elif not t_length: 106 | ## No data to transfer 107 | blocksize = 0 108 | 109 | if t_dir == 0: 110 | # transfer data from the application client to the ATA device 111 | dataout_alloclen = tl * blocksize 112 | datain_alloclen = 0 113 | else: 114 | # transfer data from the ATA device to the application client 115 | dataout_alloclen = 0 116 | datain_alloclen = tl * blocksize 117 | 118 | SCSICommand.__init__(self, opcode, dataout_alloclen, datain_alloclen) 119 | # re-set data 120 | if data: 121 | if t_dir == 0: 122 | self.dataout = data 123 | else: 124 | self.datain = data 125 | self.cdb = self.build_cdb( 126 | opcode=self.opcode.value, 127 | protocol=protocal, 128 | t_length=t_length, 129 | byte_block=byte_block, 130 | t_dir=t_dir, 131 | t_type=t_type, 132 | off_line=off_line, 133 | fetures=fetures, 134 | count=count, 135 | lba=ATAPassThrough12.scsi_to_ata_lba_convert(lba), 136 | command=command, 137 | control=control, 138 | ck_cond=ck_cond, 139 | device=device, 140 | ) 141 | 142 | @staticmethod 143 | def scsi_to_ata_lba_convert(lba): 144 | """ 145 | This function converts the lba to the ATAPassThrough12->lba field. 146 | 147 | :param lba: lba(int) to convert 148 | :return: ATAPassThrough12->lba 149 | """ 150 | result = 0 151 | result += (lba & 0xFF) << 16 152 | result += ((lba >> 8) & 0xFF) << 8 153 | result += (lba >> 16) & 0xFF 154 | return result 155 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_atapassthrough16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2022 by Erick 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI ata-pass-through command and definitions 12 | # 13 | 14 | 15 | class ATAPassThrough16(SCSICommand): 16 | """ 17 | A class to send a ATAPassThrough16 command to a ata device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "extend": [0x01, 1], 23 | "protocol": [0x1E, 1], 24 | "t_length": [0x03, 2], 25 | "byte_block": [0x04, 2], 26 | "t_dir": [0x08, 2], 27 | "t_type": [0x10, 2], 28 | "ck_cond": [0x20, 2], 29 | "off_line": [0xC0, 2], 30 | "fetures": [0xFFFF, 3], 31 | "count": [0xFFFF, 5], 32 | "lba": [0xFFFFFFFFFFFF, 7], 33 | "device": [0xFF, 13], 34 | "command": [0xFF, 14], 35 | "control": [0xFF, 15], 36 | } 37 | 38 | def __init__( 39 | self, 40 | opcode, 41 | protocal, 42 | t_length, 43 | byte_block, 44 | t_dir, 45 | t_type, 46 | off_line, 47 | fetures, 48 | count, 49 | lba, 50 | command, 51 | blocksize=0, 52 | extra_tl=None, 53 | ck_cond=0, 54 | device=0x00, 55 | control=0, 56 | data=None, 57 | extend=1, 58 | ): 59 | """ 60 | initialize a new instance 61 | 62 | :param opcode: a OpCode instance 63 | :param protocal: ATAPassthrough16 PROTOCOL field 64 | :param t_length: ATAPassthrough16 t_length field 65 | :param byte_block: ATAPassthrough16 byte_block field 66 | :param t_dir: ATAPassthrough16 t_dir field 67 | :param t_type: ATAPassthrough16 t_type field 68 | :param off_line: ATAPassthrough16 off_line field 69 | :param fetures: ATAPassthrough16 fetures field 70 | :param count: ATAPassthrough16 count field 71 | :param lba: ATAPassthrough16 lba field 72 | :param command: ATAPassthrough16 command field 73 | :param blocksize=None: a blocksize 74 | :param extra_tl=None: if t_length=3, can fix the transfer length in this option 75 | :param ck_cond=0: ATAPassthrough16 ck_cond field 76 | :param device=0: ATAPassthrough16 device field 77 | :param control=0: ATAPassthrough16 control field 78 | :param data=None: a byte array with data, if command need data-in OR data-out 79 | :param extend=1: ATAPassthrough16 extend field 80 | """ 81 | tl = 0 82 | if t_length == 1: 83 | # The transfer length is an unsigned integer specified in the FEATURES (7:0) field and, 84 | # for the ATA PASS-THROUGH (16) command and the ATA PASS-THROUGH (32) command, the 85 | # FEATURES (15:8) field. 86 | tl = fetures 87 | elif t_length == 2: 88 | # The transfer length is an unsigned integer specified in the COUNT (7:0) field and, for 89 | # the ATA PASS-THROUGH(16) command and the ATA PASS-THROUGH (32) command, the COUNT(15:8) 90 | # field. 91 | tl = count 92 | elif t_length == 3: 93 | # The transfer length is an unsigned integer specified in the TPSIU 94 | # It's not the tool's job to check transfer length in different commands, can be set it by 95 | # extra_tl 96 | if extra_tl is not None: 97 | tl = extra_tl 98 | 99 | if byte_block and (not t_type) and t_length: 100 | # fix the blocksize to 512 101 | blocksize = 512 102 | elif byte_block and t_type and t_length: 103 | # The number of ATA logical sector size blocks to be transferred, set it in param blocksize 104 | if blocksize == 0: 105 | raise SCSICommand.MissingBlocksizeException # pylint: disable=maybe-no-member 106 | elif (not byte_block) and t_length: 107 | blocksize = 1 108 | elif not t_length: 109 | ## No data to transfer 110 | blocksize = 0 111 | 112 | if t_dir == 0: 113 | # transfer data from the application client to the ATA device 114 | dataout_alloclen = tl * blocksize 115 | datain_alloclen = 0 116 | else: 117 | # transfer data from the ATA device to the application client 118 | dataout_alloclen = 0 119 | datain_alloclen = tl * blocksize 120 | 121 | SCSICommand.__init__(self, opcode, dataout_alloclen, datain_alloclen) 122 | # re-set data 123 | if data: 124 | if t_dir == 0: 125 | self.dataout = data 126 | else: 127 | self.datain = data 128 | self.cdb = self.build_cdb( 129 | opcode=self.opcode.value, 130 | extend=extend, 131 | protocol=protocal, 132 | t_length=t_length, 133 | byte_block=byte_block, 134 | t_dir=t_dir, 135 | t_type=t_type, 136 | off_line=off_line, 137 | fetures=fetures, 138 | count=count, 139 | lba=ATAPassThrough16.scsi_to_ata_lba_convert(lba), 140 | command=command, 141 | control=control, 142 | ck_cond=ck_cond, 143 | device=device, 144 | ) 145 | 146 | @staticmethod 147 | def scsi_to_ata_lba_convert(lba): 148 | """ 149 | This function converts the lba to the ATAPassThrough16->lba field. 150 | 151 | :param lba: lba(int) to convert 152 | :return: ATAPassThrough16->lba 153 | """ 154 | result = 0 155 | result += (lba & 0xFF) << 32 156 | result += ((lba >> 8) & 0xFF) << 16 157 | result += (lba >> 16) & 0xFF 158 | result += ((lba >> 24) & 0xFF) << 40 159 | result += ((lba >> 32) & 0xFF) << 24 160 | result += ((lba >> 40) & 0xFF) << 8 161 | return result 162 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_exchangemedium.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI ExchangeMedium command and definitions 12 | # 13 | 14 | 15 | class ExchangeMedium(SCSICommand): 16 | """ 17 | A class to hold information from a ExchangeMedium command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "medium_transport_address": [0xFFFF, 2], 23 | "source_address": [0xFFFF, 4], 24 | "first_destination_address": [0xFFFF, 6], 25 | "second_destination_address": [0xFFFF, 8], 26 | "inv2": [0x01, 10], 27 | "inv1": [0x02, 10], 28 | } 29 | 30 | def __init__(self, opcode, xfer, source, dest1, dest2, inv1=0, inv2=0): 31 | """ 32 | initialize a new instance 33 | 34 | :param opcode: a OpCode instance 35 | :param xfer: medium transfer address 36 | :param source: source address 37 | :param dest1: first destination address 38 | :param dest2: second destination address 39 | :param inv1: value indicating if first destination should be inverted 40 | :param inv2: value indicating if scond destination should be inverted 41 | """ 42 | SCSICommand.__init__(self, opcode, 0, 0) 43 | 44 | self.cdb = self.build_cdb( 45 | opcode=self.opcode.value, 46 | medium_transport_address=xfer, 47 | source_address=source, 48 | first_destination_address=dest1, 49 | second_destination_address=dest2, 50 | inv1=inv1, 51 | inv2=inv2, 52 | ) 53 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_getlbastatus.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | from pyscsi.utils.converter import ( 10 | decode_bits, 11 | encode_dict, 12 | scsi_ba_to_int, 13 | scsi_int_to_ba, 14 | ) 15 | 16 | # 17 | # SCSI GetLBAStatus command and definitions 18 | # 19 | 20 | 21 | class GetLBAStatus(SCSICommand): 22 | """ 23 | A class to hold information from a GetLBAStatus command to a scsi device 24 | """ 25 | 26 | _cdb_bits = { 27 | "opcode": [0xFF, 0], 28 | "service_action": [0x1F, 1], 29 | "lba": [0xFFFFFFFFFFFFFFFF, 2], 30 | "alloc_len": [0xFFFFFFFF, 10], 31 | } 32 | _datain_bits = { 33 | "lba": [0xFFFFFFFFFFFFFFFF, 0], 34 | "num_blocks": [0xFFFFFFFF, 8], 35 | "p_status": [0x0F, 12], 36 | } 37 | 38 | def __init__(self, opcode, lba, alloclen=16384): 39 | """ 40 | initialize a new instance 41 | 42 | :param opcode: a OpCode instance 43 | :param lba: a local block address 44 | :param alloclen: the max number of bytes allocated for the data_in buffer 45 | """ 46 | SCSICommand.__init__(self, opcode, 0, alloclen) 47 | self.cdb = self.build_cdb( 48 | opcode=self.opcode.value, 49 | service_action=self.opcode.serviceaction.GET_LBA_STATUS, 50 | lba=lba, 51 | alloc_len=alloclen, 52 | ) 53 | 54 | @classmethod 55 | def unmarshall_datain(cls, data): 56 | """ 57 | Unmarshall the GetLBAStatus datain. 58 | 59 | :param data: a byte array 60 | :return result: a dict 61 | """ 62 | result = {} 63 | _data = data[8 : scsi_ba_to_int(data[:4]) + 4] 64 | _lbas = [] 65 | while len(_data): 66 | _r = {} 67 | decode_bits(_data[:16], cls._datain_bits, _r) 68 | 69 | _lbas.append(_r) 70 | _data = _data[16:] 71 | 72 | result.update({"lbas": _lbas}) 73 | return result 74 | 75 | @classmethod 76 | def marshall_datain(cls, data): 77 | """ 78 | Marshall the GetLBAStatus datain. 79 | 80 | :param data: a dict 81 | :return result: a byte array 82 | """ 83 | result = bytearray(8) 84 | if "lbas" not in data: 85 | result[:4] = scsi_int_to_ba(len(result) - 4, 4) 86 | return result 87 | 88 | for l in data["lbas"]: 89 | _r = bytearray(16) 90 | encode_dict(l, cls._datain_bits, _r) 91 | 92 | result += _r 93 | 94 | result[:4] = scsi_int_to_ba(len(result) - 4, 4) 95 | return result 96 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_initelementstatus.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI InitializeElementStatus command and definitions 12 | # 13 | 14 | 15 | class InitializeElementStatus(SCSICommand): 16 | """ 17 | A class to hold information from a InitializeElementStatus command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | } 23 | 24 | def __init__(self, opcode): 25 | """ 26 | initialize a new instance 27 | 28 | :param opcode: a OpCode instance 29 | :param scsi: 30 | """ 31 | SCSICommand.__init__(self, opcode, 0, 0) 32 | 33 | self.cdb = self.build_cdb( 34 | opcode=self.opcode.value, 35 | ) 36 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_initelementstatuswithrange.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI InitializeElementStatusWithRange command and definitions 12 | # 13 | 14 | 15 | class InitializeElementStatusWithRange(SCSICommand): 16 | """ 17 | A class to hold information from a InitializeElementStatusWithRange command 18 | to a scsi device 19 | """ 20 | 21 | _cdb_bits = { 22 | "opcode": [0xFF, 0], 23 | "fast": [0x02, 1], 24 | "range": [0x01, 1], 25 | "starting_element_address": [0xFFFF, 2], 26 | "number_of_elements": [0xFFFF, 6], 27 | } 28 | 29 | def __init__(self, opcode, xfer, elements, rng=0, fast=0): 30 | """ 31 | initialize a new instance 32 | 33 | :param opcode: a OpCode instance 34 | :param xfer: starting element address 35 | :param elements: number of elements 36 | :param rng: range indicates if all elements should be checked, if set to 1 xfer 37 | and elements are ignored 38 | :param fast: fast , if set to 1 scan for media presence only. If set to 0 scan 39 | elements for all relevant status. 40 | """ 41 | SCSICommand.__init__(self, opcode, 0, 0) 42 | 43 | self.cdb = self.build_cdb( 44 | opcode=self.opcode.value, 45 | starting_element_address=xfer, 46 | number_of_elements=elements, 47 | range=rng, 48 | fast=fast, 49 | ) 50 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_movemedium.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI MoveMedium command and definitions 12 | # 13 | 14 | 15 | class MoveMedium(SCSICommand): 16 | """ 17 | A class to hold information from a MoveMedium command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "medium_transport_address": [0xFFFF, 2], 23 | "source_address": [0xFFFF, 4], 24 | "destination_address": [0xFFFF, 6], 25 | "invert": [0x01, 10], 26 | } 27 | 28 | def __init__(self, opcode, xfer, source, dest, invert=0): 29 | """ 30 | initialize a new instance 31 | 32 | :param opcode: a OpCode instance 33 | :param xfer: medium transport address 34 | :param source: source address 35 | :param dest: destination address 36 | :param invert: invert can be 0 or 1 37 | """ 38 | SCSICommand.__init__(self, opcode, 0, 0) 39 | self.cdb = self.build_cdb( 40 | opcode=self.opcode.value, 41 | medium_transport_address=xfer, 42 | source_address=source, 43 | destination_address=dest, 44 | invert=invert, 45 | ) 46 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_openclose_exportimport_element.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI OpenCloseImportExportElement command and definitions 12 | # 13 | 14 | 15 | class OpenCloseImportExportElement(SCSICommand): 16 | """ 17 | A class to hold information from a OpenCloseImportExportElement 18 | command to a scsi device 19 | """ 20 | 21 | _cdb_bits = { 22 | "opcode": [0xFF, 0], 23 | "element_address": [0xFFFF, 2], 24 | "action_code": [0x1F, 4], 25 | } 26 | 27 | def __init__(self, opcode, xfer, acode, **kwargs): 28 | """ 29 | initialize a new instance 30 | 31 | :param opcode: a OpCode instance 32 | :param xfer: element address 33 | :param acode: action code 34 | """ 35 | SCSICommand.__init__(self, opcode, 0, 0) 36 | 37 | self.cdb = self.build_cdb( 38 | opcode=self.opcode.value, 39 | element_address=xfer, 40 | action_code=acode, 41 | ) 42 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_persistentreserveout.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # Copyright (C) 2023 by Brian Meagher 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | from pyscsi.pyscsi.scsi_cdb_persistentreservein import PersistentReserveInReadFullStatus 10 | from pyscsi.pyscsi.scsi_command import SCSICommand 11 | from pyscsi.pyscsi.scsi_enum_persistentreserve import * 12 | from pyscsi.utils.converter import ( 13 | decode_bits, 14 | encode_dict, 15 | scsi_ba_to_int, 16 | scsi_int_to_ba, 17 | ) 18 | 19 | # 20 | # SCSI PersistentReserveOut command and definitions 21 | # 22 | 23 | __all__ = ["PersistentReserveOut"] 24 | 25 | 26 | class PersistentReserveOut(SCSICommand): 27 | """ 28 | A class to hold information from a PersistentReserveOut command to a scsi device 29 | """ 30 | 31 | _cdb_bits = { 32 | "opcode": [0xFF, 0], 33 | "service_action": [0x1F, 1], 34 | "scope": [0xF0, 2], 35 | "pr_type": [0x0F, 2], 36 | "parameter_list_length": [0xFFFFFFFF, 5], 37 | } 38 | 39 | _basic_parameter_list_bits = { 40 | "reservation_key": [0xFFFFFFFFFFFFFFFF, 0], 41 | "service_action_reservation_key": [0xFFFFFFFFFFFFFFFF, 8], 42 | "spec_i_pt": [0x08, 20], 43 | "all_tg_pt": [0x04, 20], 44 | "aptpl": [0x01, 20], 45 | } 46 | 47 | _ram_parameter_list_bits = { 48 | "reservation_key": [0xFFFFFFFFFFFFFFFF, 0], 49 | "service_action_reservation_key": [0xFFFFFFFFFFFFFFFF, 8], 50 | "unreg": [0x02, 17], 51 | "aptpl": [0x01, 17], 52 | "relative_target_port_id": [0xFFFF, 18], 53 | "transportid_length": [0xFFFFFFFF, 20], 54 | } 55 | 56 | @classmethod 57 | def marshall_dataout(cls, opcode, service_action, data): 58 | """ 59 | Marshall the PersistentReserveOut dataout. 60 | 61 | Depending on the service action, this will be either the 'Basic 62 | PERSISTENT RESERVE OUT parameter list' (SPC-5 6.17.3) or the 63 | 'PERSISTENT RESERVE OUT command with REGISTER AND MOVE service action 64 | parameter list' (SPC-5 6.17.4) 65 | 66 | :param data: a dict with data 67 | :return result: a byte array 68 | """ 69 | if service_action == opcode.serviceaction.REGISTER_AND_MOVE: 70 | _d = data.copy() 71 | result = bytearray(24) 72 | if _d.get("transport_id"): 73 | transportID = PersistentReserveInReadFullStatus.marshall_transport_id( 74 | _d["transport_id"] 75 | ) 76 | _d["transportid_length"] = len(transportID) 77 | encode_dict(_d, cls._ram_parameter_list_bits, result) 78 | return result + transportID 79 | else: 80 | _d["transportid_length"] = 0 81 | encode_dict(_d, cls._ram_parameter_list_bits, result) 82 | return result 83 | elif service_action == opcode.serviceaction.REGISTER and data.get("spec_i_pt"): 84 | result = bytearray(28) 85 | encode_dict(data, cls._basic_parameter_list_bits, result) 86 | transport_ids = [] 87 | for t in data.get("transport_ids", []): 88 | transport_ids.append( 89 | PersistentReserveInReadFullStatus.marshall_transport_id(t) 90 | ) 91 | additional_parameter_data = b"".join(transport_ids) 92 | result[24:28] = scsi_int_to_ba(len(additional_parameter_data), 4) 93 | return result + additional_parameter_data 94 | else: 95 | result = bytearray(24) 96 | encode_dict(data, cls._basic_parameter_list_bits, result) 97 | return result 98 | 99 | def __init__(self, opcode, service_action, scope=0, pr_type=0, **kwargs): 100 | """ 101 | initialize a new instance 102 | 103 | :param opcode: a OpCode instance 104 | :param service_action: service action code 105 | :param scope: persistent reservation SCOPE field 106 | :param pr_type: persistent reservation TYPE field 107 | :param data: a dict holding parameter list items (Basic or Register and Move) 108 | """ 109 | _d = PersistentReserveOut.marshall_dataout(opcode, service_action, kwargs) 110 | 111 | SCSICommand.__init__(self, opcode, 0, 0) 112 | 113 | self.dataout = _d 114 | 115 | self.cdb = self.build_cdb( 116 | opcode=self.opcode.value, 117 | service_action=service_action, 118 | scope=scope, 119 | pr_type=pr_type, 120 | parameter_list_length=len(_d), 121 | ) 122 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_positiontoelement.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI PositionToElement command and definitions 12 | # 13 | 14 | 15 | class PositionToElement(SCSICommand): 16 | """ 17 | A class to hold information from a PositionToElement command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "medium_transport_address": [0xFFFF, 2], 23 | "destination_address": [0xFFFF, 4], 24 | "invert": [0x01, 8], 25 | } 26 | 27 | def __init__(self, opcode, xfer, dest, invert=0): 28 | """ 29 | initialize a new instance 30 | 31 | :param opcode: a OpCode instance 32 | :param xfer: medium transport address 33 | :param dest: destination address 34 | :param invert: invert can be 0 or 1 35 | """ 36 | SCSICommand.__init__(self, opcode, 0, 0) 37 | 38 | self.cdb = self.build_cdb( 39 | opcode=self.opcode.value, 40 | medium_transport_address=xfer, 41 | destination_address=dest, 42 | invert=invert, 43 | ) 44 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_preventallow_mediumremoval.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI PreventAllowMediumRemoval command and definitions 12 | # 13 | 14 | 15 | class PreventAllowMediumRemoval(SCSICommand): 16 | """ 17 | A class to hold information from a PreventAllowMediumRemoval command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "prevent": [0x03, 4], 23 | } 24 | 25 | def __init__(self, opcode, prevent=0): 26 | """ 27 | initialize a new instance 28 | 29 | :param opcode: a OpCode instance 30 | :param prevent: prevent can have a value between 0 and 3 31 | """ 32 | SCSICommand.__init__(self, opcode, 0, 0) 33 | 34 | self.cdb = self.build_cdb(opcode=self.opcode.value, prevent=prevent) 35 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_read10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI Read10 command and definitions 12 | # 13 | 14 | 15 | class Read10(SCSICommand): 16 | """ 17 | A class to send a Read(10) command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "rdprotect": [0xE0, 1], 23 | "dpo": [0x10, 1], 24 | "fua": [0x08, 1], 25 | "rarc": [0x04, 1], 26 | "lba": [0xFFFFFFFF, 2], 27 | "group": [0x1F, 6], 28 | "tl": [0xFFFF, 7], 29 | } 30 | 31 | def __init__( 32 | self, opcode, blocksize, lba, tl, rdprotect=0, dpo=0, fua=0, rarc=0, group=0 33 | ): 34 | """ 35 | initialize a new instance 36 | 37 | :param opcode: a OpCode instance 38 | :param blocksize: a blocksize 39 | :param lba: Logical Block Address 40 | :param tl: transfer length 41 | :param rdprotect: 42 | :param dpo: 43 | :param fua: 44 | :param rarc: 45 | :param group: 46 | """ 47 | if blocksize == 0: 48 | raise SCSICommand.MissingBlocksizeException 49 | 50 | SCSICommand.__init__(self, opcode, 0, blocksize * tl) 51 | 52 | self.cdb = self.build_cdb( 53 | opcode=self.opcode.value, 54 | lba=lba, 55 | tl=tl, 56 | rdprotect=rdprotect, 57 | dpo=dpo, 58 | fua=fua, 59 | rarc=rarc, 60 | group=group, 61 | ) 62 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_read12.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI Read12 command and definitions 12 | # 13 | 14 | 15 | class Read12(SCSICommand): 16 | """ 17 | A class to send a Read(12) command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "rdprotect": [0xE0, 1], 23 | "dpo": [0x10, 1], 24 | "fua": [0x08, 1], 25 | "rarc": [0x04, 1], 26 | "lba": [0xFFFFFFFF, 2], 27 | "tl": [0xFFFFFFFF, 6], 28 | "group": [0x1F, 10], 29 | } 30 | 31 | def __init__( 32 | self, opcode, blocksize, lba, tl, rdprotect=0, dpo=0, fua=0, rarc=0, group=0 33 | ): 34 | """ 35 | initialize a new instance 36 | 37 | :param opcode: a OpCode instance 38 | :param blocksize: a blocksize 39 | :param lba: Logical Block Address 40 | :param tl: transfer length 41 | :param rdprotect=0: 42 | :param dpo=0: 43 | :param fua=0: 44 | :param rarc=0: 45 | :param group=0: 46 | """ 47 | if blocksize == 0: 48 | raise SCSICommand.MissingBlocksizeException 49 | 50 | SCSICommand.__init__(self, opcode, 0, blocksize * tl) 51 | 52 | self.cdb = self.build_cdb( 53 | opcode=self.opcode.value, 54 | lba=lba, 55 | tl=tl, 56 | rdprotect=rdprotect, 57 | dpo=dpo, 58 | fua=fua, 59 | rarc=rarc, 60 | group=group, 61 | ) 62 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_read16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI Read16 command and definitions 12 | # 13 | 14 | 15 | class Read16(SCSICommand): 16 | """ 17 | A class to send a Read(16) command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "rdprotect": [0xE0, 1], 23 | "dpo": [0x10, 1], 24 | "fua": [0x08, 1], 25 | "rarc": [0x04, 1], 26 | "lba": [0xFFFFFFFFFFFFFFFF, 2], 27 | "group": [0x1F, 14], 28 | "tl": [0xFFFFFFFF, 10], 29 | } 30 | 31 | def __init__( 32 | self, opcode, blocksize, lba, tl, rdprotect=0, dpo=0, fua=0, rarc=0, group=0 33 | ): 34 | """ 35 | initialize a new instance 36 | 37 | :param opcode: a OpCode instance 38 | :param blocksize: a blocksize 39 | :param lba: Logical Block Address 40 | :param tl: transfer length 41 | :param rdprotect=0: 42 | :param dpo=0: 43 | :param fua=0: 44 | :param rarc=0: 45 | :param group=0: 46 | """ 47 | if blocksize == 0: 48 | raise SCSICommand.MissingBlocksizeException 49 | 50 | SCSICommand.__init__(self, opcode, 0, blocksize * tl) 51 | 52 | self.cdb = self.build_cdb( 53 | opcode=self.opcode.value, 54 | lba=lba, 55 | tl=tl, 56 | rdprotect=rdprotect, 57 | dpo=dpo, 58 | fua=fua, 59 | rarc=rarc, 60 | group=group, 61 | ) 62 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_readcapacity10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | from pyscsi.utils.converter import decode_bits, encode_dict 10 | 11 | # 12 | # SCSI ReadCapacity10 command and definitions 13 | # 14 | 15 | 16 | class ReadCapacity10(SCSICommand): 17 | """ 18 | A class to hold information from a ReadCapacity(10) command to a scsi device 19 | """ 20 | 21 | _cdb_bits = { 22 | "opcode": [0xFF, 0], 23 | } 24 | 25 | _datain_bits = { 26 | "returned_lba": [0xFFFFFFFF, 0], 27 | "block_length": [0xFFFFFFFF, 4], 28 | } 29 | 30 | def __init__(self, opcode, alloclen=8): 31 | """ 32 | initialize a new instance 33 | 34 | :param opcode: a OpCode instance 35 | :param alloclen: the max number of bytes allocated for the data_in buffer 36 | """ 37 | SCSICommand.__init__(self, opcode, 0, alloclen) 38 | 39 | self.cdb = self.build_cdb(opcode=self.opcode.value) 40 | 41 | @classmethod 42 | def unmarshall_datain(cls, data): 43 | """ 44 | Unmarshall the ReadCapacity10 datain. 45 | 46 | :param data: a byte array 47 | :return result: a dict 48 | """ 49 | result = {} 50 | decode_bits(data, cls._datain_bits, result) 51 | return result 52 | 53 | @classmethod 54 | def marshall_datain(cls, data): 55 | """ 56 | Marshall the ReadCapacity10 datain. 57 | 58 | :param data: a dict 59 | :return result: a byte array 60 | """ 61 | result = bytearray(8) 62 | encode_dict(data, cls._datain_bits, result) 63 | return result 64 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_readcapacity16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | from pyscsi.utils.converter import decode_bits, encode_dict 10 | 11 | # 12 | # SCSI ReadCapacity16 command and definitions 13 | # 14 | 15 | 16 | class ReadCapacity16(SCSICommand): 17 | """ 18 | A class to hold information from a ReadCapacity(16) command to a scsi device 19 | """ 20 | 21 | _cdb_bits = { 22 | "opcode": [0xFF, 0], 23 | "service_action": [0x1F, 1], 24 | "alloc_len": [0xFFFFFFFF, 10], 25 | } 26 | 27 | _datain_bits = { 28 | "returned_lba": [0xFFFFFFFFFFFFFFFF, 0], 29 | "block_length": [0xFFFFFFFF, 8], 30 | "p_type": [0x0E, 12], 31 | "prot_en": [0x01, 12], 32 | "p_i_exponent": [0xF0, 13], 33 | "lbppbe": [0x0F, 13], 34 | "lbpme": [0x80, 14], 35 | "lbprz": [0x40, 14], 36 | "lowest_aligned_lba": [0x3FFF, 14], 37 | } 38 | 39 | def __init__(self, opcode, alloclen=32): 40 | """ 41 | initialize a new instance 42 | 43 | :param opcode: a OpCode instance 44 | :param alloclen: the max number of bytes allocated for the data_in buffer 45 | """ 46 | SCSICommand.__init__(self, opcode, 0, alloclen) 47 | 48 | self.cdb = self.build_cdb( 49 | opcode=self.opcode.value, 50 | service_action=self.opcode.serviceaction.READ_CAPACITY_16, 51 | alloc_len=alloclen, 52 | ) 53 | 54 | @classmethod 55 | def unmarshall_datain(cls, data): 56 | """ 57 | Unmarshall the ReadCapacity16 datain. 58 | 59 | :param data: a byte array 60 | :return result: a dict 61 | """ 62 | result = {} 63 | decode_bits(data, cls._datain_bits, result) 64 | return result 65 | 66 | @classmethod 67 | def marshall_datain(cls, data): 68 | """ 69 | Marshall the ReadCapacity16 datain. 70 | 71 | :param data: a dict 72 | :return result: a byte array 73 | """ 74 | result = bytearray(32) 75 | encode_dict(data, cls._datain_bits, result) 76 | return result 77 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_readdiscinformation.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2021 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | import pyscsi.pyscsi.scsi_enum_readdiscinformation as rdi_enums 9 | from pyscsi.pyscsi.scsi_command import SCSICommand 10 | from pyscsi.utils.converter import decode_bits, encode_dict 11 | 12 | # 13 | # SCSI ReadDiscInformation command and definitions 14 | # 15 | 16 | # we get a generator for all readdiskinformation enums, so we can add them to the class 17 | _enums = ( 18 | (key, rdi_enums.__dict__[key]) 19 | for key in rdi_enums.__dict__.keys() 20 | if key in rdi_enums.__all__ and key not in ["MODESENSE6"] 21 | ) 22 | 23 | 24 | class ReadDiscInformation(SCSICommand): 25 | """ 26 | A class to hold information from a ReadDiscInformation command to a scsi device 27 | """ 28 | 29 | _cdb_bits = { 30 | "opcode": [0xFF, 0], 31 | "data_type": [0x07, 1], 32 | "alloc_len": [0xFFFF, 7], 33 | } 34 | 35 | for enum in _enums: 36 | setattr(SCSICommand, enum[0], enum[1]) 37 | 38 | _sdi_bits = { 39 | "disc_information_length": [0xFFFF, 0], 40 | "disc_information_data_type": [0xE0, 2], 41 | "erasable": [0x10, 2], 42 | "state_of_last_session": [0x0C, 2], 43 | "disc_status": [0x03, 2], 44 | "number_of_first_track_on_disc": [0xFF, 3], 45 | "number_of_sessions_lsb": [0xFF, 4], 46 | "first_track_number_in_last_session_lsb": [0xFF, 5], 47 | "last_track_number_in_last_session_lsb": [0xFF, 6], 48 | "did_v": [0x80, 7], 49 | "dbc_v": [0x40, 7], 50 | "uru": [0x20, 7], 51 | "dac_v": [0x10, 7], 52 | "legacy": [0x04, 7], 53 | "bg_format_status": [0x03, 7], 54 | "disc_type": [0x03, 8], 55 | "number_of_sessions_msb": [0xFF, 9], 56 | "first_track_number_in_last_session_msb": [0xFF, 10], 57 | "last_track_number_in_last_session_msb": [0xFF, 11], 58 | "disc_identification": [0xFFFFFFFF, 12], 59 | "last_session_lead_in_start_address": ["b", 16, 4], 60 | "last_possible_lead_out_start_address": ["b", 20, 4], 61 | "disc_bar_code": ["b", 24, 8], 62 | "disc_application_code": [0xFF, 32], 63 | "number_of_opc_tables": [0xFF, 33], 64 | } 65 | _tri_bits = { 66 | "disc_information_length": [0xFFFF, 0], 67 | "disc_information_data_type": [0xE0, 2], 68 | "maximum_possible_number_of_the_tracks": [0xFFFF, 4], 69 | "number_of_the_assigned_tracks": [0xFFFF, 6], 70 | "maximum_possible_number_of_appendable_tracks": [0xFFFF, 8], 71 | "current_number_of_appendable_tracks": [0xFFFF, 10], 72 | } 73 | _pow_bits = { 74 | "disc_information_length": [0xFFFF, 0], 75 | "disc_information_data_type": [0xE0, 2], 76 | "remaining_pow_replacements": [0xFFFFFFFF, 4], 77 | "remaining_pow_reallocation_map_entries": [0xFFFFFFFF, 8], 78 | "number_of_remaining_pow_updates": [0xFFFFFFFF, 12], 79 | } 80 | 81 | def __init__(self, opcode, data_type, alloc_len=4096): 82 | """ 83 | initialize a new instance 84 | 85 | :param opcode: a OpCode instance 86 | :param data_type: Data Type 87 | :param alloc_len: Allocation Length 88 | """ 89 | self._data_type = data_type 90 | SCSICommand.__init__(self, opcode, 0, alloc_len) 91 | 92 | self.cdb = self.build_cdb( 93 | opcode=self.opcode.value, data_type=data_type, alloc_len=alloc_len 94 | ) 95 | 96 | @classmethod 97 | def unmarshall_datain(cls, data): 98 | """ 99 | Unmarshall the ReadDiscInformation datain. 100 | 101 | :param data: a byte array 102 | :return result: a dict 103 | """ 104 | result = {} 105 | if data[2] >> 5 == cls.DISC_INFORMATION_DATA_TYPE.STANDARD_DISC_INFORMATION: 106 | decode_bits(data, cls._sdi_bits, result) 107 | data = data[: result["disc_information_length"] + 2] 108 | result["number_of_sessions"] = ( 109 | result["number_of_sessions_msb"] * 256 110 | + result["number_of_sessions_lsb"] 111 | ) 112 | del result["number_of_sessions_msb"] 113 | del result["number_of_sessions_lsb"] 114 | result["first_track_number_in_last_session"] = ( 115 | result["first_track_number_in_last_session_msb"] * 256 116 | + result["first_track_number_in_last_session_lsb"] 117 | ) 118 | del result["first_track_number_in_last_session_msb"] 119 | del result["first_track_number_in_last_session_lsb"] 120 | result["last_track_number_in_last_session"] = ( 121 | result["last_track_number_in_last_session_msb"] * 256 122 | + result["last_track_number_in_last_session_lsb"] 123 | ) 124 | del result["last_track_number_in_last_session_msb"] 125 | del result["last_track_number_in_last_session_lsb"] 126 | return result 127 | if data[2] >> 5 == cls.DISC_INFORMATION_DATA_TYPE.TRACK_RESOURCES_INFORMATION: 128 | decode_bits(data, cls._tri_bits, result) 129 | return result 130 | if ( 131 | data[2] >> 5 132 | == cls.DISC_INFORMATION_DATA_TYPE.POW_RESOURCES_DISC_INFORMATION 133 | ): 134 | decode_bits(data, cls._pow_bits, result) 135 | return result 136 | 137 | raise NotImplementedError( 138 | "Unknown disc information data type %d" % (data[2] >> 5) 139 | ) 140 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_report_luns.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2016 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | from pyscsi.utils.converter import ( 10 | decode_bits, 11 | encode_dict, 12 | scsi_ba_to_int, 13 | scsi_int_to_ba, 14 | ) 15 | 16 | # 17 | # SCSI GetLBAStatus command and definitions 18 | # 19 | 20 | 21 | class ReportLuns(SCSICommand): 22 | """ 23 | A class to hold information from a ReportLuns command to a scsi device 24 | """ 25 | 26 | _cdb_bits = { 27 | "opcode": [0xFF, 0], 28 | "select_report": [0xFF, 2], 29 | "alloc_len": [0xFFFFFFFF, 6], 30 | } 31 | 32 | _datain_bits = { 33 | "lun": [0xFFFFFFFFFFFFFFFF, 0], 34 | } 35 | 36 | def __init__(self, opcode, report=0x00, alloclen=96): 37 | """ 38 | initialize a new instance 39 | 40 | :param opcode: a OpCode instance 41 | :param report: select report field value 42 | :param alloclen: the max number of bytes allocated for the data_in buffer 43 | """ 44 | SCSICommand.__init__(self, opcode, 0, alloclen) 45 | 46 | self.cdb = self.build_cdb( 47 | opcode=self.opcode.value, select_report=report, alloc_len=alloclen 48 | ) 49 | 50 | @classmethod 51 | def unmarshall_datain(cls, data): 52 | """ 53 | Unmarshall the ReportLuns datain buffer. 54 | 55 | :param data: a byte array 56 | :return result: a dic 57 | """ 58 | result = {} 59 | _data = data[8 : scsi_ba_to_int(data[:4]) + 4] 60 | _luns = [] 61 | _count = 0 62 | while len(_data): 63 | # maybe we drop the whole "put a dict into the list for every lun" thing at all 64 | _r = {} 65 | decode_bits(_data[:8], cls._datain_bits, _r) 66 | key = "lun%s" % _count 67 | _r[key] = _r.pop("lun") 68 | _luns.append(_r) 69 | _data = _data[8:] 70 | _count += 1 71 | 72 | result.update({"luns": _luns}) 73 | return result 74 | 75 | @classmethod 76 | def marshall_datain(cls, data): 77 | """ 78 | Marshall the ReportLuns datain. 79 | 80 | :param data: a dict 81 | :return result: a byte array 82 | """ 83 | result = bytearray(8) 84 | if "luns" not in data: 85 | result[:4] = scsi_int_to_ba(len(result) - 4, 4) 86 | return result 87 | 88 | for l in data["luns"]: 89 | _r = bytearray(8) 90 | encode_dict(l, cls._datain_bits, _r) 91 | 92 | result += _r 93 | result[:4] = scsi_int_to_ba(len(result) - 4, 4) 94 | return result 95 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_report_priority.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2016 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | from pyscsi.utils.converter import ( 10 | decode_bits, 11 | encode_dict, 12 | scsi_ba_to_int, 13 | scsi_int_to_ba, 14 | ) 15 | 16 | # 17 | # SCSI ReportPriority command and definitions 18 | # 19 | 20 | 21 | class ReportPriority(SCSICommand): 22 | """ 23 | A class to hold information from a ReportPriority command to a scsi device 24 | """ 25 | 26 | _cdb_bits = { 27 | "opcode": [0xFF, 0], 28 | "service_action": [0x1F, 1], 29 | "priority_reported": [0xC0, 2], 30 | "alloc_len": [0xFFFFFFFF, 6], 31 | } 32 | 33 | _data_bits = { 34 | "current_priority": [0x0F, 0], 35 | "rtpi": [0xFFFF, 2], 36 | "adlen": [0xFFFF, 6], 37 | } 38 | 39 | def __init__(self, opcode, priority=0, alloclen=16384): 40 | """ 41 | initialize a new instance 42 | 43 | :param opcode: a OpCode instance 44 | :param priority: specifies information to be returned in data_in buffer 45 | :param alloclen: the max number of bytes allocated for the data_in buffer 46 | """ 47 | SCSICommand.__init__(self, opcode, 0, alloclen) 48 | 49 | self.cdb = self.build_cdb( 50 | opcode=self.opcode.value, 51 | service_action=self.opcode.serviceaction.REPORT_PRIORITY, 52 | priority_reported=priority, 53 | alloc_len=alloclen, 54 | ) 55 | 56 | @classmethod 57 | def unmarshall_datain(cls, data): 58 | """ 59 | Unmarshall the ReportPriority datain. 60 | 61 | :param data: a byte array 62 | :return result: a dic 63 | """ 64 | result = {} 65 | # get the data after the ppd_len 66 | _data = data[4 : scsi_ba_to_int(data[:4])] 67 | _descriptors = [] 68 | while len(_data): 69 | _r = {} 70 | _dict = dict(cls._datain_bits.copy) 71 | _dict.update( 72 | { 73 | "transport_id": [hex(scsi_ba_to_int(_data[6:7])), 8], 74 | } 75 | ) 76 | decode_bits(_data[: 8 + scsi_ba_to_int(_data[6:7])], _dict, _r) 77 | _descriptors.append(_r) 78 | _data = _data[scsi_ba_to_int(_r["adlen"]) + 8 :] 79 | result.update( 80 | { 81 | "priority_descriptors": _descriptors, 82 | } 83 | ) 84 | return result 85 | 86 | @classmethod 87 | def marshall_datain(cls, data): 88 | """ 89 | Marshall the ReportPriority datain. 90 | 91 | :param data: a dict 92 | :return result: a byte array 93 | """ 94 | result = bytearray(4) 95 | if "priority_descriptors" not in data: 96 | result[:4] = scsi_int_to_ba(len(result), 4) 97 | return result 98 | 99 | for l in data["priority_descriptors"]: 100 | _r = bytearray(len(l)) 101 | _dict = dict(cls._datain_bits.copy) 102 | _dict.update( 103 | { 104 | "transport_id": [hex(scsi_ba_to_int(len(l) - 8)), 8], 105 | } 106 | ) 107 | encode_dict(l, _dict, _r) 108 | result += _r 109 | 110 | result[:4] = scsi_int_to_ba(len(result), 4) 111 | return result 112 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_report_target_port_groups.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2023 by Brian Meagher 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | from pyscsi.pyscsi.scsi_enum_report_target_port_groups import DATA_FORMAT_TYPE 10 | from pyscsi.utils.converter import ( 11 | decode_bits, 12 | encode_dict, 13 | scsi_ba_to_int, 14 | scsi_int_to_ba, 15 | ) 16 | 17 | # 18 | # SCSI ReportTargetPortGroups command and definitions 19 | # 20 | 21 | 22 | class ReportTargetPortGroups(SCSICommand): 23 | """ 24 | A class to hold information from a ReportTargetPortGroups command to a scsi device 25 | """ 26 | 27 | _cdb_bits = { 28 | "opcode": [0xFF, 0], 29 | "service_action": [0x1F, 1], 30 | "parameter_data_format": [0xE0, 1], 31 | "alloc_len": [0xFFFFFFFF, 6], 32 | } 33 | 34 | _tpgd_bits = { 35 | "asymmetric_access_state": [0x0F, 0], 36 | "pref": [0x80, 0], 37 | "ao_sup": [0x01, 1], 38 | "an_sup": [0x02, 1], 39 | "s_sup": [0x04, 1], 40 | "u_sup": [0x08, 1], 41 | "o_sup": [0x40, 1], 42 | "t_sup": [0x80, 1], 43 | "target_port_group": [0xFFFF, 2], 44 | "status_code": [0xFF, 5], 45 | "vendor": [0xFF, 6], 46 | "target_port_count": [0xFF, 7], 47 | } 48 | 49 | _ext_hdr_bits = { 50 | "format_type": [0x70, 0], 51 | "implicit_transition_time": [0xFF, 1], 52 | } 53 | 54 | def __init__( 55 | self, 56 | opcode, 57 | data_format=DATA_FORMAT_TYPE.LENGTH_ONLY_HEADER_PARAMETER_DATA_FORMAT, 58 | alloclen=16384, 59 | ): 60 | """ 61 | initialize a new instance 62 | 63 | :param opcode: a OpCode instance 64 | :param data_format: specifies the requested format for the parameter data returned 65 | :param alloclen: the max number of bytes allocated for the data_in buffer 66 | """ 67 | SCSICommand.__init__(self, opcode, 0, alloclen) 68 | 69 | self.cdb = self.build_cdb( 70 | opcode=self.opcode.value, 71 | service_action=self.opcode.serviceaction.REPORT_TARGET_PORT_GROUPS, 72 | parameter_data_format=data_format, 73 | alloc_len=alloclen, 74 | ) 75 | 76 | @classmethod 77 | def unmarshall_datain(cls, data): 78 | """ 79 | Unmarshall the ReportTargetPortGroups datain. 80 | 81 | :param data: a byte array 82 | :return result: a dic 83 | """ 84 | result = {} 85 | # get the data after the return_data_length 86 | _data = data[4 : scsi_ba_to_int(data[:4]) + 4] 87 | 88 | # Check whether length only or extended header parameter data format 89 | if len(_data) >= 4: 90 | _r = {} 91 | decode_bits(_data, cls._ext_hdr_bits, _r) 92 | result["format_type"] = _r["format_type"] 93 | if ( 94 | _r["format_type"] 95 | == DATA_FORMAT_TYPE.EXTENDED_HEADER_PARAMETER_DATA_FORMAT 96 | ): 97 | result["implicit_transition_time"] = _r["implicit_transition_time"] 98 | _data = _data[4:] 99 | else: 100 | result[ 101 | "format_type" 102 | ] = DATA_FORMAT_TYPE.LENGTH_ONLY_HEADER_PARAMETER_DATA_FORMAT 103 | 104 | _tpg_descriptors = [] # Target Port Group Descriptors 105 | while len(_data): 106 | _tpgd = {} # Target Port Group Descriptor 107 | decode_bits(_data, cls._tpgd_bits, _tpgd) 108 | _data = _data[8:] 109 | 110 | _tp_descriptors = [] # Target Port Desxcriptors 111 | while len(_data) and len(_tp_descriptors) < _tpgd["target_port_count"]: 112 | _tpd = {} # Target Port Desxcriptor 113 | _tpd["relative_target_port_id"] = scsi_ba_to_int(_data[2:4]) 114 | _tp_descriptors.append(_tpd) 115 | _data = _data[4:] 116 | 117 | _tpgd["target_ports"] = _tp_descriptors 118 | _tpg_descriptors.append(_tpgd) 119 | 120 | result.update( 121 | { 122 | "target_port_group_descriptors": _tpg_descriptors, 123 | } 124 | ) 125 | return result 126 | 127 | @classmethod 128 | def marshall_datain(cls, data): 129 | """ 130 | Marshall the ReportTargetPortGroups datain. 131 | 132 | :param data: a dict 133 | :return result: a byte array 134 | """ 135 | result = bytearray(4) 136 | if ( 137 | "format_type" in data 138 | and data["format_type"] 139 | == DATA_FORMAT_TYPE.EXTENDED_HEADER_PARAMETER_DATA_FORMAT 140 | ): 141 | _r = bytearray(4) 142 | encode_dict(data, cls._ext_hdr_bits, _r) 143 | result += _r 144 | 145 | for _tpgd in data["target_port_group_descriptors"]: 146 | _r = bytearray(8) 147 | encode_dict(_tpgd, cls._tpgd_bits, _r) 148 | result += _r 149 | for _tpd in _tpgd["relative_target_port_id"]: 150 | result += bytearray(2) 151 | result += scsi_int_to_ba(_tpd["relative_target_port_id"], 2) 152 | 153 | result[:4] = scsi_int_to_ba(len(result) - 4, 4) 154 | return result 155 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_synchronize_cache10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2024 by Brian Meagher 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI SYNCHRONIZE CACHE 10 command and definitions 12 | # 13 | # See SBC-4 5.33 SYNCHRONIZE CACHE (10) command 14 | # 15 | 16 | 17 | class SynchronizeCache10(SCSICommand): 18 | """ 19 | A class to send a Synchronize Cache (10) command to a scsi device 20 | """ 21 | 22 | _cdb_bits = { 23 | "opcode": [0xFF, 0], 24 | "immed": [0x02, 1], 25 | "lba": [0xFFFFFFFF, 2], 26 | "group": [0x1F, 6], 27 | "numblks": [0xFFFF, 7], 28 | } 29 | 30 | def __init__(self, opcode, lba, numblks, immed=0, group=0): 31 | """ 32 | initialize a new instance 33 | 34 | :param opcode: a OpCode instance 35 | :param lba: Logical Block Address 36 | :param numblks: transfer length 37 | :param immed=0: 38 | :param group=0: 39 | """ 40 | SCSICommand.__init__(self, opcode, 0, 0) 41 | self.cdb = self.build_cdb( 42 | opcode=self.opcode.value, 43 | lba=lba, 44 | numblks=numblks, 45 | immed=immed, 46 | group=group, 47 | ) 48 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_synchronize_cache16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2024 by Brian Meagher 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI SYNCHRONIZE CACHE 16 command and definitions 12 | # 13 | # See SBC-4 5.34 SYNCHRONIZE CACHE (16) command 14 | # 15 | 16 | 17 | class SynchronizeCache16(SCSICommand): 18 | """ 19 | A class to send a Synchronize Cache (16) command to a scsi device 20 | """ 21 | 22 | _cdb_bits = { 23 | "opcode": [0xFF, 0], 24 | "immed": [0x02, 1], 25 | "lba": [0xFFFFFFFFFFFFFFFF, 2], 26 | "numblks": [0xFFFFFFFF, 10], 27 | "group": [0x1F, 14], 28 | } 29 | 30 | def __init__(self, opcode, lba, numblks, immed=0, group=0): 31 | """ 32 | initialize a new instance 33 | 34 | :param opcode: a OpCode instance 35 | :param lba: Logical Block Address 36 | :param numblks: transfer length 37 | :param immed=0: 38 | :param group=0: 39 | """ 40 | SCSICommand.__init__(self, opcode, 0, 0) 41 | self.cdb = self.build_cdb( 42 | opcode=self.opcode.value, 43 | lba=lba, 44 | numblks=numblks, 45 | immed=immed, 46 | group=group, 47 | ) 48 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_testunitready.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI TestUnitReady command 12 | # 13 | 14 | 15 | class TestUnitReady(SCSICommand): 16 | """ 17 | A class to hold information from a testunitready command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | } 23 | 24 | def __init__(self, opcode): 25 | """ 26 | initialize a new instance 27 | 28 | :param opcode: a OpCode instance 29 | """ 30 | SCSICommand.__init__(self, opcode, 0, 0) 31 | 32 | self.cdb = self.build_cdb(opcode=self.opcode.value) 33 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_write10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI Write10 command and definitions 12 | # 13 | 14 | 15 | class Write10(SCSICommand): 16 | """ 17 | A class to send a Write(10) command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "wrprotect": [0xE0, 1], 23 | "dpo": [0x10, 1], 24 | "fua": [0x08, 1], 25 | "lba": [0xFFFFFFFF, 2], 26 | "group": [0x1F, 6], 27 | "tl": [0xFFFF, 7], 28 | } 29 | 30 | def __init__( 31 | self, opcode, blocksize, lba, tl, data, wrprotect=0, dpo=0, fua=0, group=0 32 | ): 33 | """ 34 | initialize a new instance 35 | 36 | :param opcode: a OpCode instance 37 | :param blocksize: a blocksize 38 | :param lba: Logical Block Address 39 | :param tl: transfer length 40 | :param data: a byte array with data 41 | :param wrprotect=0: 42 | :param dpo=0: 43 | :param fua=0: 44 | :param group=0: 45 | """ 46 | if blocksize == 0: 47 | raise SCSICommand.MissingBlocksizeException 48 | 49 | SCSICommand.__init__(self, opcode, blocksize * tl, 0) 50 | self.dataout = data 51 | self.cdb = self.build_cdb( 52 | opcode=self.opcode.value, 53 | lba=lba, 54 | tl=tl, 55 | wrprotect=wrprotect, 56 | dpo=dpo, 57 | fua=fua, 58 | group=group, 59 | ) 60 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_write12.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI Write12 command and definitions 12 | # 13 | 14 | 15 | class Write12(SCSICommand): 16 | """ 17 | A class to send a Write(12) command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "wrprotect": [0xE0, 1], 23 | "dpo": [0x10, 1], 24 | "fua": [0x08, 1], 25 | "lba": [0xFFFFFFFF, 2], 26 | "group": [0x1F, 10], 27 | "tl": [0xFFFFFFFF, 6], 28 | } 29 | 30 | def __init__( 31 | self, opcode, blocksize, lba, tl, data, wrprotect=0, dpo=0, fua=0, group=0 32 | ): 33 | """ 34 | initialize a new instance 35 | 36 | :param opcode: a OpCode instance 37 | :param blocksize: a blocksize 38 | :param lba: Logical Block Address 39 | :param tl: transfer length 40 | :param data: a byte array with data 41 | :param wrprotect=0: 42 | :param dpo=0: 43 | :param fua=0: 44 | :param group=0: 45 | """ 46 | if blocksize == 0: 47 | raise SCSICommand.MissingBlocksizeException 48 | 49 | SCSICommand.__init__(self, opcode, blocksize * tl, 0) 50 | self.dataout = data 51 | self.cdb = self.build_cdb( 52 | opcode=self.opcode.value, 53 | lba=lba, 54 | tl=tl, 55 | wrprotect=wrprotect, 56 | dpo=dpo, 57 | fua=fua, 58 | group=group, 59 | ) 60 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_write16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI Write16 command and definitions 12 | # 13 | 14 | 15 | class Write16(SCSICommand): 16 | """ 17 | A class to send a Write(16) command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "wrprotect": [0xE0, 1], 23 | "dpo": [0x10, 1], 24 | "fua": [0x08, 1], 25 | "lba": [0xFFFFFFFFFFFFFFFF, 2], 26 | "group": [0x1F, 14], 27 | "tl": [0xFFFFFFFF, 10], 28 | } 29 | 30 | def __init__( 31 | self, opcode, blocksize, lba, tl, data, wrprotect=0, dpo=0, fua=0, group=0 32 | ): 33 | """ 34 | initialize a new instance 35 | 36 | :param opcode: a OpCode instance 37 | :param blocksize: a blocksize 38 | :param lba: Logical Block Address 39 | :param tl: transfer length 40 | :param data: a byte array with data 41 | :param wrprotect=0: 42 | :param dpo=0: 43 | :param fua=0: 44 | :param group=0: 45 | """ 46 | if blocksize == 0: 47 | raise SCSICommand.MissingBlocksizeException 48 | 49 | SCSICommand.__init__(self, opcode, blocksize * tl, 0) 50 | self.dataout = data 51 | self.cdb = self.build_cdb( 52 | opcode=self.opcode.value, 53 | lba=lba, 54 | tl=tl, 55 | wrprotect=wrprotect, 56 | dpo=dpo, 57 | fua=fua, 58 | group=group, 59 | ) 60 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_writesame10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | 10 | # 11 | # SCSI WriteSame10 command and definitions 12 | # 13 | 14 | 15 | class WriteSame10(SCSICommand): 16 | """ 17 | A class to send a WriteSame(10) command to a scsi device 18 | """ 19 | 20 | _cdb_bits = { 21 | "opcode": [0xFF, 0], 22 | "wrprotect": [0xE0, 1], 23 | "anchor": [0x10, 1], 24 | "unmap": [0x08, 1], 25 | "lba": [0xFFFFFFFF, 2], 26 | "group": [0x1F, 6], 27 | "nb": [0xFFFF, 7], 28 | } 29 | 30 | def __init__( 31 | self, opcode, blocksize, lba, nb, data, wrprotect=0, anchor=0, unmap=0, group=0 32 | ): 33 | """ 34 | initialize a new instance 35 | 36 | :param opcode: a OpCode instance 37 | :param blocksize: a blocksize 38 | :param lba: logical block address 39 | :param nb: number of logical blocks 40 | :param data: a byte array with data 41 | :param wrprotect: value to specify write protection information 42 | :param anchor: anchor can have a value of 0 or 1 43 | :param unmap: unmap can have a value of 0 or 1 44 | :param group: group number, can be 0 or greater 45 | """ 46 | if blocksize == 0: 47 | raise SCSICommand.MissingBlocksizeException 48 | 49 | SCSICommand.__init__(self, opcode, blocksize, 0) 50 | self.dataout = data 51 | self.cdb = self.build_cdb( 52 | opcode=self.opcode.value, 53 | lba=lba, 54 | nb=nb, 55 | wrprotect=wrprotect, 56 | anchor=anchor, 57 | unmap=unmap, 58 | group=group, 59 | ) 60 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_cdb_writesame16.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | # coding: utf-8 6 | 7 | 8 | from pyscsi.pyscsi.scsi_command import SCSICommand 9 | from pyscsi.utils.converter import decode_bits, encode_dict 10 | 11 | # 12 | # SCSI WriteSame16 command and definitions 13 | # 14 | 15 | 16 | class WriteSame16(SCSICommand): 17 | """ 18 | A class to send a WriteSame(16) command to a scsi device 19 | """ 20 | 21 | _cdb_bits = { 22 | "opcode": [0xFF, 0], 23 | "wrprotect": [0xE0, 1], 24 | "anchor": [0x10, 1], 25 | "unmap": [0x08, 1], 26 | "ndob": [0x01, 1], 27 | "lba": [0xFFFFFFFFFFFFFFFF, 2], 28 | "group": [0x1F, 14], 29 | "nb": [0xFFFFFFFF, 10], 30 | } 31 | 32 | def __init__( 33 | self, 34 | opcode, 35 | blocksize, 36 | lba, 37 | nb, 38 | data, 39 | wrprotect=0, 40 | anchor=0, 41 | unmap=0, 42 | ndob=0, 43 | group=0, 44 | ): 45 | """ 46 | initialize a new instance 47 | 48 | :param opcode: a OpCode instance 49 | :param blocksize: a blocksize 50 | :param lba: logical block address 51 | :param nb: number of logical blocks 52 | :param data: a byte array with data 53 | :param wrprotect: value to specify write protection information 54 | :param anchor: anchor can have a value of 0 or 1 55 | :param unmap: unmap can have a value of 0 or 1 56 | :param ndob: Value can be 0 or 1, use logical block data from data out buffer 57 | (data arg) if set to 1. 58 | :param group: group number, can be 0 or greater 59 | """ 60 | if not ndob and blocksize == 0: 61 | raise SCSICommand.MissingBlocksizeException 62 | 63 | SCSICommand.__init__(self, opcode, 0 if ndob else blocksize, 0) 64 | self.dataout = None if ndob else data 65 | self.cdb = self.build_cdb( 66 | opcode=self.opcode.value, 67 | lba=lba, 68 | nb=nb, 69 | wrprotect=wrprotect, 70 | anchor=anchor, 71 | unmap=unmap, 72 | ndob=ndob, 73 | group=group, 74 | ) 75 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_device.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import os 10 | 11 | import pyscsi.pyscsi.scsi_enum_command as scsi_enum_command 12 | from pyscsi.pyscsi.scsi_exception import SCSIDeviceCommandExceptionMeta as ExMETA 13 | 14 | try: 15 | import sgio 16 | 17 | _has_sgio = True 18 | except ImportError as e: 19 | _has_sgio = False 20 | 21 | 22 | def get_inode(file): 23 | # type: (str) -> int 24 | return os.stat(file).st_ino 25 | 26 | 27 | class SCSIDevice(metaclass=ExMETA): 28 | """ 29 | The scsi device class 30 | 31 | By default it gets the SPC opcodes assigned so it's always possible to issue 32 | a inquiry command to the device. This is important since the the Command will 33 | figure out the opcode from the SCSIDevice first to use it for building the cdb. 34 | This means after the that it's possible to use the proper OpCodes for the device. 35 | A basic workflow for using a device would be: 36 | - try to open the device passed by the device arg 37 | - create a Inquiry instance, with the default opcodes of the device 38 | - execute the inquiry with the device 39 | - unmarshall the datain from the inquiry command to figure out the device type 40 | - assign the proper Opcode for the device type (it would also work just to use the 41 | opcodes without assigning them to the device since the command builds the cdb 42 | and the device just executes) 43 | 44 | Note: The workflow above is already implemented in the SCSI class 45 | """ 46 | 47 | def __init__(self, device, readwrite=False, detect_replugged=True, buffering=-1): 48 | """ 49 | initialize a new instance of a SCSIDevice 50 | :param device: the file descriptor 51 | :param readwrite: access type 52 | :param detect_replugged: detects device unplugged and plugged events and ensure executions will not fail 53 | silently due to replugged events 54 | :param buffering: Set the amount of buffering. For details, refer to the documentation of the open() built-in 55 | """ 56 | self._opcodes = scsi_enum_command.spc 57 | self._file_name = device 58 | self._read_write = readwrite 59 | self._file = None 60 | self._ino = None 61 | self._detect_replugged = detect_replugged 62 | self._buffering = buffering 63 | 64 | if _has_sgio and device[:5] == "/dev/": 65 | self.open() 66 | else: 67 | raise NotImplementedError("No backend implemented for %s" % device) 68 | 69 | def __enter__(self): 70 | """ 71 | 72 | :return: 73 | """ 74 | return self 75 | 76 | def __exit__(self, exc_type, exc_val, exc_tb): 77 | """ 78 | 79 | :param exc_type: 80 | :param exc_val: 81 | :param exc_tb: 82 | :return: 83 | """ 84 | self.close() 85 | 86 | def __repr__(self): 87 | """ 88 | 89 | :return: 90 | """ 91 | return self.__class__.__name__ 92 | 93 | def _is_replugged(self): 94 | # type: (SCSIDevice) -> bool 95 | ino = get_inode(self._file_name) 96 | return ino != self._ino 97 | 98 | def open(self): 99 | """ 100 | 101 | :param dev: 102 | :param read_write: 103 | :return: 104 | """ 105 | self._file = open( 106 | self._file_name, 107 | "w+b" if self._read_write else "rb", 108 | buffering=self._buffering, 109 | ) 110 | self._ino = get_inode(self._file_name) 111 | 112 | def close(self): 113 | self._file.close() 114 | 115 | def execute(self, cmd, en_raw_sense=False): 116 | """ 117 | execute a scsi command 118 | 119 | :param cmd: a SCSICommand 120 | """ 121 | if self._detect_replugged and self._is_replugged(): 122 | try: 123 | self.close() 124 | finally: 125 | self.open() 126 | 127 | try: 128 | # TODO: If exist the corner case that sense cannot be raised by error.sense? 129 | # will not set return_sense_data=True until i test most of the ata command set. 130 | sgio.execute(self._file, cmd.cdb, cmd.dataout, cmd.datain) 131 | except sgio.CheckConditionError as error: 132 | self.CheckCondition(error.sense) 133 | # For ata-passthrough, mostly the scsi command return no real error, here 134 | # save the raw sense data to command.raw_sense_data for upper level use. 135 | # If you execute the other scsi commands with en_raw_sense=True, this will 136 | # be a coppy of error.sense 137 | if en_raw_sense: 138 | cmd.raw_sense_data = error.sense 139 | 140 | @property 141 | def opcodes(self): 142 | return self._opcodes 143 | 144 | @opcodes.setter 145 | def opcodes(self, value): 146 | self._opcodes = value 147 | 148 | @property 149 | def devicetype(self): 150 | return self._devicetype 151 | 152 | @devicetype.setter 153 | def devicetype(self, value): 154 | self._devicetype = value 155 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_enum_getlbastatus.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | # coding: utf-8 6 | 7 | 8 | from pyscsi.utils.enum import Enum 9 | 10 | # 11 | # P_STATUS 12 | # 13 | _p_status = { 14 | "MAPPED": 0x00, 15 | "DEALLOCATED": 0x01, 16 | "ANCHORED": 0x02, 17 | } 18 | 19 | P_STATUS = Enum(_p_status) 20 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_enum_inquiry.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.utils.enum import Enum 9 | 10 | __all__ = [ 11 | "PROVISIONING_TYPE", 12 | "QUALIFIER", 13 | "DEVICE_TYPE", 14 | "VERSION", 15 | "TPGS", 16 | "NOMINAL_FORM_FACTOR", 17 | "PROTOCOL_IDENTIFIER", 18 | "CODE_SET", 19 | "ASSOCIATION", 20 | "DESIGNATOR", 21 | "NAA", 22 | "VPD", 23 | ] 24 | # 25 | # Provisioning type 26 | # 27 | _provisioning_type = { 28 | "NO_PROVISIONING_REPORTED": 0x00, 29 | "RESOURCE_PROVISIONED": 0x01, 30 | "THIN_PROVISIONED": 0x02, 31 | } 32 | 33 | PROVISIONING_TYPE = Enum(_provisioning_type) 34 | 35 | # 36 | # Device qualifier 37 | # 38 | _qualifiers = { 39 | "CONNECTED": 0x00, 40 | "NOT_CONNECTED": 0x01, 41 | "NOT_CAPABLE": 0x03, 42 | } 43 | 44 | QUALIFIER = Enum(_qualifiers) 45 | 46 | # 47 | # Device type 48 | # 49 | _device_types = { 50 | "BLOCK_DEVICE": 0x00, 51 | "TAPE_DEVICE": 0x01, 52 | "PRINTER_DEVICE": 0x02, 53 | "PROCESSOR_DEVICE": 0x03, 54 | "WRITE_ONCE_DEVICE": 0x04, 55 | "MULTIMEDIA_DEVICE": 0x05, 56 | "SCANNER_DEVICE": 0x06, 57 | "OPTICAL_MEMORY_DEVICE": 0x07, 58 | "MEDIA_CHANGER_DEVICE": 0x08, 59 | "COMMUNICATIONS_DEVICE": 0x09, 60 | "STORAGE_ARRAY_CONTROLLER": 0x0C, 61 | "ENCLOSURE_SERVICE_DEVICE": 0x0D, 62 | "SIMPLIFIED_DIRECT_ACCESS_DEVICE": 0x0E, 63 | "OPTICAL_CARD_READER": 0x0F, 64 | "BRIDGE_CONTROLLER": 0x10, 65 | "OBJECT_STORAGE_DEVICE": 0x11, 66 | "AUTOMATION_DRIVE_DEVICE": 0x12, 67 | "SECURITY_MANAGER_DEVICE": 0x13, 68 | "WELL_KNOWN_LOGICAL_UNIT": 0x1E, 69 | "UNKNOWN_DEVICE": 0x1F, 70 | } 71 | 72 | DEVICE_TYPE = Enum(_device_types) 73 | 74 | # 75 | # Version 76 | # 77 | _versions = { 78 | "NO_STANDARD_CLAIMED": 0x00, 79 | "ANSI_INCITS_301_1997": 0x03, 80 | "SPC_2": 0x04, 81 | "SPC_3": 0x05, 82 | "SPC_4": 0x06, 83 | } 84 | 85 | VERSION = Enum(_versions) 86 | 87 | # 88 | # TargetPortalGroupSupport 89 | # 90 | _tpgss = { 91 | "NO_ASSYMETRIC_LUN_ACCESS": 0x00, 92 | "ONLY_IMPLICIT_ASSYMETRIC_LUN_ACCESS": 0x01, 93 | "ONLY_EXPLICIT_ASSYMETRIC_LUN_ACCESS": 0x02, 94 | "BOTH_IMPLICIT_AND_EXPLICIT_ASSYMETRIC_LUN_ACCESS": 0x03, 95 | } 96 | 97 | TPGS = Enum(_tpgss) 98 | 99 | # 100 | # Nominal Form Factor 101 | # 102 | _nff = { 103 | "NOT REPORTED": 0x00, 104 | "5.25": 0x01, 105 | "3.5": 0x02, 106 | "2.5": 0x03, 107 | "1.8": 0x04, 108 | "Less than 1.8": 0x05, 109 | } 110 | 111 | NOMINAL_FORM_FACTOR = Enum(_nff) 112 | 113 | _protocol_identifier = { 114 | "FIBRE_CHANNEL": 0x00, 115 | "SCSI_PARALLEL_INTERFACE": 0x01, 116 | "SERIAL_STORAGE_ARCHITECTURE": 0x02, 117 | "SERIAL_BUS_PROTOCOL": 0x03, 118 | "RDMA": 0x04, 119 | "ISCSI": 0x05, 120 | "SAS": 0x06, 121 | "AUTOMATION_DRIVE_INTERFACE": 0x07, 122 | "AT_ATTACHMENT_INTERFACE": 0x08, 123 | "USB_ATTACHED_SCSI": 0x09, 124 | "SCSI_OVER_PCI_EXPRESS": 0x0A, 125 | "NO_SPECIFIC_PROTOCOL": 0x0F, 126 | } 127 | 128 | PROTOCOL_IDENTIFIER = Enum(_protocol_identifier) 129 | 130 | _code_set = { 131 | "BINARY": 0x01, 132 | "ASCII": 0x02, 133 | "UTF8": 0x03, 134 | } 135 | 136 | CODE_SET = Enum(_code_set) 137 | 138 | _association = { 139 | "ASSOCIATED_WITH_LUN": 0x00, 140 | "ASSOCIATED_WITH_TARGET_PORT": 0x01, 141 | "ASSOCIATED_WITH_TARGET_DEVICE": 0x02, 142 | } 143 | 144 | ASSOCIATION = Enum(_association) 145 | 146 | _designator = { 147 | "VENDOR_SPECIFIC": 0x00, 148 | "T10_VENDOR_ID": 0x01, 149 | "EUI_64": 0x02, 150 | "NAA": 0x03, 151 | "RELATIVE_TARGET_PORT_IDENTIFIER": 0x04, 152 | "TARGET_PORTAL_GROUP": 0x05, 153 | "LOGICAL_UNIT_GROUP": 0x06, 154 | "MD5_LOGICAL_IDENTIFIER": 0x07, 155 | "SCSI_NAME_STRING": 0x08, 156 | "PCI_EXPRESS_ROUTING_ID": 0x09, 157 | } 158 | 159 | DESIGNATOR = Enum(_designator) 160 | 161 | _naa = { 162 | "IEEE_EXTENDED": 0x02, 163 | "LOCALLY_ASSIGNED": 0x03, 164 | "IEEE_REGISTERED": 0x05, 165 | "IEEE_REGISTERED_EXTENDED": 0x06, 166 | } 167 | 168 | NAA = Enum(_naa) 169 | 170 | # 171 | # VPD pages 172 | # 173 | 174 | _vpds = { 175 | "SUPPORTED_VPD_PAGES": 0x00, 176 | "UNIT_SERIAL_NUMBER": 0x80, 177 | "DEVICE_IDENTIFICATION": 0x83, 178 | "SOFTWARE_INTERFACE_IDENTIFICATION": 0x84, 179 | "MANAGEMENT_NETWORK_ADDRESS": 0x85, 180 | "EXTENDED_INQUIRY_DATA": 0x86, 181 | "MODE_PAGE_POLICT": 0x87, 182 | "SCSI_PORTS": 0x88, 183 | "ATA_INFORMATION": 0x89, 184 | "POWER_CONDITION": 0x8A, 185 | "DEVICE_CONSTITUENTS": 0x8B, 186 | "CFA_PROFILE_INFORMATION": 0x8C, 187 | "POWER_CONSUMPTION": 0x8D, 188 | "THIRD_PARTY_COPY": 0x8F, 189 | "PROTOCOL_SPECIFIC_LOGICAL_UNIT_INFORMATION": 0x90, 190 | "PROTOCOL_SPECIFIC_PORT_INFORMATION": 0x91, 191 | # 192 | # SBC 193 | # 194 | "BLOCK_LIMITS": 0xB0, 195 | "BLOCK_DEVICE_CHARACTERISTICS": 0xB1, 196 | "LOGICAL_BLOCK_PROVISIONING": 0xB2, 197 | "REFERRALS": 0xB3, 198 | "SUPPORTED_BLOCK_LENGTHS_AND_PROTECTION_TYPES": 0xB4, 199 | "BLOCK_DEVICE_CHARACTERISTICS_EXTENSION": 0xB5, 200 | } 201 | 202 | VPD = Enum(_vpds) 203 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_enum_persistentreserve.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # Copyright (C) 2023 by Brian Meagher 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | from pyscsi.utils.enum import Enum 10 | 11 | __all__ = ["PR_TYPE", "PR_SCOPE", "PROTOCOL_ID"] 12 | 13 | # ------------------------------------------------------------------------------ 14 | # Persistent reservation TYPE 15 | # ------------------------------------------------------------------------------ 16 | 17 | pr_type = { 18 | "WRITE_EXCLUSIVE": 0x01, 19 | "EXCLUSIVE_ACCESS": 0x03, 20 | "WRITE_EXCLUSIVE_REGISTRANTS_ONLY": 0x05, 21 | "EXCLUSIVE_ACCESS_REGISTRANTS_ONLY": 0x06, 22 | "WRITE_EXCLUSIVE_ALL_REGISTRANTS": 0x07, 23 | "EXCLUSIVE_ACCESS_ALL_REGISTRANTS": 0x08, 24 | } 25 | 26 | # ------------------------------------------------------------------------------ 27 | # Persistent reservations scope 28 | # ------------------------------------------------------------------------------ 29 | 30 | pr_scope = { 31 | "LU_SCOPE": 0x00, 32 | } 33 | 34 | # ------------------------------------------------------------------------------ 35 | # Protocol Identifier 36 | # ------------------------------------------------------------------------------ 37 | 38 | protocol_id = { 39 | "FIBRE_CHANNEL": 0x00, 40 | "IEEE_1394": 0x03, 41 | "RDMA": 0x04, 42 | "ISCSI": 0x05, 43 | "SAS": 0x06, 44 | "SOP": 0x0A, 45 | } 46 | 47 | # ------------------------------------------------------------------------------ 48 | # Instantiate the Enum Objects 49 | # ------------------------------------------------------------------------------ 50 | 51 | PR_TYPE = Enum(pr_type) 52 | PR_SCOPE = Enum(pr_scope) 53 | PROTOCOL_ID = Enum(protocol_id) 54 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_enum_readcapacity16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.utils.enum import Enum 9 | 10 | # 11 | # P_TYPE 12 | # 13 | _p_types = { 14 | "TYPE_1_PROTECTION": 0x00, 15 | "TYPE_2_PROTECTION": 0x01, 16 | "TYPE_3_PROTECTION": 0x02, 17 | } 18 | 19 | P_TYPE = Enum(_p_types) 20 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_enum_readcd.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | # coding: utf-8 6 | 7 | 8 | from pyscsi.utils.enum import Enum 9 | 10 | # 11 | # EXPECTED_SECTOR_TYPE 12 | # 13 | _expected_sector_type = { 14 | "CDDA": 1, 15 | "MODE_1": 2, 16 | "MODE_2_FORMLESS": 3, 17 | "MODE_2_FORM_1": 4, 18 | "MODE_2_FORM_2": 5, 19 | } 20 | 21 | EXPECTED_SECTOR_TYPE = Enum(_expected_sector_type) 22 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_enum_readdiscinformation.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2021 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2021 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.utils.enum import Enum 9 | 10 | __all__ = [ 11 | "DISC_INFORMATION_DATA_TYPE", 12 | "STATE_OF_LAST_SESSION", 13 | "DISC_STATUS", 14 | "DISC_TYPE", 15 | ] 16 | 17 | disc_information_data_type = { 18 | "STANDARD_DISC_INFORMATION": 0x00, 19 | "TRACK_RESOURCES_INFORMATION": 0x01, 20 | "POW_RESOURCES_DISC_INFORMATION": 0x02, 21 | } 22 | state_of_last_session = { 23 | "EMPTY_SESSION": 0x00, 24 | "INCOMPLETE_SESSION": 0x01, 25 | "DAMAGED_SESSION": 0x02, 26 | "COMPLETE_SESSION": 0x03, 27 | } 28 | 29 | disc_status = { 30 | "EMPTY_DISC": 0x00, 31 | "INCOMPLETE_DISC": 0x01, 32 | "FINALIZED_DISC": 0x02, 33 | "OTHERS": 0x03, 34 | } 35 | 36 | disc_type = { 37 | "CD-DA or CD-ROM": 0x00, 38 | "CD-I": 0x10, 39 | "CD-ROM XA": 0x20, 40 | "UNDEFINED": 0xFF, 41 | } 42 | DISC_INFORMATION_DATA_TYPE = Enum(disc_information_data_type) 43 | STATE_OF_LAST_SESSION = Enum(state_of_last_session) 44 | DISC_STATUS = Enum(disc_status) 45 | DISC_TYPE = Enum(disc_type) 46 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_enum_readelementstatus.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | __all__ = [ 9 | "ELEMENT_TYPE", 10 | ] 11 | 12 | from pyscsi.utils.enum import Enum 13 | 14 | # 15 | # Element Type Code 16 | # 17 | _element_type = { 18 | "ALL": 0x00, 19 | "MEDIUM_TRANSPORT": 0x01, 20 | "STORAGE": 0x02, 21 | "IMPORT_EXPORT": 0x03, 22 | "DATA_TRANSFER": 0x04, 23 | } 24 | 25 | ELEMENT_TYPE = Enum(_element_type) 26 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_enum_report_target_port_groups.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | # coding: utf-8 6 | 7 | 8 | from pyscsi.utils.enum import Enum 9 | 10 | # 11 | # PARAMETER DATA FORMAT TYPE 12 | # 13 | _data_format_type = { 14 | "LENGTH_ONLY_HEADER_PARAMETER_DATA_FORMAT": 0, 15 | "EXTENDED_HEADER_PARAMETER_DATA_FORMAT": 1, 16 | } 17 | 18 | DATA_FORMAT_TYPE = Enum(_data_format_type) 19 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_exception.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # Copyright (C) 2016 by Diego Elio Pettenò 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | from pyscsi.pyscsi.scsi_sense import SCSICheckCondition 10 | 11 | 12 | class SCSICommandExceptionMeta(type): 13 | """ 14 | A meta class for class depending SCSICommand exceptions 15 | """ 16 | 17 | def __new__(mcs, cls, bases, attributes): 18 | class CommandNotImplemented(Exception): 19 | pass 20 | 21 | class MissingBlocksizeException(Exception): 22 | pass 23 | 24 | class OpcodeException(Exception): 25 | pass 26 | 27 | attributes.update({"CommandNotImplemented": CommandNotImplemented}) 28 | attributes.update({"MissingBlocksizeException": MissingBlocksizeException}) 29 | attributes.update({"OpcodeException": OpcodeException}) 30 | 31 | return type.__new__(mcs, cls, bases, attributes) 32 | 33 | 34 | class SCSIDeviceExceptionMeta(type): 35 | """ 36 | A meta class for class depending SCSICommand exceptions 37 | """ 38 | 39 | def __new__(mcs, cls, bases, attributes): 40 | class CheckCondition(SCSICheckCondition): 41 | pass 42 | 43 | class ConditionsMet(Exception): 44 | pass 45 | 46 | class BusyStatus(Exception): 47 | pass 48 | 49 | class ReservationConflict(Exception): 50 | pass 51 | 52 | class TaskSetFull(Exception): 53 | pass 54 | 55 | class ACAActive(Exception): 56 | pass 57 | 58 | class TaskAborted(Exception): 59 | pass 60 | 61 | attributes.update({"CheckCondition": CheckCondition}) 62 | attributes.update({"ConditionsMet": ConditionsMet}) 63 | attributes.update({"BusyStatus": BusyStatus}) 64 | attributes.update({"ReservationConflict": ReservationConflict}) 65 | attributes.update({"TaskSetFull": TaskSetFull}) 66 | attributes.update({"ACAActive": ACAActive}) 67 | attributes.update({"TaskAborted": TaskAborted}) 68 | 69 | return type.__new__(mcs, cls, bases, attributes) 70 | 71 | 72 | class SCSIDeviceCommandExceptionMeta(SCSICommandExceptionMeta, SCSIDeviceExceptionMeta): 73 | def __init__(cls, name, bases, attr): 74 | SCSICommandExceptionMeta.__init__(cls, name, bases, attr) 75 | SCSIDeviceExceptionMeta.__init__(cls, name, bases, attr) 76 | 77 | def __new__(mcs, name, bases, attr): 78 | t1 = SCSICommandExceptionMeta.__new__(mcs, name, bases, attr) 79 | name = t1.__name__ 80 | bases = tuple(t1.mro()) 81 | attr = t1.__dict__.copy() 82 | t2 = SCSIDeviceExceptionMeta.__new__(mcs, name, bases, attr) 83 | return t2 84 | -------------------------------------------------------------------------------- /pyscsi/pyscsi/scsi_opcode.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.utils.enum import Enum 9 | 10 | 11 | class OpCode(object): 12 | """ 13 | A class to hold information about a scsi operation code 14 | """ 15 | 16 | _name = "" 17 | _code = 0xFF 18 | _serviceaction = None 19 | 20 | def __init__(self, name, code, serviceaction): 21 | """ 22 | initialize a new instance 23 | 24 | :param name: a string representing the name of the operation code 25 | :param code: a hexadecimal value representing the value associated with the operation code 26 | :param serviceaction: a Enum with service actions supported by the command associtaed with the operation code 27 | """ 28 | self._name = name 29 | self._code = code 30 | self._serviceaction = Enum(serviceaction) 31 | 32 | def __str__(self): 33 | return "%s - %x" % (self.name, self.value) 34 | 35 | def __repr__(self): 36 | return "%s - %x" % (self.name, self.value) 37 | 38 | @property 39 | def name(self): 40 | """ 41 | getter method of the name property 42 | 43 | :return: a string 44 | """ 45 | return self._name 46 | 47 | @name.setter 48 | def name(self, value): 49 | """ 50 | setter method of the name property 51 | 52 | :param value: a string 53 | """ 54 | self._name = value 55 | 56 | @property 57 | def value(self): 58 | """ 59 | getter method of the value property 60 | 61 | :return: a hex value 62 | """ 63 | return self._code 64 | 65 | @value.setter 66 | def value(self, value): 67 | """ 68 | setter method of the value property 69 | 70 | :param value: a hex value 71 | """ 72 | self._code = value 73 | 74 | @property 75 | def serviceaction(self): 76 | """ 77 | getter method of the serviceaction property 78 | 79 | :return: a Enum object 80 | """ 81 | return self._serviceaction 82 | 83 | @serviceaction.setter 84 | def serviceaction(self, value): 85 | """ 86 | setter method of the serviceaction property 87 | 88 | :param value: a Enum object 89 | """ 90 | self._serviceaction = value 91 | -------------------------------------------------------------------------------- /pyscsi/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | import socket 6 | 7 | from .converter import * 8 | from .enum import * 9 | 10 | 11 | def init_device( 12 | dev, 13 | read_write=False, 14 | initiator_name=f"iqn.2018-01.org.pyscsi:{socket.gethostname()}", 15 | ): 16 | if dev[:5] == "/dev/": 17 | from pyscsi.pyscsi.scsi_device import SCSIDevice 18 | 19 | device = SCSIDevice(dev, read_write) 20 | elif dev[:8] == "iscsi://": 21 | from pyscsi.pyiscsi.iscsi_device import ISCSIDevice 22 | 23 | device = ISCSIDevice(dev, initiator_name) 24 | else: 25 | raise NotImplementedError("No backend implemented for %s" % dev) 26 | return device 27 | -------------------------------------------------------------------------------- /pyscsi/utils/converter.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | from typing import Mapping, Sequence, Tuple, Union 10 | 11 | CheckDict = Mapping[str, Union[Sequence[int], Tuple[int, int], Tuple[str, int, int]]] 12 | 13 | 14 | def scsi_int_to_ba(to_convert=0, array_size=4): 15 | """ 16 | This function converts a integer of (8 *array_size)-bit to a bytearray(array_size) in 17 | BigEndian byte order. Here we use the 32-bit as default. 18 | 19 | example: 20 | 21 | >>scsi_to_ba(34,4) 22 | bytearray(b'\x00\x00\x00"') 23 | 24 | so we take a 32-bit integer and get a byte array(4) 25 | 26 | :param to_convert: a integer 27 | :param array_size: a integer defining the size of the byte array 28 | :return: a byte array 29 | """ 30 | return bytearray((to_convert >> i * 8) & 0xFF for i in reversed(range(array_size))) 31 | 32 | 33 | def scsi_ba_to_int(ba): 34 | """ 35 | This function converts a bytearray in BigEndian byte order 36 | to an integer. 37 | 38 | :param ba: a bytearray 39 | :return: an integer 40 | """ 41 | return sum(ba[i] << ((len(ba) - 1 - i) * 8) for i in range(len(ba))) 42 | 43 | 44 | def decode_bits(data, check_dict, result_dict): 45 | """ 46 | helper method to perform some simple bit operations 47 | 48 | the list in the value of each key:value pair contains 2 values 49 | - the bit mask 50 | - the offset byte in the datain byte array 51 | 52 | for now we assume he have to right shift only 53 | 54 | :param data: a buffer containing the bits to decode 55 | :param check_dict: a dict mapping field-names to notation tuples. 56 | :param result_dict: a dict mapping field-names to notation tuples. 57 | """ 58 | for key in check_dict.keys(): 59 | # Notation format: 60 | # 61 | # If the length is 2 we have the legacy notation [bitmask, offset] 62 | # Example: 'sync': [0x10, 7], 63 | # 64 | # >2-tuples is the new style of notation. 65 | # These tuples always consist of at least three elements, where the 66 | # first element is a string that describes the type of value. 67 | # 68 | # 'b': Byte array blobs 69 | # ---------------- 70 | # ('b', offset, length) 71 | # Example: 't10_vendor_identification': ('b', 8, 8), 72 | # 73 | 74 | val = check_dict[key] 75 | if len(val) == 2: 76 | bitmask, byte_pos = val 77 | _num = 1 78 | _bm = bitmask 79 | while _bm > 0xFF: 80 | _bm >>= 8 81 | _num += 1 82 | value = scsi_ba_to_int(data[byte_pos : byte_pos + _num]) 83 | while not bitmask & 0x01: 84 | bitmask >>= 1 85 | value >>= 1 86 | value &= bitmask 87 | elif val[0] == "b": 88 | offset, length = val[1:] 89 | value = data[offset : offset + length] 90 | elif val[0] == "w": 91 | offset, length = val[1:] 92 | value = data[offset : offset + length * 2] 93 | elif val[0] == "dw": 94 | offset, length = val[1:] 95 | value = data[offset : offset + length * 4] 96 | result_dict.update({key: value}) 97 | 98 | 99 | def encode_dict(data_dict, check_dict, result): 100 | """ 101 | helper method to perform some simple bit operations 102 | 103 | the list in the value of each key:value pair contains 2 values 104 | - the bit mask 105 | - the offset byte in the datain byte array 106 | 107 | for now we assume he have to right shift only 108 | 109 | :param data_dict: a dict mapping field-names to notation tuples. 110 | :param check_dict: a dict mapping field-names to notation tuples. 111 | :param result: a buffer containing the bits encoded 112 | """ 113 | for key in data_dict.keys(): 114 | if key not in check_dict: 115 | continue 116 | value = data_dict[key] 117 | 118 | val = check_dict[key] 119 | if len(val) == 2: 120 | bitmask, bytepos = val 121 | 122 | _num = 1 123 | _bm = bitmask 124 | while _bm > 0xFF: 125 | _bm >>= 8 126 | _num += 1 127 | 128 | _bm = bitmask 129 | while not _bm & 0x01: 130 | _bm >>= 1 131 | value <<= 1 132 | 133 | v = scsi_int_to_ba(value, _num) 134 | for i in range(len(v)): 135 | result[bytepos + i] ^= v[i] 136 | elif val[0] == "b": 137 | offset, length = val[1:] 138 | result[offset : offset + length] = value 139 | elif val[0] == "w": 140 | offset, length = val[1:] 141 | result[offset : offset + length * 2] = value 142 | elif val[0] == "dw": 143 | offset, length = val[1:] 144 | result[offset : offset + length * 4] = value 145 | 146 | 147 | def print_data(data_dict): 148 | """ 149 | A small method to print out data we generate in this package. 150 | 151 | It's not really a converter but in a way we convert a dict of 152 | key - value pairs into strings ... 153 | 154 | :param data_dict: a dictionary 155 | :return: a few strings 156 | """ 157 | for k, v in data_dict.items(): 158 | if isinstance(v, dict): 159 | print(k) 160 | print_data(v) 161 | else: 162 | if isinstance(v, str): 163 | print("%s -> %s" % (k, v)) 164 | elif isinstance(v, float): 165 | print("%s -> %.02d" % (k, v)) 166 | else: 167 | print("%s -> 0x%02X" % (k, v)) 168 | 169 | 170 | def get_opcode(enum, part): 171 | """ 172 | A generator that returns an OpCode object from a given 173 | Enum object. 174 | 175 | :param enum: the Enum of opcodes 176 | :param part: a string to lookup in the enum keys 177 | :return: an OpCode object 178 | """ 179 | for val in enum.keys: 180 | if val[len(val) - 2 :] == part: 181 | yield getattr(enum, val) 182 | -------------------------------------------------------------------------------- /pyscsi/utils/enum.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | # we disable some pylint errors since we know it will work .. 10 | # pylint: disable=not-an-iterable 11 | # pylint: disable=unsupported-membership-test 12 | 13 | from typing import Any, Dict, List 14 | 15 | from pyscsi.utils.exception import NotSupportedArgumentError 16 | 17 | 18 | class Enum(type): 19 | """A class for pseudo enumerators 20 | 21 | usage: 22 | 23 | >>fubar = Enum(a=1,b=2) 24 | >>fubar.a 25 | 1 26 | >>fubar= Enum({'a': 1, 'b': 2}) 27 | >>fubar.a 28 | 1 29 | 30 | for now there is not much of a sanity check here, like if the key already exists 31 | or if a enum value is already there. It's basically a little helper for the other 32 | packages in this project and for now the developer has to take care that stuff is 33 | sane. And we assume that we pass either a dict or simply keyword arguments! 34 | 35 | TODO: 36 | 37 | - adding check if value exists 38 | """ 39 | 40 | def __new__(cls, *args: Any, **kwargs: Any): 41 | """ 42 | Building a new Enum object with a dict or keyword arguments 43 | """ 44 | tmp: Dict[str, Any] = {} 45 | if len(args) == 1 and type(args[0]).__name__ == "dict": 46 | tmp.update(args[0]) 47 | elif kwargs: 48 | tmp.update(**kwargs) 49 | else: 50 | raise NotSupportedArgumentError( 51 | "use either as dict or provide keyword arguments" 52 | ) 53 | return super().__new__(cls, cls.__name__, (), tmp) 54 | 55 | def __init__(cls, *args: Any, **kwargs: Any) -> None: 56 | super().__init__(cls.__name__, args, kwargs) 57 | 58 | def __getitem__(cls, value: str) -> str: 59 | for key in cls.keys: 60 | if getattr(cls, key) == value: 61 | return key 62 | return "" 63 | 64 | def add(cls, key: str, value: Any) -> None: 65 | """ 66 | method to add key to the Enum 67 | """ 68 | if key in cls.keys: 69 | raise KeyError(f"key {key} already exist") 70 | setattr(cls, key, value) 71 | 72 | def remove(cls, key: str) -> None: 73 | """ 74 | method to remove key from the Enum 75 | """ 76 | try: 77 | delattr(cls, key) 78 | except (AttributeError, KeyError) as ex: 79 | raise KeyError(f"Key {ex} not found") from ex 80 | 81 | @property 82 | def keys(cls) -> List[str]: 83 | """ 84 | Property to return a list of Keys in the Enum. 85 | """ 86 | result: List[str] = [ 87 | key 88 | for key, val in vars(cls).items() 89 | if not callable(val) 90 | and not key.startswith("__") 91 | or not type(val).__name__ != "method" 92 | ] 93 | return result 94 | -------------------------------------------------------------------------------- /pyscsi/utils/exception.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2022 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | 9 | class NotSupportedArgumentError(Exception): 10 | """ 11 | Exception for a not supported argument. 12 | """ 13 | 14 | ... 15 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | [metadata] 6 | name = PYSCSI 7 | description = Module for calling SCSI devices from Python 8 | long_description = file: README.md 9 | long_description_content_type = text/markdown 10 | url = https://github.com/python-scsi/python-scsi 11 | maintainer = Markus Rosjat 12 | maintainer_email = markus.rosjat@gmail.com 13 | license = LGPL-2.1 14 | license_file = LICENSE 15 | license_files = 16 | LICENSES/* 17 | classifiers = 18 | License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2) 19 | License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) 20 | Programming Language :: Python 21 | Programming Language :: Python :: 3 22 | keywords = 23 | scsi 24 | 25 | [options] 26 | packages = find: 27 | python_requires = ~= 3.7 28 | 29 | [options.extras_require] 30 | dev = 31 | isort 32 | mypy 33 | pre-commit 34 | pytest 35 | pytest-mypy 36 | setuptools>=42 37 | setuptools_scm[toml]>=3.4 38 | wheel 39 | iscsi = 40 | cython-iscsi 41 | sgio = 42 | cython-sgio>=1.1.2 43 | 44 | [options.packages.find] 45 | exclude = 46 | tests 47 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: LGPL-2.1-or-later 4 | 5 | import setuptools_scm # noqa: F401 # Ensure it's present. 6 | from setuptools import setup 7 | 8 | setup() 9 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 The python-scsi Authors 2 | # 3 | # SPDX-License-Identifier: MIT 4 | -------------------------------------------------------------------------------- /tests/mock_device.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2015 by Markus Rosjat 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | from pyscsi.pyscsi.scsi import SCSI 9 | 10 | 11 | class MockSCSI(SCSI): 12 | def __init__(self, dev): 13 | self.device = dev 14 | 15 | 16 | class MockDevice: 17 | _opcodes = None 18 | 19 | def __init__(self, opcodes): 20 | self.opcodes = opcodes 21 | 22 | @property 23 | def opcodes(self): 24 | return self._opcodes 25 | 26 | @opcodes.setter 27 | def opcodes(self, value): 28 | self._opcodes = value 29 | 30 | def execute(self, cmd, en_raw_sense: bool = False): 31 | pass 32 | 33 | def open(self): 34 | pass 35 | 36 | def close(self): 37 | pass 38 | -------------------------------------------------------------------------------- /tests/test_cdb_exchangemedium.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_exchangemedium import ExchangeMedium 12 | from pyscsi.pyscsi.scsi_enum_command import smc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbExchangemediumTest(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(smc)) as s: 20 | m = s.exchangemedium(15, 32, 64, 32, inv1=1) 21 | cdb = m.cdb 22 | self.assertEqual(cdb[0], s.device.opcodes.EXCHANGE_MEDIUM.value) 23 | self.assertEqual(cdb[1], 0) 24 | self.assertEqual(scsi_ba_to_int(cdb[2:4]), 15) 25 | self.assertEqual(scsi_ba_to_int(cdb[4:6]), 32) 26 | self.assertEqual(scsi_ba_to_int(cdb[6:8]), 64) 27 | self.assertEqual(scsi_ba_to_int(cdb[8:10]), 32) 28 | self.assertEqual(cdb[10], 0x02) 29 | cdb = m.unmarshall_cdb(cdb) 30 | self.assertEqual(cdb["opcode"], s.device.opcodes.EXCHANGE_MEDIUM.value) 31 | self.assertEqual(cdb["medium_transport_address"], 15) 32 | self.assertEqual(cdb["source_address"], 32) 33 | self.assertEqual(cdb["first_destination_address"], 64) 34 | self.assertEqual(cdb["second_destination_address"], 32) 35 | self.assertEqual(cdb["inv1"], 1) 36 | self.assertEqual(cdb["inv2"], 0) 37 | 38 | d = ExchangeMedium.unmarshall_cdb(ExchangeMedium.marshall_cdb(cdb)) 39 | self.assertEqual(d, cdb) 40 | -------------------------------------------------------------------------------- /tests/test_cdb_getlbastatus.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_getlbastatus import GetLBAStatus 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbGetlbastatusTest(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | r = s.getlbastatus(19938722, alloclen=1112527) 21 | cdb = r.cdb 22 | self.assertEqual(cdb[0], s.device.opcodes.SBC_OPCODE_9E.value) 23 | self.assertEqual( 24 | cdb[1], s.device.opcodes.SBC_OPCODE_9E.serviceaction.GET_LBA_STATUS 25 | ) 26 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 19938722) 27 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 1112527) 28 | self.assertEqual(cdb[14:16], bytearray(2)) 29 | cdb = r.unmarshall_cdb(cdb) 30 | self.assertEqual(cdb["opcode"], s.device.opcodes.SBC_OPCODE_9E.value) 31 | self.assertEqual( 32 | cdb["service_action"], 33 | s.device.opcodes.SBC_OPCODE_9E.serviceaction.GET_LBA_STATUS, 34 | ) 35 | self.assertEqual(cdb["lba"], 19938722) 36 | self.assertEqual(cdb["alloc_len"], 1112527) 37 | 38 | d = GetLBAStatus.unmarshall_cdb(GetLBAStatus.marshall_cdb(cdb)) 39 | self.assertEqual(d, cdb) 40 | -------------------------------------------------------------------------------- /tests/test_cdb_initelementstatus.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_initelementstatus import InitializeElementStatus 12 | from pyscsi.pyscsi.scsi_enum_command import smc 13 | from tests.mock_device import MockDevice, MockSCSI 14 | 15 | 16 | class CdbInitelementstatusTest(unittest.TestCase): 17 | def test_main(self): 18 | with MockSCSI(MockDevice(smc)) as s: 19 | r = s.initializeelementstatus() 20 | cdb = r.cdb 21 | self.assertEqual(cdb[0], s.device.opcodes.INITIALIZE_ELEMENT_STATUS.value) 22 | cdb = r.unmarshall_cdb(cdb) 23 | self.assertEqual( 24 | cdb["opcode"], s.device.opcodes.INITIALIZE_ELEMENT_STATUS.value 25 | ) 26 | 27 | d = InitializeElementStatus.unmarshall_cdb( 28 | InitializeElementStatus.marshall_cdb(cdb) 29 | ) 30 | self.assertEqual(d, cdb) 31 | -------------------------------------------------------------------------------- /tests/test_cdb_initelementstatuswithrange.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_initelementstatuswithrange import ( 12 | InitializeElementStatusWithRange, 13 | ) 14 | from pyscsi.pyscsi.scsi_enum_command import smc 15 | from pyscsi.utils.converter import scsi_ba_to_int 16 | from tests.mock_device import MockDevice, MockSCSI 17 | 18 | 19 | class MockInitializeElementStatusWithRange(MockDevice): 20 | pass 21 | 22 | 23 | class CdbInitelementstatuswithrangeTest(unittest.TestCase): 24 | def test_main(self): 25 | with MockSCSI(MockDevice(smc)) as s: 26 | r = s.initializeelementstatuswithrange(15, 3, rng=1, fast=1) 27 | cdb = r.cdb 28 | self.assertEqual( 29 | cdb[0], s.device.opcodes.INITIALIZE_ELEMENT_STATUS_WITH_RANGE.value 30 | ) 31 | self.assertEqual(cdb[1], 0x03) 32 | self.assertEqual(scsi_ba_to_int(cdb[2:4]), 15) 33 | self.assertEqual(scsi_ba_to_int(cdb[6:8]), 3) 34 | 35 | cdb = r.unmarshall_cdb(cdb) 36 | self.assertEqual( 37 | cdb["opcode"], 38 | s.device.opcodes.INITIALIZE_ELEMENT_STATUS_WITH_RANGE.value, 39 | ) 40 | self.assertEqual(cdb["starting_element_address"], 15) 41 | self.assertEqual(cdb["number_of_elements"], 3) 42 | self.assertEqual(cdb["fast"], 1) 43 | self.assertEqual(cdb["range"], 1) 44 | 45 | d = InitializeElementStatusWithRange.unmarshall_cdb( 46 | InitializeElementStatusWithRange.marshall_cdb(cdb) 47 | ) 48 | self.assertEqual(d, cdb) 49 | -------------------------------------------------------------------------------- /tests/test_cdb_inquiry.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_inquiry import Inquiry 12 | from pyscsi.pyscsi.scsi_enum_command import spc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbInquiryTest(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(spc)) as s: 20 | # cdb for standard page request 21 | i = s.inquiry(alloclen=128) 22 | cdb = i.cdb 23 | self.assertEqual(cdb[0], s.device.opcodes.INQUIRY.value) 24 | self.assertEqual(cdb[1:3], bytearray(2)) 25 | self.assertEqual(scsi_ba_to_int(cdb[3:5]), 128) 26 | self.assertEqual(cdb[5], 0) 27 | cdb = i.unmarshall_cdb(cdb) 28 | self.assertEqual(cdb["opcode"], s.device.opcodes.INQUIRY.value) 29 | self.assertEqual(cdb["evpd"], 0) 30 | self.assertEqual(cdb["page_code"], 0) 31 | self.assertEqual(cdb["alloc_len"], 128) 32 | 33 | d = Inquiry.unmarshall_cdb(Inquiry.marshall_cdb(cdb)) 34 | self.assertEqual(d, cdb) 35 | 36 | # supported vpd pages 37 | i = s.inquiry(evpd=1, page_code=0x88, alloclen=300) 38 | cdb = i.cdb 39 | self.assertEqual(cdb[0], s.device.opcodes.INQUIRY.value) 40 | self.assertEqual(cdb[1], 0x01) 41 | self.assertEqual(cdb[2], 0x88) 42 | self.assertEqual(scsi_ba_to_int(cdb[3:5]), 300) 43 | self.assertEqual(cdb[5], 0) 44 | cdb = i.unmarshall_cdb(cdb) 45 | self.assertEqual(cdb["opcode"], s.device.opcodes.INQUIRY.value) 46 | self.assertEqual(cdb["evpd"], 1) 47 | self.assertEqual(cdb["page_code"], 0x88) 48 | self.assertEqual(cdb["alloc_len"], 300) 49 | 50 | d = Inquiry.unmarshall_cdb(Inquiry.marshall_cdb(cdb)) 51 | self.assertEqual(d, cdb) 52 | -------------------------------------------------------------------------------- /tests/test_cdb_modesense10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi import scsi_enum_modesense as MODESENSE10 12 | from pyscsi.pyscsi.scsi_cdb_modesense10 import ModeSense10 13 | from pyscsi.pyscsi.scsi_enum_command import smc 14 | from pyscsi.utils.converter import scsi_ba_to_int 15 | from tests.mock_device import MockDevice, MockSCSI 16 | 17 | 18 | class CdbModesense10Test(unittest.TestCase): 19 | def test_main(self): 20 | with MockSCSI(MockDevice(smc)) as s: 21 | # cdb for SMC: ElementAddressAssignment 22 | m = s.modesense10( 23 | page_code=MODESENSE10.PAGE_CODE.ELEMENT_ADDRESS_ASSIGNMENT 24 | ) 25 | cdb = m.cdb 26 | self.assertEqual(cdb[0], s.device.opcodes.MODE_SENSE_10.value) 27 | self.assertEqual(cdb[1], 0) 28 | self.assertEqual(cdb[2], MODESENSE10.PAGE_CODE.ELEMENT_ADDRESS_ASSIGNMENT) 29 | self.assertEqual(cdb[3], 0) 30 | self.assertEqual(cdb[4:6], bytearray(2)) 31 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 96) 32 | self.assertEqual(cdb[9], 0) 33 | cdb = m.unmarshall_cdb(cdb) 34 | self.assertEqual(cdb["opcode"], s.device.opcodes.MODE_SENSE_10.value) 35 | self.assertEqual(cdb["dbd"], 0) 36 | self.assertEqual(cdb["llbaa"], 0) 37 | self.assertEqual( 38 | cdb["page_code"], MODESENSE10.PAGE_CODE.ELEMENT_ADDRESS_ASSIGNMENT 39 | ) 40 | self.assertEqual(cdb["pc"], 0) 41 | self.assertEqual(cdb["sub_page_code"], 0) 42 | self.assertEqual(cdb["alloc_len"], 96) 43 | 44 | d = ModeSense10.unmarshall_cdb(ModeSense10.marshall_cdb(cdb)) 45 | self.assertEqual(d, cdb) 46 | 47 | m = s.modesense10( 48 | page_code=0, 49 | sub_page_code=3, 50 | llbaa=1, 51 | dbd=1, 52 | pc=MODESENSE10.PC.DEFAULT, 53 | alloclen=90, 54 | ) 55 | cdb = m.cdb 56 | self.assertEqual(cdb[0], s.device.opcodes.MODE_SENSE_10.value) 57 | self.assertEqual(cdb[1], 0x18) 58 | self.assertEqual(cdb[2], MODESENSE10.PC.DEFAULT << 6) 59 | self.assertEqual(cdb[3], 3) 60 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 90) 61 | cdb = m.unmarshall_cdb(cdb) 62 | self.assertEqual(cdb["opcode"], s.device.opcodes.MODE_SENSE_10.value) 63 | self.assertEqual(cdb["dbd"], 1) 64 | self.assertEqual(cdb["pc"], MODESENSE10.PC.DEFAULT) 65 | self.assertEqual(cdb["page_code"], 0) 66 | self.assertEqual(cdb["sub_page_code"], 3) 67 | self.assertEqual(cdb["alloc_len"], 90) 68 | self.assertEqual(cdb["llbaa"], 1) 69 | 70 | d = ModeSense10.unmarshall_cdb(ModeSense10.marshall_cdb(cdb)) 71 | self.assertEqual(d, cdb) 72 | -------------------------------------------------------------------------------- /tests/test_cdb_modesense6.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi import scsi_enum_modesense as MODESENSE6 12 | from pyscsi.pyscsi.scsi_cdb_modesense6 import ModeSense6 13 | from pyscsi.pyscsi.scsi_enum_command import spc 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbModesense6Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(spc)) as s: 20 | # cdb for SMC: ElementAddressAssignment 21 | m = s.modesense6(page_code=MODESENSE6.PAGE_CODE.ELEMENT_ADDRESS_ASSIGNMENT) 22 | cdb = m.cdb 23 | self.assertEqual(cdb[0], s.device.opcodes.MODE_SENSE_6.value) 24 | self.assertEqual(cdb[1], 0) 25 | self.assertEqual(cdb[2], MODESENSE6.PAGE_CODE.ELEMENT_ADDRESS_ASSIGNMENT) 26 | self.assertEqual(cdb[3], 0) 27 | self.assertEqual(cdb[4], 96) 28 | self.assertEqual(cdb[5], 0) 29 | cdb = m.unmarshall_cdb(cdb) 30 | self.assertEqual(cdb["opcode"], s.device.opcodes.MODE_SENSE_6.value) 31 | self.assertEqual(cdb["dbd"], 0) 32 | self.assertEqual(cdb["pc"], 0) 33 | self.assertEqual( 34 | cdb["page_code"], MODESENSE6.PAGE_CODE.ELEMENT_ADDRESS_ASSIGNMENT 35 | ) 36 | self.assertEqual(cdb["sub_page_code"], 0) 37 | self.assertEqual(cdb["alloc_len"], 96) 38 | 39 | d = ModeSense6.unmarshall_cdb(ModeSense6.marshall_cdb(cdb)) 40 | self.assertEqual(d, cdb) 41 | 42 | m = s.modesense6( 43 | page_code=0, 44 | sub_page_code=3, 45 | dbd=1, 46 | pc=MODESENSE6.PC.DEFAULT, 47 | alloclen=90, 48 | ) 49 | cdb = m.cdb 50 | self.assertEqual(cdb[0], s.device.opcodes.MODE_SENSE_6.value) 51 | self.assertEqual(cdb[1], 0x08) 52 | self.assertEqual(cdb[2], MODESENSE6.PC.DEFAULT << 6) 53 | self.assertEqual(cdb[3], 3) 54 | self.assertEqual(cdb[4], 90) 55 | self.assertEqual(cdb[5], 0) 56 | cdb = m.unmarshall_cdb(cdb) 57 | self.assertEqual(cdb["opcode"], s.device.opcodes.MODE_SENSE_6.value) 58 | self.assertEqual(cdb["dbd"], 1) 59 | self.assertEqual(cdb["pc"], MODESENSE6.PC.DEFAULT) 60 | self.assertEqual(cdb["page_code"], 0) 61 | self.assertEqual(cdb["sub_page_code"], 3) 62 | self.assertEqual(cdb["alloc_len"], 90) 63 | 64 | d = ModeSense6.unmarshall_cdb(ModeSense6.marshall_cdb(cdb)) 65 | self.assertEqual(d, cdb) 66 | -------------------------------------------------------------------------------- /tests/test_cdb_movemedium.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_movemedium import MoveMedium 12 | from pyscsi.pyscsi.scsi_enum_command import smc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbMovemediumTest(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(smc)) as s: 20 | m = s.movemedium(15, 32, 64, invert=1) 21 | cdb = m.cdb 22 | self.assertEqual(cdb[0], s.device.opcodes.MOVE_MEDIUM.value) 23 | self.assertEqual(cdb[1], 0) 24 | self.assertEqual(scsi_ba_to_int(cdb[2:4]), 15) 25 | self.assertEqual(scsi_ba_to_int(cdb[4:6]), 32) 26 | self.assertEqual(scsi_ba_to_int(cdb[6:8]), 64) 27 | self.assertEqual(cdb[8], 0) 28 | self.assertEqual(cdb[9], 0) 29 | self.assertEqual(cdb[10], 0x01) 30 | cdb = m.unmarshall_cdb(cdb) 31 | self.assertEqual(cdb["opcode"], s.device.opcodes.MOVE_MEDIUM.value) 32 | self.assertEqual(cdb["medium_transport_address"], 15) 33 | self.assertEqual(cdb["source_address"], 32) 34 | self.assertEqual(cdb["destination_address"], 64) 35 | self.assertEqual(cdb["invert"], 1) 36 | 37 | d = MoveMedium.unmarshall_cdb(MoveMedium.marshall_cdb(cdb)) 38 | self.assertEqual(d, cdb) 39 | -------------------------------------------------------------------------------- /tests/test_cdb_openclose_exportimport_element.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_openclose_exportimport_element import ( 12 | OpenCloseImportExportElement, 13 | ) 14 | from pyscsi.pyscsi.scsi_enum_command import smc 15 | from pyscsi.utils.converter import scsi_ba_to_int 16 | from tests.mock_device import MockDevice, MockSCSI 17 | 18 | 19 | class CdbOpencloseExportimportElementTest(unittest.TestCase): 20 | def test_main(self): 21 | with MockSCSI(MockDevice(smc)) as s: 22 | m = s.opencloseimportexportelement( 23 | 32, 24 | s.device.opcodes.OPEN_CLOSE_IMPORT_EXPORT_ELEMENT.serviceaction.CLOSE_IMPORTEXPORT_ELEMENT, 25 | ) 26 | cdb = m.cdb 27 | self.assertEqual( 28 | cdb[0], s.device.opcodes.OPEN_CLOSE_IMPORT_EXPORT_ELEMENT.value 29 | ) 30 | self.assertEqual(scsi_ba_to_int(cdb[2:4]), 32) 31 | self.assertEqual(cdb[4], 0x01) 32 | cdb = m.unmarshall_cdb(cdb) 33 | self.assertEqual( 34 | cdb["opcode"], s.device.opcodes.OPEN_CLOSE_IMPORT_EXPORT_ELEMENT.value 35 | ) 36 | self.assertEqual(cdb["element_address"], 32) 37 | self.assertEqual( 38 | cdb["action_code"], 39 | s.device.opcodes.OPEN_CLOSE_IMPORT_EXPORT_ELEMENT.serviceaction.CLOSE_IMPORTEXPORT_ELEMENT, 40 | ) 41 | 42 | d = OpenCloseImportExportElement.unmarshall_cdb( 43 | OpenCloseImportExportElement.marshall_cdb(cdb) 44 | ) 45 | self.assertEqual(d, cdb) 46 | -------------------------------------------------------------------------------- /tests/test_cdb_persistentreserveout.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # Copyright (C) 2023 by Brian Meagher 6 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 7 | # 8 | # SPDX-License-Identifier: LGPL-2.1-or-later 9 | 10 | import unittest 11 | 12 | from pyscsi.pyscsi.scsi_cdb_persistentreserveout import * 13 | from pyscsi.pyscsi.scsi_enum_command import spc 14 | from pyscsi.pyscsi.scsi_enum_persistentreserve import * 15 | from pyscsi.pyscsi.scsi_enum_report_target_port_groups import DATA_FORMAT_TYPE 16 | from pyscsi.utils.converter import scsi_ba_to_int 17 | from tests.mock_device import MockDevice, MockSCSI 18 | 19 | 20 | class CdbPersistentreserveoutTest(unittest.TestCase): 21 | def test_main(self): 22 | key1_data = bytearray(b"\x00\x00\x00\x00\xDE\xAD\xBE\xEF") 23 | key2_data = bytearray(b"\xAB\xCD\xEF\xAA\xBB\xCC\xDD\xEE") 24 | key1_value = 0xDEADBEEF 25 | key2_value = 0xABCDEFAABBCCDDEE 26 | 27 | with MockSCSI(MockDevice(spc)) as s: 28 | # REGISTER 29 | r = s.persistentreserveout(service_action=0x00, scope=1, pr_type=4) 30 | self.assertIsInstance(r, PersistentReserveOut) 31 | cdb = r.cdb 32 | self.assertEqual(cdb[0], s.device.opcodes.PERSISTENT_RESERVE_OUT.value) 33 | self.assertEqual( 34 | cdb[1] & 0x1F, 35 | s.device.opcodes.PERSISTENT_RESERVE_OUT.serviceaction.REGISTER, 36 | ) 37 | self.assertEqual(cdb[2], 0x14) 38 | self.assertEqual(cdb[3:5], bytearray(2)) 39 | self.assertEqual(scsi_ba_to_int(cdb[5:9]), 24) 40 | self.assertEqual(cdb[9], 0) 41 | self.assertEqual(len(cdb), 10) 42 | cdb = r.unmarshall_cdb(cdb) 43 | self.assertEqual( 44 | cdb["opcode"], s.device.opcodes.PERSISTENT_RESERVE_OUT.value 45 | ) 46 | self.assertEqual( 47 | cdb["service_action"], 48 | s.device.opcodes.PERSISTENT_RESERVE_OUT.serviceaction.REGISTER, 49 | ) 50 | self.assertEqual(cdb["scope"], 1) 51 | self.assertEqual(cdb["pr_type"], 4) 52 | d = PersistentReserveOut.unmarshall_cdb( 53 | PersistentReserveOut.marshall_cdb(cdb) 54 | ) 55 | 56 | r = s.persistentreserveout( 57 | service_action=0x00, service_action_reservation_key=key2_value 58 | ) 59 | self.assertEqual(r.cdb.hex(), "5f000000000000001800") 60 | self.assertEqual(len(r.dataout), 24) 61 | self.assertEqual( 62 | r.dataout.hex(), "0000000000000000abcdefaabbccddee0000000000000000" 63 | ) 64 | 65 | r = s.persistentreserveout( 66 | service_action=0x00, 67 | service_action_reservation_key=key2_value, 68 | spec_i_pt=1, 69 | ) 70 | self.assertEqual( 71 | r.dataout.hex(), 72 | "0000000000000000abcdefaabbccddee000000000800000000000000", 73 | ) 74 | 75 | r = s.persistentreserveout( 76 | service_action=0x00, 77 | service_action_reservation_key=key2_value, 78 | all_tg_pt=1, 79 | ) 80 | self.assertEqual( 81 | r.dataout.hex(), "0000000000000000abcdefaabbccddee0000000004000000" 82 | ) 83 | 84 | r = s.persistentreserveout( 85 | service_action=0x00, service_action_reservation_key=key2_value, aptpl=1 86 | ) 87 | self.assertEqual( 88 | r.dataout.hex(), "0000000000000000abcdefaabbccddee0000000001000000" 89 | ) 90 | 91 | r = s.persistentreserveout( 92 | service_action=0x07, 93 | reservation_key=key2_value, 94 | service_action_reservation_key=0x0102030405060708, 95 | unreg=1, 96 | aptpl=1, 97 | relative_target_port_id=0xAABB, 98 | ) 99 | self.assertEqual(r.cdb.hex(), "5f070000000000001800") 100 | self.assertEqual(len(r.dataout), 24) 101 | self.assertEqual( 102 | r.dataout.hex(), "abcdefaabbccddee01020304050607080003aabb00000000" 103 | ) 104 | 105 | r = s.persistentreserveout( 106 | service_action=0x07, 107 | reservation_key=key2_value, 108 | service_action_reservation_key=0x0102030405060708, 109 | unreg=1, 110 | aptpl=1, 111 | relative_target_port_id=0xAABB, 112 | transport_id={ 113 | "protocol_id": PROTOCOL_ID.ISCSI, 114 | "tpid_format": 0, 115 | "iscsi_name": "iqn.1993-08.org.debian:01:90c27cf89279", 116 | }, 117 | ) 118 | self.assertEqual(r.cdb.hex(), "5f070000000000004400") 119 | tid = "0500002869716e2e313939332d30382e6f72672e64656269616e3a30313a3930633237636638393237390000" 120 | self.assertEqual(len(r.dataout), 68) 121 | self.assertEqual( 122 | r.dataout.hex(), 123 | "abcdefaabbccddee01020304050607080003aabb0000002c" + tid, 124 | ) 125 | -------------------------------------------------------------------------------- /tests/test_cdb_positiontoelement.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_positiontoelement import PositionToElement 12 | from pyscsi.pyscsi.scsi_enum_command import smc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbPositiontoelementTest(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(smc)) as s: 20 | m = s.positiontoelement(15, 32, invert=1) 21 | cdb = m.cdb 22 | self.assertEqual(cdb[0], s.device.opcodes.POSITION_TO_ELEMENT.value) 23 | self.assertEqual(cdb[1], 0) 24 | self.assertEqual(scsi_ba_to_int(cdb[2:4]), 15) 25 | self.assertEqual(scsi_ba_to_int(cdb[4:6]), 32) 26 | self.assertEqual(cdb[8], 0x01) 27 | cdb = m.unmarshall_cdb(cdb) 28 | self.assertEqual(cdb["opcode"], s.device.opcodes.POSITION_TO_ELEMENT.value) 29 | self.assertEqual(cdb["medium_transport_address"], 15) 30 | self.assertEqual(cdb["destination_address"], 32) 31 | self.assertEqual(cdb["invert"], 1) 32 | 33 | d = PositionToElement.unmarshall_cdb(PositionToElement.marshall_cdb(cdb)) 34 | self.assertEqual(d, cdb) 35 | -------------------------------------------------------------------------------- /tests/test_cdb_preventallow_mediumremoval.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_preventallow_mediumremoval import PreventAllowMediumRemoval 12 | from pyscsi.pyscsi.scsi_enum_command import smc 13 | from tests.mock_device import MockDevice, MockSCSI 14 | 15 | 16 | class CdbPreventallowMediumremovalTest(unittest.TestCase): 17 | def test_main(self): 18 | with MockSCSI(MockDevice(smc)) as s: 19 | m = s.preventallowmediumremoval(prevent=3) 20 | cdb = m.cdb 21 | self.assertEqual( 22 | cdb[0], s.device.opcodes.PREVENT_ALLOW_MEDIUM_REMOVAL.value 23 | ) 24 | self.assertEqual(cdb[4], 0x03) 25 | cdb = m.unmarshall_cdb(cdb) 26 | self.assertEqual( 27 | cdb["opcode"], s.device.opcodes.PREVENT_ALLOW_MEDIUM_REMOVAL.value 28 | ) 29 | self.assertEqual(cdb["prevent"], 3) 30 | 31 | d = PreventAllowMediumRemoval.unmarshall_cdb( 32 | PreventAllowMediumRemoval.marshall_cdb(cdb) 33 | ) 34 | self.assertEqual(d, cdb) 35 | -------------------------------------------------------------------------------- /tests/test_cdb_read10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_read10 import Read10 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbRead10Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | s.blocksize = 512 21 | r = s.read10(1024, 27) 22 | cdb = r.cdb 23 | self.assertEqual(cdb[0], s.device.opcodes.READ_10.value) 24 | self.assertEqual(cdb[1], 0) 25 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 1024) 26 | self.assertEqual(cdb[6], 0) 27 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 27) 28 | self.assertEqual(cdb[9], 0) 29 | cdb = r.unmarshall_cdb(cdb) 30 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_10.value) 31 | self.assertEqual(cdb["rdprotect"], 0) 32 | self.assertEqual(cdb["dpo"], 0) 33 | self.assertEqual(cdb["fua"], 0) 34 | self.assertEqual(cdb["rarc"], 0) 35 | self.assertEqual(cdb["lba"], 1024) 36 | self.assertEqual(cdb["group"], 0) 37 | self.assertEqual(cdb["tl"], 27) 38 | 39 | d = Read10.unmarshall_cdb(Read10.marshall_cdb(cdb)) 40 | self.assertEqual(d, cdb) 41 | 42 | r = s.read10(1024, 27, rdprotect=2, dpo=1, fua=1, rarc=1, group=19) 43 | cdb = r.cdb 44 | self.assertEqual(cdb[0], s.device.opcodes.READ_10.value) 45 | self.assertEqual(cdb[1], 0x5C) 46 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 1024) 47 | self.assertEqual(cdb[6], 0x13) 48 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 27) 49 | self.assertEqual(cdb[9], 0) 50 | cdb = r.unmarshall_cdb(cdb) 51 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_10.value) 52 | self.assertEqual(cdb["rdprotect"], 2) 53 | self.assertEqual(cdb["dpo"], 1) 54 | self.assertEqual(cdb["fua"], 1) 55 | self.assertEqual(cdb["rarc"], 1) 56 | self.assertEqual(cdb["lba"], 1024) 57 | self.assertEqual(cdb["group"], 19) 58 | self.assertEqual(cdb["tl"], 27) 59 | 60 | d = Read10.unmarshall_cdb(Read10.marshall_cdb(cdb)) 61 | self.assertEqual(d, cdb) 62 | -------------------------------------------------------------------------------- /tests/test_cdb_read12.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_read12 import Read12 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbRead12Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | s.blocksize = 512 21 | 22 | r = s.read12(1024, 27) 23 | cdb = r.cdb 24 | self.assertEqual(cdb[0], s.device.opcodes.READ_12.value) 25 | self.assertEqual(cdb[1], 0) 26 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 1024) 27 | self.assertEqual(scsi_ba_to_int(cdb[6:10]), 27) 28 | self.assertEqual(cdb[10], 0) 29 | self.assertEqual(cdb[11], 0) 30 | cdb = r.unmarshall_cdb(cdb) 31 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_12.value) 32 | self.assertEqual(cdb["rdprotect"], 0) 33 | self.assertEqual(cdb["dpo"], 0) 34 | self.assertEqual(cdb["fua"], 0) 35 | self.assertEqual(cdb["rarc"], 0) 36 | self.assertEqual(cdb["lba"], 1024) 37 | self.assertEqual(cdb["group"], 0) 38 | self.assertEqual(cdb["tl"], 27) 39 | 40 | d = Read12.unmarshall_cdb(Read12.marshall_cdb(cdb)) 41 | self.assertEqual(d, cdb) 42 | 43 | r = s.read12(1024, 27, rdprotect=2, dpo=1, fua=1, rarc=1, group=19) 44 | cdb = r.cdb 45 | self.assertEqual(cdb[0], s.device.opcodes.READ_12.value) 46 | self.assertEqual(cdb[1], 0x5C) 47 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 1024) 48 | self.assertEqual(scsi_ba_to_int(cdb[6:10]), 27) 49 | self.assertEqual(cdb[10], 0x13) 50 | self.assertEqual(cdb[11], 0) 51 | cdb = r.unmarshall_cdb(cdb) 52 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_12.value) 53 | self.assertEqual(cdb["rdprotect"], 2) 54 | self.assertEqual(cdb["dpo"], 1) 55 | self.assertEqual(cdb["fua"], 1) 56 | self.assertEqual(cdb["rarc"], 1) 57 | self.assertEqual(cdb["lba"], 1024) 58 | self.assertEqual(cdb["group"], 19) 59 | self.assertEqual(cdb["tl"], 27) 60 | 61 | d = Read12.unmarshall_cdb(Read12.marshall_cdb(cdb)) 62 | self.assertEqual(d, cdb) 63 | -------------------------------------------------------------------------------- /tests/test_cdb_read16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_read16 import Read16 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbRead16Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | s.blocksize = 512 21 | 22 | r = s.read16(1024, 27) 23 | cdb = r.cdb 24 | self.assertEqual(cdb[0], s.device.opcodes.READ_16.value) 25 | self.assertEqual(cdb[1], 0) 26 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 1024) 27 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 27) 28 | self.assertEqual(cdb[14], 0) 29 | self.assertEqual(cdb[15], 0) 30 | cdb = r.unmarshall_cdb(cdb) 31 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_16.value) 32 | self.assertEqual(cdb["rdprotect"], 0) 33 | self.assertEqual(cdb["dpo"], 0) 34 | self.assertEqual(cdb["fua"], 0) 35 | self.assertEqual(cdb["rarc"], 0) 36 | self.assertEqual(cdb["lba"], 1024) 37 | self.assertEqual(cdb["group"], 0) 38 | self.assertEqual(cdb["tl"], 27) 39 | 40 | d = Read16.unmarshall_cdb(Read16.marshall_cdb(cdb)) 41 | self.assertEqual(d, cdb) 42 | 43 | r = s.read16(1024, 27, rdprotect=2, dpo=1, fua=1, rarc=1, group=19) 44 | cdb = r.cdb 45 | self.assertEqual(cdb[0], s.device.opcodes.READ_16.value) 46 | self.assertEqual(cdb[1], 0x5C) 47 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 1024) 48 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 27) 49 | self.assertEqual(cdb[14], 0x13) 50 | self.assertEqual(cdb[15], 0) 51 | cdb = r.unmarshall_cdb(cdb) 52 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_16.value) 53 | self.assertEqual(cdb["rdprotect"], 2) 54 | self.assertEqual(cdb["dpo"], 1) 55 | self.assertEqual(cdb["fua"], 1) 56 | self.assertEqual(cdb["rarc"], 1) 57 | self.assertEqual(cdb["lba"], 1024) 58 | self.assertEqual(cdb["group"], 19) 59 | self.assertEqual(cdb["tl"], 27) 60 | 61 | d = Read16.unmarshall_cdb(Read16.marshall_cdb(cdb)) 62 | self.assertEqual(d, cdb) 63 | -------------------------------------------------------------------------------- /tests/test_cdb_readcapacity10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_readcapacity10 import ReadCapacity10 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from tests.mock_device import MockDevice, MockSCSI 14 | 15 | 16 | class CdbReadcapacity10Test(unittest.TestCase): 17 | def test_main(self): 18 | with MockSCSI(MockDevice(sbc)) as s: 19 | r = s.readcapacity10() 20 | cdb = r.cdb 21 | self.assertEqual(cdb[0], s.device.opcodes.READ_CAPACITY_10.value) 22 | self.assertEqual(cdb[1:10], bytearray(9)) 23 | cdb = r.unmarshall_cdb(cdb) 24 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_CAPACITY_10.value) 25 | 26 | d = ReadCapacity10.unmarshall_cdb(ReadCapacity10.marshall_cdb(cdb)) 27 | self.assertEqual(d, cdb) 28 | -------------------------------------------------------------------------------- /tests/test_cdb_readcapacity16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_readcapacity16 import ReadCapacity16 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbReadcapacity16Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | r = s.readcapacity16(alloclen=37) 21 | cdb = r.cdb 22 | self.assertEqual(cdb[0], s.device.opcodes.SBC_OPCODE_9E.value) 23 | self.assertEqual( 24 | cdb[1], s.device.opcodes.SBC_OPCODE_9E.serviceaction.READ_CAPACITY_16 25 | ) 26 | self.assertEqual(cdb[2:10], bytearray(8)) 27 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 37) 28 | self.assertEqual(cdb[14:16], bytearray(2)) 29 | cdb = r.unmarshall_cdb(cdb) 30 | self.assertEqual(cdb["opcode"], s.device.opcodes.SBC_OPCODE_9E.value) 31 | self.assertEqual( 32 | cdb["service_action"], 33 | s.device.opcodes.SBC_OPCODE_9E.serviceaction.READ_CAPACITY_16, 34 | ) 35 | self.assertEqual(cdb["alloc_len"], 37) 36 | 37 | d = ReadCapacity16.unmarshall_cdb(ReadCapacity16.marshall_cdb(cdb)) 38 | self.assertEqual(d, cdb) 39 | -------------------------------------------------------------------------------- /tests/test_cdb_readcd.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_readcd import ReadCd 12 | from pyscsi.pyscsi.scsi_enum_command import mmc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbRead10Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(mmc)) as s: 20 | r = s.readcd(lba=640, tl=2, est=1, dap=1, mcsb=0x10, c2ei=2, scsb=5) 21 | cdb = r.cdb 22 | self.assertEqual(cdb[0], s.device.opcodes.READ_CD.value) 23 | self.assertEqual(cdb[1], 0x06) 24 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 640) 25 | self.assertEqual(scsi_ba_to_int(cdb[6:9]), 2) 26 | self.assertEqual(cdb[9], 0x84) 27 | self.assertEqual(cdb[10], 0x05) 28 | self.assertEqual(cdb[11], 0x00) 29 | cdb = r.unmarshall_cdb(cdb) 30 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_CD.value) 31 | self.assertEqual(cdb["lba"], 640) 32 | self.assertEqual(cdb["tl"], 2) 33 | self.assertEqual(cdb["est"], 1) 34 | self.assertEqual(cdb["dap"], 1) 35 | self.assertEqual(cdb["mcsb"], 0x10) 36 | self.assertEqual(cdb["c2ei"], 2) 37 | self.assertEqual(cdb["scsb"], 5) 38 | 39 | d = ReadCd.unmarshall_cdb(ReadCd.marshall_cdb(cdb)) 40 | self.assertEqual(d, cdb) 41 | 42 | r = s.readcd(lba=16384, tl=512, est=4, dap=0, mcsb=0x0A, c2ei=1, scsb=2) 43 | cdb = r.cdb 44 | self.assertEqual(cdb[0], s.device.opcodes.READ_CD.value) 45 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 16384) 46 | self.assertEqual(scsi_ba_to_int(cdb[6:9]), 512) 47 | self.assertEqual(cdb[9], 0x52) 48 | self.assertEqual(cdb[10], 0x02) 49 | self.assertEqual(cdb[11], 0x00) 50 | cdb = r.unmarshall_cdb(cdb) 51 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_CD.value) 52 | self.assertEqual(cdb["lba"], 16384) 53 | self.assertEqual(cdb["tl"], 512) 54 | self.assertEqual(cdb["est"], 4) 55 | self.assertEqual(cdb["dap"], 0) 56 | self.assertEqual(cdb["mcsb"], 0x0A) 57 | self.assertEqual(cdb["c2ei"], 1) 58 | self.assertEqual(cdb["scsb"], 2) 59 | 60 | d = ReadCd.unmarshall_cdb(ReadCd.marshall_cdb(cdb)) 61 | self.assertEqual(d, cdb) 62 | -------------------------------------------------------------------------------- /tests/test_cdb_readelementstatus.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi import scsi_enum_readelementstatus as READELEMENTSTATUS 12 | from pyscsi.pyscsi.scsi_cdb_readelementstatus import ReadElementStatus 13 | from pyscsi.pyscsi.scsi_enum_command import smc 14 | from pyscsi.utils.converter import scsi_ba_to_int 15 | from tests.mock_device import MockDevice, MockSCSI 16 | 17 | 18 | class CdbReadelementstatusTest(unittest.TestCase): 19 | def test_main(self): 20 | with MockSCSI(MockDevice(smc)) as s: 21 | # cdb for SMC: ReadElementStatus 22 | r = s.readelementstatus( 23 | 300, 24 | 700, 25 | element_type=READELEMENTSTATUS.ELEMENT_TYPE.STORAGE, 26 | voltag=1, 27 | curdata=1, 28 | dvcid=1, 29 | ) 30 | cdb = r.cdb 31 | self.assertEqual(cdb[0], s.device.opcodes.READ_ELEMENT_STATUS.value) 32 | self.assertEqual(cdb[1], 0x10 | READELEMENTSTATUS.ELEMENT_TYPE.STORAGE) 33 | self.assertEqual(scsi_ba_to_int(cdb[2:4]), 300) 34 | self.assertEqual(scsi_ba_to_int(cdb[4:6]), 700) 35 | self.assertEqual(cdb[6], 0x03) 36 | self.assertEqual(scsi_ba_to_int(cdb[7:10]), 16384) 37 | cdb = r.unmarshall_cdb(cdb) 38 | self.assertEqual(cdb["opcode"], s.device.opcodes.READ_ELEMENT_STATUS.value) 39 | self.assertEqual(cdb["voltag"], 1) 40 | self.assertEqual( 41 | cdb["element_type"], READELEMENTSTATUS.ELEMENT_TYPE.STORAGE 42 | ) 43 | self.assertEqual(cdb["starting_element_address"], 300) 44 | self.assertEqual(cdb["num_elements"], 700) 45 | self.assertEqual(cdb["curdata"], 1) 46 | self.assertEqual(cdb["dvcid"], 1) 47 | self.assertEqual(cdb["alloc_len"], 16384) 48 | 49 | d = ReadElementStatus.unmarshall_cdb(ReadElementStatus.marshall_cdb(cdb)) 50 | self.assertEqual(d, cdb) 51 | -------------------------------------------------------------------------------- /tests/test_cdb_reportpriority.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_report_priority import ReportPriority 12 | from pyscsi.pyscsi.scsi_enum_command import spc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbReportpriorityTest(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(spc)) as s: 20 | r = s.reportpriority(priority=0x00, alloclen=1112527) 21 | cdb = r.cdb 22 | self.assertEqual(cdb[0], s.device.opcodes.SPC_OPCODE_A3.value) 23 | self.assertEqual( 24 | cdb[1], s.device.opcodes.SPC_OPCODE_A3.serviceaction.REPORT_PRIORITY 25 | ) 26 | self.assertEqual(cdb[2], 0) 27 | self.assertEqual(scsi_ba_to_int(cdb[6:10]), 1112527) 28 | self.assertEqual(cdb[10:12], bytearray(2)) 29 | cdb = r.unmarshall_cdb(cdb) 30 | self.assertEqual(cdb["opcode"], s.device.opcodes.SPC_OPCODE_A3.value) 31 | self.assertEqual( 32 | cdb["service_action"], 33 | s.device.opcodes.SPC_OPCODE_A3.serviceaction.REPORT_PRIORITY, 34 | ) 35 | self.assertEqual(cdb["priority_reported"], 0) 36 | self.assertEqual(cdb["alloc_len"], 1112527) 37 | 38 | d = ReportPriority.unmarshall_cdb(ReportPriority.marshall_cdb(cdb)) 39 | self.assertEqual(d, cdb) 40 | -------------------------------------------------------------------------------- /tests/test_cdb_reporttargetportgroups.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # Copyright (C) 2023 by Brian Meagher 6 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 7 | # 8 | # SPDX-License-Identifier: LGPL-2.1-or-later 9 | 10 | import unittest 11 | 12 | from pyscsi.pyscsi.scsi_cdb_report_target_port_groups import ReportTargetPortGroups 13 | from pyscsi.pyscsi.scsi_enum_command import spc 14 | from pyscsi.pyscsi.scsi_enum_report_target_port_groups import DATA_FORMAT_TYPE 15 | from pyscsi.utils.converter import scsi_ba_to_int 16 | from tests.mock_device import MockDevice, MockSCSI 17 | 18 | 19 | class CdbReporttargetportgroupsTest(unittest.TestCase): 20 | def test_main(self): 21 | with MockSCSI(MockDevice(spc)) as s: 22 | r = s.reporttargetportgroups(data_format=0x00, alloclen=1112527) 23 | cdb = r.cdb 24 | self.assertEqual(cdb[0], s.device.opcodes.SPC_OPCODE_A3.value) 25 | self.assertEqual( 26 | cdb[1] & 0x1F, 27 | s.device.opcodes.SPC_OPCODE_A3.serviceaction.REPORT_TARGET_PORT_GROUPS, 28 | ) 29 | self.assertEqual(cdb[1] & 0xE0, 0x00) 30 | self.assertEqual(cdb[2:6], bytearray(4)) 31 | self.assertEqual(scsi_ba_to_int(cdb[6:10]), 1112527) 32 | self.assertEqual(cdb[10:12], bytearray(2)) 33 | cdb = r.unmarshall_cdb(cdb) 34 | self.assertEqual(cdb["opcode"], s.device.opcodes.SPC_OPCODE_A3.value) 35 | self.assertEqual( 36 | cdb["service_action"], 37 | s.device.opcodes.SPC_OPCODE_A3.serviceaction.REPORT_TARGET_PORT_GROUPS, 38 | ) 39 | self.assertEqual(cdb["parameter_data_format"], 0) 40 | self.assertEqual(cdb["alloc_len"], 1112527) 41 | 42 | d = ReportTargetPortGroups.unmarshall_cdb( 43 | ReportTargetPortGroups.marshall_cdb(cdb) 44 | ) 45 | self.assertEqual(d, cdb) 46 | 47 | r = s.reporttargetportgroups(data_format=0x01, alloclen=0x400) 48 | cdb = r.cdb 49 | self.assertEqual(cdb[0], s.device.opcodes.SPC_OPCODE_A3.value) 50 | self.assertEqual( 51 | cdb[1] & 0x1F, 52 | s.device.opcodes.SPC_OPCODE_A3.serviceaction.REPORT_TARGET_PORT_GROUPS, 53 | ) 54 | self.assertEqual(cdb[1] & 0xE0, 0x20) 55 | self.assertEqual(cdb[2:6], bytearray(4)) 56 | self.assertEqual(scsi_ba_to_int(cdb[6:10]), 0x400) 57 | self.assertEqual(cdb[10:12], bytearray(2)) 58 | cdb = r.unmarshall_cdb(cdb) 59 | self.assertEqual(cdb["opcode"], s.device.opcodes.SPC_OPCODE_A3.value) 60 | self.assertEqual( 61 | cdb["service_action"], 62 | s.device.opcodes.SPC_OPCODE_A3.serviceaction.REPORT_TARGET_PORT_GROUPS, 63 | ) 64 | self.assertEqual( 65 | cdb["parameter_data_format"], 66 | DATA_FORMAT_TYPE.EXTENDED_HEADER_PARAMETER_DATA_FORMAT, 67 | ) 68 | self.assertEqual(cdb["alloc_len"], 0x400) 69 | 70 | d = ReportTargetPortGroups.unmarshall_cdb( 71 | ReportTargetPortGroups.marshall_cdb(cdb) 72 | ) 73 | self.assertEqual(d, cdb) 74 | -------------------------------------------------------------------------------- /tests/test_cdb_synchronize_cache10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2024 by Brian Meagher 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | import unittest 9 | 10 | from pyscsi.pyscsi.scsi_cdb_synchronize_cache10 import SynchronizeCache10 11 | from pyscsi.pyscsi.scsi_enum_command import sbc 12 | from pyscsi.utils.converter import scsi_ba_to_int 13 | from tests.mock_device import MockDevice, MockSCSI 14 | 15 | 16 | class CdbSynchronizeCache10Test(unittest.TestCase): 17 | def test_main(self): 18 | with MockSCSI(MockDevice(sbc)) as s: 19 | s.blocksize = 512 20 | 21 | sc = s.synchronizecache10(1024, 25) 22 | cdb = sc.cdb 23 | self.assertEqual(cdb[0], s.device.opcodes.SYNCHRONIZE_CACHE_10.value) 24 | self.assertEqual(cdb[1], 0) 25 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 1024) 26 | self.assertEqual(cdb[6], 0) 27 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 25) 28 | self.assertEqual(cdb[9], 0) 29 | cdb = sc.unmarshall_cdb(cdb) 30 | self.assertEqual(cdb["opcode"], s.device.opcodes.SYNCHRONIZE_CACHE_10.value) 31 | self.assertEqual(cdb["immed"], 0) 32 | self.assertEqual(cdb["lba"], 1024) 33 | self.assertEqual(cdb["group"], 0) 34 | self.assertEqual(cdb["numblks"], 25) 35 | 36 | d = SynchronizeCache10.unmarshall_cdb(SynchronizeCache10.marshall_cdb(cdb)) 37 | self.assertEqual(d, cdb) 38 | 39 | sc = s.synchronizecache10(65536, 27, immed=1, group=19) 40 | cdb = sc.cdb 41 | self.assertEqual(cdb[0], s.device.opcodes.SYNCHRONIZE_CACHE_10.value) 42 | self.assertEqual(cdb[1], 0x02) 43 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 65536) 44 | self.assertEqual(cdb[6], 0x13) 45 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 27) 46 | self.assertEqual(cdb[9], 0) 47 | cdb = sc.unmarshall_cdb(cdb) 48 | self.assertEqual(cdb["opcode"], s.device.opcodes.SYNCHRONIZE_CACHE_10.value) 49 | self.assertEqual(cdb["immed"], 1) 50 | self.assertEqual(cdb["lba"], 65536) 51 | self.assertEqual(cdb["group"], 19) 52 | self.assertEqual(cdb["numblks"], 27) 53 | 54 | d = SynchronizeCache10.unmarshall_cdb(SynchronizeCache10.marshall_cdb(cdb)) 55 | self.assertEqual(d, cdb) 56 | -------------------------------------------------------------------------------- /tests/test_cdb_synchronize_cache16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2024 by Brian Meagher 4 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 5 | # 6 | # SPDX-License-Identifier: LGPL-2.1-or-later 7 | 8 | import unittest 9 | 10 | from pyscsi.pyscsi.scsi_cdb_synchronize_cache16 import SynchronizeCache16 11 | from pyscsi.pyscsi.scsi_enum_command import sbc 12 | from pyscsi.utils.converter import scsi_ba_to_int 13 | from tests.mock_device import MockDevice, MockSCSI 14 | 15 | 16 | class CdbSynchronizeCache16Test(unittest.TestCase): 17 | def test_main(self): 18 | with MockSCSI(MockDevice(sbc)) as s: 19 | s.blocksize = 512 20 | 21 | sc = s.synchronizecache16(1024, 25) 22 | cdb = sc.cdb 23 | self.assertEqual(cdb[0], s.device.opcodes.SYNCHRONIZE_CACHE_16.value) 24 | self.assertEqual(cdb[1], 0) 25 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 1024) 26 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 25) 27 | self.assertEqual(cdb[14], 0) 28 | cdb = sc.unmarshall_cdb(cdb) 29 | self.assertEqual(cdb["opcode"], s.device.opcodes.SYNCHRONIZE_CACHE_16.value) 30 | self.assertEqual(cdb["immed"], 0) 31 | self.assertEqual(cdb["lba"], 1024) 32 | self.assertEqual(cdb["group"], 0) 33 | self.assertEqual(cdb["numblks"], 25) 34 | 35 | d = SynchronizeCache16.unmarshall_cdb(SynchronizeCache16.marshall_cdb(cdb)) 36 | self.assertEqual(d, cdb) 37 | 38 | sc = s.synchronizecache16(65536, 27, immed=1, group=19) 39 | cdb = sc.cdb 40 | self.assertEqual(cdb[0], s.device.opcodes.SYNCHRONIZE_CACHE_16.value) 41 | self.assertEqual(cdb[1], 0x02) 42 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 65536) 43 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 27) 44 | self.assertEqual(cdb[14], 0x13) 45 | cdb = sc.unmarshall_cdb(cdb) 46 | self.assertEqual(cdb["opcode"], s.device.opcodes.SYNCHRONIZE_CACHE_16.value) 47 | self.assertEqual(cdb["immed"], 1) 48 | self.assertEqual(cdb["lba"], 65536) 49 | self.assertEqual(cdb["group"], 19) 50 | self.assertEqual(cdb["numblks"], 27) 51 | 52 | d = SynchronizeCache16.unmarshall_cdb(SynchronizeCache16.marshall_cdb(cdb)) 53 | self.assertEqual(d, cdb) 54 | -------------------------------------------------------------------------------- /tests/test_cdb_testunitready.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_testunitready import TestUnitReady 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from tests.mock_device import MockDevice, MockSCSI 14 | 15 | 16 | class CdbTestunitreadyTest(unittest.TestCase): 17 | def test_main(self): 18 | with MockSCSI(MockDevice(sbc)) as s: 19 | w = s.testunitready() 20 | cdb = w.cdb 21 | self.assertEqual(cdb[0], s.device.opcodes.TEST_UNIT_READY.value) 22 | self.assertEqual(cdb[1], 0) 23 | self.assertEqual(cdb[2], 0) 24 | self.assertEqual(cdb[3], 0) 25 | self.assertEqual(cdb[4], 0) 26 | self.assertEqual(cdb[5], 0) 27 | cdb = w.unmarshall_cdb(cdb) 28 | self.assertEqual(cdb["opcode"], s.device.opcodes.TEST_UNIT_READY.value) 29 | 30 | d = TestUnitReady.unmarshall_cdb(TestUnitReady.marshall_cdb(cdb)) 31 | self.assertEqual(d, cdb) 32 | -------------------------------------------------------------------------------- /tests/test_cdb_write10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_write10 import Write10 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbWrite10Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | s.blocksize = 512 21 | 22 | data = bytearray(27 * 512) 23 | 24 | w = s.write10(1024, 27, data) 25 | cdb = w.cdb 26 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_10.value) 27 | self.assertEqual(cdb[1], 0) 28 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 1024) 29 | self.assertEqual(cdb[6], 0) 30 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 27) 31 | self.assertEqual(cdb[9], 0) 32 | cdb = w.unmarshall_cdb(cdb) 33 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_10.value) 34 | self.assertEqual(cdb["wrprotect"], 0) 35 | self.assertEqual(cdb["dpo"], 0) 36 | self.assertEqual(cdb["fua"], 0) 37 | self.assertEqual(cdb["lba"], 1024) 38 | self.assertEqual(cdb["group"], 0) 39 | self.assertEqual(cdb["tl"], 27) 40 | 41 | d = Write10.unmarshall_cdb(Write10.marshall_cdb(cdb)) 42 | self.assertEqual(d, cdb) 43 | 44 | w = s.write10(65536, 27, data, wrprotect=2, dpo=1, fua=1, group=19) 45 | cdb = w.cdb 46 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_10.value) 47 | self.assertEqual(cdb[1], 0x58) 48 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 65536) 49 | self.assertEqual(cdb[6], 0x13) 50 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 27) 51 | self.assertEqual(cdb[9], 0) 52 | cdb = w.unmarshall_cdb(cdb) 53 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_10.value) 54 | self.assertEqual(cdb["wrprotect"], 2) 55 | self.assertEqual(cdb["dpo"], 1) 56 | self.assertEqual(cdb["fua"], 1) 57 | self.assertEqual(cdb["lba"], 65536) 58 | self.assertEqual(cdb["group"], 19) 59 | self.assertEqual(cdb["tl"], 27) 60 | 61 | d = Write10.unmarshall_cdb(Write10.marshall_cdb(cdb)) 62 | self.assertEqual(d, cdb) 63 | -------------------------------------------------------------------------------- /tests/test_cdb_write12.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_write12 import Write12 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbWrite12Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | s.blocksize = 512 21 | 22 | data = bytearray(27 * 512) 23 | 24 | w = s.write12(1024, 27, data) 25 | cdb = w.cdb 26 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_12.value) 27 | self.assertEqual(cdb[1], 0) 28 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 1024) 29 | self.assertEqual(scsi_ba_to_int(cdb[6:10]), 27) 30 | self.assertEqual(cdb[10], 0) 31 | self.assertEqual(cdb[11], 0) 32 | cdb = w.unmarshall_cdb(cdb) 33 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_12.value) 34 | self.assertEqual(cdb["wrprotect"], 0) 35 | self.assertEqual(cdb["dpo"], 0) 36 | self.assertEqual(cdb["fua"], 0) 37 | self.assertEqual(cdb["lba"], 1024) 38 | self.assertEqual(cdb["group"], 0) 39 | self.assertEqual(cdb["tl"], 27) 40 | 41 | d = Write12.unmarshall_cdb(Write12.marshall_cdb(cdb)) 42 | self.assertEqual(d, cdb) 43 | 44 | w = s.write12(65536, 27, data, wrprotect=2, dpo=1, fua=1, group=19) 45 | cdb = w.cdb 46 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_12.value) 47 | self.assertEqual(cdb[1], 0x58) 48 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 65536) 49 | self.assertEqual(scsi_ba_to_int(cdb[6:10]), 27) 50 | self.assertEqual(cdb[10], 0x13) 51 | self.assertEqual(cdb[11], 0) 52 | cdb = w.unmarshall_cdb(cdb) 53 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_12.value) 54 | self.assertEqual(cdb["wrprotect"], 2) 55 | self.assertEqual(cdb["dpo"], 1) 56 | self.assertEqual(cdb["fua"], 1) 57 | self.assertEqual(cdb["lba"], 65536) 58 | self.assertEqual(cdb["group"], 19) 59 | self.assertEqual(cdb["tl"], 27) 60 | 61 | d = Write12.unmarshall_cdb(Write12.marshall_cdb(cdb)) 62 | self.assertEqual(d, cdb) 63 | -------------------------------------------------------------------------------- /tests/test_cdb_write16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_write16 import Write16 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbWrite16Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | s.blocksize = 512 21 | 22 | data = bytearray(27 * 512) 23 | 24 | w = s.write16(1024, 27, data) 25 | cdb = w.cdb 26 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_16.value) 27 | self.assertEqual(cdb[1], 0) 28 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 1024) 29 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 27) 30 | self.assertEqual(cdb[14], 0) 31 | self.assertEqual(cdb[15], 0) 32 | cdb = w.unmarshall_cdb(cdb) 33 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_16.value) 34 | self.assertEqual(cdb["wrprotect"], 0) 35 | self.assertEqual(cdb["dpo"], 0) 36 | self.assertEqual(cdb["fua"], 0) 37 | self.assertEqual(cdb["lba"], 1024) 38 | self.assertEqual(cdb["group"], 0) 39 | self.assertEqual(cdb["tl"], 27) 40 | 41 | d = Write16.unmarshall_cdb(Write16.marshall_cdb(cdb)) 42 | self.assertEqual(d, cdb) 43 | 44 | w = s.write16(65536, 27, data, wrprotect=2, dpo=1, fua=1, group=19) 45 | cdb = w.cdb 46 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_16.value) 47 | self.assertEqual(cdb[1], 0x58) 48 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 65536) 49 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 27) 50 | self.assertEqual(cdb[14], 0x13) 51 | self.assertEqual(cdb[15], 0) 52 | cdb = w.unmarshall_cdb(cdb) 53 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_16.value) 54 | self.assertEqual(cdb["wrprotect"], 2) 55 | self.assertEqual(cdb["dpo"], 1) 56 | self.assertEqual(cdb["fua"], 1) 57 | self.assertEqual(cdb["lba"], 65536) 58 | self.assertEqual(cdb["group"], 19) 59 | self.assertEqual(cdb["tl"], 27) 60 | 61 | d = Write16.unmarshall_cdb(Write16.marshall_cdb(cdb)) 62 | self.assertEqual(d, cdb) 63 | -------------------------------------------------------------------------------- /tests/test_cdb_writesame10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_writesame10 import WriteSame10 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbWritesame10Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | s.blocksize = 512 21 | 22 | data = bytearray(512) 23 | 24 | w = s.writesame10(1024, 27, data) 25 | cdb = w.cdb 26 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_SAME_10.value) 27 | self.assertEqual(cdb[1], 0) 28 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 1024) 29 | self.assertEqual(cdb[6], 0) 30 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 27) 31 | self.assertEqual(cdb[9], 0) 32 | cdb = w.unmarshall_cdb(cdb) 33 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_SAME_10.value) 34 | self.assertEqual(cdb["wrprotect"], 0) 35 | self.assertEqual(cdb["anchor"], 0) 36 | self.assertEqual(cdb["unmap"], 0) 37 | self.assertEqual(cdb["lba"], 1024) 38 | self.assertEqual(cdb["group"], 0) 39 | self.assertEqual(cdb["nb"], 27) 40 | 41 | d = WriteSame10.unmarshall_cdb(WriteSame10.marshall_cdb(cdb)) 42 | self.assertEqual(d, cdb) 43 | 44 | w = s.writesame10(65536, 27, data, wrprotect=4, anchor=1, group=19) 45 | cdb = w.cdb 46 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_SAME_10.value) 47 | self.assertEqual(cdb[1], 0x90) 48 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 65536) 49 | self.assertEqual(cdb[6], 0x13) 50 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 27) 51 | self.assertEqual(cdb[9], 0) 52 | cdb = w.unmarshall_cdb(cdb) 53 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_SAME_10.value) 54 | self.assertEqual(cdb["wrprotect"], 4) 55 | self.assertEqual(cdb["anchor"], 1) 56 | self.assertEqual(cdb["unmap"], 0) 57 | self.assertEqual(cdb["lba"], 65536) 58 | self.assertEqual(cdb["group"], 19) 59 | self.assertEqual(cdb["nb"], 27) 60 | 61 | d = WriteSame10.unmarshall_cdb(WriteSame10.marshall_cdb(cdb)) 62 | self.assertEqual(d, cdb) 63 | 64 | w = s.writesame10(65536, 27, data, wrprotect=4, unmap=1) 65 | cdb = w.cdb 66 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_SAME_10.value) 67 | self.assertEqual(cdb[1], 0x88) 68 | self.assertEqual(scsi_ba_to_int(cdb[2:6]), 65536) 69 | self.assertEqual(cdb[6], 0) 70 | self.assertEqual(scsi_ba_to_int(cdb[7:9]), 27) 71 | self.assertEqual(cdb[9], 0) 72 | cdb = w.unmarshall_cdb(cdb) 73 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_SAME_10.value) 74 | self.assertEqual(cdb["wrprotect"], 4) 75 | self.assertEqual(cdb["anchor"], 0) 76 | self.assertEqual(cdb["unmap"], 1) 77 | self.assertEqual(cdb["lba"], 65536) 78 | self.assertEqual(cdb["group"], 0) 79 | self.assertEqual(cdb["nb"], 27) 80 | 81 | d = WriteSame10.unmarshall_cdb(WriteSame10.marshall_cdb(cdb)) 82 | self.assertEqual(d, cdb) 83 | -------------------------------------------------------------------------------- /tests/test_cdb_writesame16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_writesame16 import WriteSame16 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.utils.converter import scsi_ba_to_int 14 | from tests.mock_device import MockDevice, MockSCSI 15 | 16 | 17 | class CdbWritesame16Test(unittest.TestCase): 18 | def test_main(self): 19 | with MockSCSI(MockDevice(sbc)) as s: 20 | s.blocksize = 512 21 | data = bytearray(512) 22 | 23 | w = s.writesame16(1024, 27, data) 24 | cdb = w.cdb 25 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_SAME_16.value) 26 | self.assertEqual(cdb[1], 0) 27 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 1024) 28 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 27) 29 | self.assertEqual(cdb[14], 0) 30 | self.assertEqual(cdb[15], 0) 31 | cdb = w.unmarshall_cdb(cdb) 32 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_SAME_16.value) 33 | self.assertEqual(cdb["wrprotect"], 0) 34 | self.assertEqual(cdb["anchor"], 0) 35 | self.assertEqual(cdb["unmap"], 0) 36 | self.assertEqual(cdb["ndob"], 0) 37 | self.assertEqual(cdb["lba"], 1024) 38 | self.assertEqual(cdb["group"], 0) 39 | self.assertEqual(cdb["nb"], 27) 40 | 41 | d = WriteSame16.unmarshall_cdb(WriteSame16.marshall_cdb(cdb)) 42 | self.assertEqual(d, cdb) 43 | 44 | w = s.writesame16(65536, 27, data, wrprotect=4, anchor=1, group=19) 45 | cdb = w.cdb 46 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_SAME_16.value) 47 | self.assertEqual(cdb[1], 0x90) 48 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 65536) 49 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 27) 50 | self.assertEqual(cdb[14], 0x13) 51 | self.assertEqual(cdb[15], 0) 52 | cdb = w.unmarshall_cdb(cdb) 53 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_SAME_16.value) 54 | self.assertEqual(cdb["wrprotect"], 4) 55 | self.assertEqual(cdb["anchor"], 1) 56 | self.assertEqual(cdb["unmap"], 0) 57 | self.assertEqual(cdb["ndob"], 0) 58 | self.assertEqual(cdb["lba"], 65536) 59 | self.assertEqual(cdb["group"], 19) 60 | self.assertEqual(cdb["nb"], 27) 61 | 62 | d = WriteSame16.unmarshall_cdb(WriteSame16.marshall_cdb(cdb)) 63 | self.assertEqual(d, cdb) 64 | 65 | w = s.writesame16(65536, 27, data, wrprotect=4, unmap=1, ndob=1) 66 | cdb = w.cdb 67 | self.assertEqual(cdb[0], s.device.opcodes.WRITE_SAME_16.value) 68 | self.assertEqual(cdb[1], 0x89) 69 | self.assertEqual(scsi_ba_to_int(cdb[2:10]), 65536) 70 | self.assertEqual(scsi_ba_to_int(cdb[10:14]), 27) 71 | self.assertEqual(cdb[14], 0) 72 | self.assertEqual(cdb[15], 0) 73 | cdb = w.unmarshall_cdb(cdb) 74 | self.assertEqual(cdb["opcode"], s.device.opcodes.WRITE_SAME_16.value) 75 | self.assertEqual(cdb["wrprotect"], 4) 76 | self.assertEqual(cdb["anchor"], 0) 77 | self.assertEqual(cdb["unmap"], 1) 78 | self.assertEqual(cdb["ndob"], 1) 79 | self.assertEqual(cdb["lba"], 65536) 80 | self.assertEqual(cdb["group"], 0) 81 | self.assertEqual(cdb["nb"], 27) 82 | 83 | d = WriteSame16.unmarshall_cdb(WriteSame16.marshall_cdb(cdb)) 84 | self.assertEqual(d, cdb) 85 | -------------------------------------------------------------------------------- /tests/test_enum.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_enum_command import smc 12 | from pyscsi.utils.enum import Enum 13 | from pyscsi.utils.exception import NotSupportedArgumentError 14 | 15 | enum_dict = { 16 | "A": 1, 17 | "B": 2, 18 | "C": 3, 19 | } 20 | 21 | 22 | class EnumTest(unittest.TestCase): 23 | def test_fails_on_passing_multiple_args(self): 24 | """ 25 | method to test exeption on creation of an Enum instance 26 | """ 27 | with self.assertRaises(NotSupportedArgumentError): 28 | Enum(1, 2, 3) 29 | 30 | def test_fails_on_passing_list(self): 31 | """ 32 | method to test exeption on creation of an Enum instanc 33 | """ 34 | with self.assertRaises(NotSupportedArgumentError): 35 | Enum((1, 2, 3)) 36 | 37 | def test_key_exists(self): 38 | """ 39 | method to test exeption on adding an existing value 40 | """ 41 | i = Enum(enum_dict) 42 | with self.assertRaises(KeyError): 43 | i.add("A", 6) 44 | 45 | def test_main(self): 46 | i = Enum(enum_dict) 47 | self.assertEqual(i.A, 1) 48 | self.assertEqual(i.B, 2) 49 | self.assertEqual(i.C, 3) 50 | self.assertEqual(i[1], "A") 51 | self.assertEqual(i[2], "B") 52 | self.assertEqual(i[3], "C") 53 | self.assertEqual(i[4], "") 54 | a = Enum(A=1, B=2, C=3) 55 | self.assertEqual(a.A, 1) 56 | self.assertEqual(a.B, 2) 57 | self.assertEqual(a.C, 3) 58 | self.assertEqual(a[1], "A") 59 | self.assertEqual(a[2], "B") 60 | self.assertEqual(a[3], "C") 61 | self.assertEqual(a[4], "") 62 | self.assertEqual(smc.WRITE_BUFFER.value, 0x3B) 63 | self.assertEqual(smc.WRITE_BUFFER.name, "WRITE_BUFFER") 64 | -------------------------------------------------------------------------------- /tests/test_opcode_mapper.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_enum_command import sbc, smc, spc, ssc 12 | 13 | 14 | class OpcodeMapperTest(unittest.TestCase): 15 | def test_main(self): 16 | self.assertEqual(spc.SPC_OPCODE_A4.value, 0xA4) 17 | self.assertEqual(sbc.SBC_OPCODE_9E.value, 0x9E) 18 | self.assertEqual(ssc.READ_ELEMENT_STATUS_ATTACHED.value, 0xB4) 19 | self.assertEqual(smc.MAINTENANCE_IN.value, 0xA3) 20 | self.assertEqual( 21 | smc.MAINTENANCE_IN.serviceaction.REPORT_DEVICE_IDENTIFICATION, 0x07 22 | ) 23 | -------------------------------------------------------------------------------- /tests/test_unmarshall_getlbastatus.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_getlbastatus import GetLBAStatus 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from pyscsi.pyscsi.scsi_enum_getlbastatus import P_STATUS 14 | from pyscsi.utils.converter import scsi_int_to_ba 15 | from tests.mock_device import MockDevice, MockSCSI 16 | 17 | 18 | class MockGetLBAStatus(MockDevice): 19 | def execute(self, cmd, en_raw_sense: bool = False): 20 | cmd.datain[0:8] = bytearray(8) 21 | pos = 8 22 | 23 | lbas = bytearray(16) 24 | lbas[0:8] = scsi_int_to_ba(1023, 8) 25 | lbas[8:12] = scsi_int_to_ba(27, 4) 26 | lbas[12] = 0 27 | cmd.datain[pos : pos + len(lbas)] = lbas 28 | pos += len(lbas) 29 | 30 | lbas = bytearray(16) 31 | lbas[0:8] = scsi_int_to_ba(200000, 8) 32 | lbas[8:12] = scsi_int_to_ba(9999, 4) 33 | lbas[12] = 1 34 | cmd.datain[pos : pos + len(lbas)] = lbas 35 | pos += len(lbas) 36 | 37 | cmd.datain[0:4] = scsi_int_to_ba(pos - 4, 4) 38 | 39 | 40 | class UnmarshallGetlbastatusTest(unittest.TestCase): 41 | def test_main(self): 42 | with MockSCSI(MockGetLBAStatus(sbc)) as s: 43 | i = s.getlbastatus(0).result 44 | self.assertEqual(len(i["lbas"]), 2) 45 | self.assertEqual(i["lbas"][0]["lba"], 1023) 46 | self.assertEqual(i["lbas"][0]["num_blocks"], 27) 47 | self.assertEqual(i["lbas"][0]["p_status"], P_STATUS.MAPPED) 48 | self.assertEqual(i["lbas"][1]["lba"], 200000) 49 | self.assertEqual(i["lbas"][1]["num_blocks"], 9999) 50 | self.assertEqual(i["lbas"][1]["p_status"], P_STATUS.DEALLOCATED) 51 | 52 | d = GetLBAStatus.unmarshall_datain(GetLBAStatus.marshall_datain(i)) 53 | self.assertEqual(d, i) 54 | -------------------------------------------------------------------------------- /tests/test_unmarshall_readcapacity10.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_readcapacity10 import ReadCapacity10 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from tests.mock_device import MockDevice, MockSCSI 14 | 15 | 16 | class MockReadCapacity10(MockDevice): 17 | def execute(self, cmd, en_raw_sense: bool = False): 18 | # lba 19 | cmd.datain[0:4] = [0x00, 0x01, 0x00, 0x00] 20 | # block size 21 | cmd.datain[4:8] = [0x00, 0x00, 0x10, 0x00] 22 | 23 | 24 | class UnmarshallReadcapacity10Test(unittest.TestCase): 25 | def test_main(self): 26 | with MockSCSI(MockReadCapacity10(sbc)) as s: 27 | i = s.readcapacity10().result 28 | self.assertEqual(i["returned_lba"], 65536) 29 | self.assertEqual(i["block_length"], 4096) 30 | 31 | d = ReadCapacity10.unmarshall_datain(ReadCapacity10.marshall_datain(i)) 32 | self.assertEqual(d, i) 33 | -------------------------------------------------------------------------------- /tests/test_unmarshall_readcapacity16.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_readcapacity16 import ReadCapacity16 12 | from pyscsi.pyscsi.scsi_enum_command import sbc 13 | from tests.mock_device import MockDevice, MockSCSI 14 | 15 | 16 | class MockReadCapacity16(MockDevice): 17 | def execute(self, cmd, en_raw_sense: bool = False): 18 | # lba 19 | cmd.datain[0:8] = [0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] 20 | # block size 21 | cmd.datain[8:12] = [0x00, 0x00, 0x10, 0x00] 22 | cmd.datain[12] = 0x09 # P_TYPE:4 PROT_EN:1 23 | cmd.datain[13] = 0x88 # P_I_EXPONENT:8 LBPPBE:8 24 | cmd.datain[14] = 0xE0 # LBPME:1 LBPRZ:1 LOWEST_ALIGNED_LBA:top-bit-set 25 | cmd.datain[15] = 0x01 # LOWEST_ALIGNED_LBA:bottom-bit-set 26 | 27 | 28 | class UnmarshallReadcapacity16Test(unittest.TestCase): 29 | def test_main(self): 30 | with MockSCSI(MockReadCapacity16(sbc)) as s: 31 | i = s.readcapacity16().result 32 | self.assertEqual(i["returned_lba"], 281474976710656) 33 | self.assertEqual(i["block_length"], 4096) 34 | self.assertEqual(i["p_type"], 4) 35 | self.assertEqual(i["prot_en"], 1) 36 | self.assertEqual(i["p_i_exponent"], 8) 37 | self.assertEqual(i["lbppbe"], 8) 38 | self.assertEqual(i["lbpme"], 1) 39 | self.assertEqual(i["lbprz"], 1) 40 | self.assertEqual(i["lowest_aligned_lba"], 8193) 41 | 42 | d = ReadCapacity16.unmarshall_datain(ReadCapacity16.marshall_datain(i)) 43 | self.assertEqual(d, i) 44 | -------------------------------------------------------------------------------- /tests/test_unmarshall_readcd.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # Copyright (C) 2014 by Ronnie Sahlberg 4 | # Copyright (C) 2015 by Markus Rosjat 5 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 6 | # 7 | # SPDX-License-Identifier: LGPL-2.1-or-later 8 | 9 | import unittest 10 | 11 | from pyscsi.pyscsi.scsi_cdb_readcd import ReadCd 12 | from pyscsi.pyscsi.scsi_enum_command import mmc 13 | from tests.mock_device import MockDevice, MockSCSI 14 | 15 | _SYNC = bytes([0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00]) 16 | 17 | 18 | # 19 | # Mock for a read of two sectors for SYNC and SubChannel type 2 20 | # 21 | class MockReadCD_SyncSubC(MockDevice): 22 | def execute(self, cmd, en_raw_sense: bool = False): 23 | # sync 24 | cmd.datain[0:12] = _SYNC 25 | # subchannel 26 | cmd.datain[12:28] = [ 27 | 0x41, 28 | 0x01, 29 | 0x01, 30 | 0x00, 31 | 0x08, 32 | 0x40, 33 | 0x00, 34 | 0x00, 35 | 0x10, 36 | 0x40, 37 | 0x19, 38 | 0xCD, 39 | 0x00, 40 | 0x00, 41 | 0x00, 42 | 0x00, 43 | ] 44 | 45 | # sync 46 | cmd.datain[28:40] = _SYNC 47 | # subchannel 48 | cmd.datain[40:56] = [ 49 | 0x41, 50 | 0x01, 51 | 0x01, 52 | 0x00, 53 | 0x08, 54 | 0x41, 55 | 0x00, 56 | 0x00, 57 | 0x10, 58 | 0x41, 59 | 0xA3, 60 | 0xBD, 61 | 0x00, 62 | 0x00, 63 | 0x00, 64 | 0x80, 65 | ] 66 | 67 | 68 | # 69 | # Mock for a read of two sectors for SYNC and SectorHeader 70 | # 71 | class MockReadCD_SyncSH(MockDevice): 72 | def execute(self, cmd, en_raw_sense: bool = False): 73 | # sync 74 | cmd.datain[0:12] = _SYNC 75 | # subchannel 76 | cmd.datain[12:16] = [0x00, 0x10, 0x40, 0x01] 77 | 78 | # sync 79 | cmd.datain[16:28] = _SYNC 80 | # subchannel 81 | cmd.datain[28:32] = [0x00, 0x10, 0x41, 0x01] 82 | 83 | 84 | class UnmarshallReadCdTest(unittest.TestCase): 85 | def test_main(self): 86 | # SYNC and SubChannel 87 | with MockSCSI(MockReadCD_SyncSubC(mmc)) as s: 88 | i = s.readcd(lba=640, tl=2, est=2, mcsb=0x10, scsb=2, c2ei=0).result 89 | self.assertEqual(i[640]["sync"], _SYNC) 90 | self.assertEqual(i[640]["subchannel"]["c"], 4) 91 | self.assertEqual(i[640]["subchannel"]["adr"], 1) 92 | self.assertEqual(i[640]["subchannel"]["track-number"], 1) 93 | self.assertEqual(i[640]["subchannel"]["index-number"], 1) 94 | self.assertEqual(i[640]["subchannel"]["min"], 0) 95 | self.assertEqual(i[640]["subchannel"]["sec"], 8) 96 | self.assertEqual(i[640]["subchannel"]["frame"], 64) 97 | self.assertEqual(i[640]["subchannel"]["zero"], 0) 98 | self.assertEqual(i[640]["subchannel"]["amin"], 0) 99 | self.assertEqual(i[640]["subchannel"]["asec"], 16) 100 | self.assertEqual(i[640]["subchannel"]["aframe"], 64) 101 | self.assertEqual(i[640]["subchannel"]["crc"], 6605) 102 | self.assertEqual(i[640]["subchannel"]["p"], 0) 103 | 104 | self.assertEqual(i[641]["sync"], _SYNC) 105 | self.assertEqual(i[641]["subchannel"]["c"], 4) 106 | self.assertEqual(i[641]["subchannel"]["adr"], 1) 107 | self.assertEqual(i[641]["subchannel"]["track-number"], 1) 108 | self.assertEqual(i[641]["subchannel"]["index-number"], 1) 109 | self.assertEqual(i[641]["subchannel"]["min"], 0) 110 | self.assertEqual(i[641]["subchannel"]["sec"], 8) 111 | self.assertEqual(i[641]["subchannel"]["frame"], 65) 112 | self.assertEqual(i[641]["subchannel"]["zero"], 0) 113 | self.assertEqual(i[641]["subchannel"]["amin"], 0) 114 | self.assertEqual(i[641]["subchannel"]["asec"], 16) 115 | self.assertEqual(i[641]["subchannel"]["aframe"], 65) 116 | self.assertEqual(i[641]["subchannel"]["crc"], 41917) 117 | self.assertEqual(i[641]["subchannel"]["p"], 1) 118 | 119 | # SYNC and SectorHeader 120 | with MockSCSI(MockReadCD_SyncSH(mmc)) as s: 121 | i = s.readcd(lba=640, tl=2, est=2, mcsb=0x14, scsb=0, c2ei=0).result 122 | self.assertEqual(i[640]["sync"], _SYNC) 123 | self.assertEqual(i[640]["sector-header"]["minute"], 0) 124 | self.assertEqual(i[640]["sector-header"]["second"], 16) 125 | self.assertEqual(i[640]["sector-header"]["frame"], 64) 126 | self.assertEqual(i[640]["sector-header"]["mode"], 1) 127 | 128 | self.assertEqual(i[641]["sync"], _SYNC) 129 | self.assertEqual(i[641]["sector-header"]["minute"], 0) 130 | self.assertEqual(i[641]["sector-header"]["second"], 16) 131 | self.assertEqual(i[641]["sector-header"]["frame"], 65) 132 | self.assertEqual(i[641]["sector-header"]["mode"], 1) 133 | -------------------------------------------------------------------------------- /tools/getlbastatus.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | 9 | import sys 10 | 11 | from pyscsi.pyscsi.scsi import SCSI 12 | from pyscsi.pyscsi.scsi_device import SCSIDevice 13 | from pyscsi.pyscsi.scsi_enum_getlbastatus import P_STATUS 14 | from pyscsi.utils import init_device 15 | 16 | 17 | def usage(): 18 | print("Usage: getlbastatus.py [--help] [-l ] ") 19 | 20 | 21 | def main(): 22 | i = 1 23 | lba = 0 24 | while i < len(sys.argv): 25 | if sys.argv[i] == "--help": 26 | return usage() 27 | if sys.argv[i] == "-l": 28 | del sys.argv[i] 29 | lba = int(sys.argv[i], 10) 30 | del sys.argv[i] 31 | continue 32 | i += 1 33 | 34 | if len(sys.argv) < 2: 35 | return usage() 36 | 37 | device = sys.argv[1] 38 | 39 | sd = init_device(device) 40 | s = SCSI(sd) 41 | 42 | r = s.readcapacity16().result 43 | if not r["lbpme"]: 44 | print("LUN is fully provisioned.") 45 | return 46 | 47 | r = s.getlbastatus(lba).result 48 | for i in range(len(r["lbas"])): 49 | print( 50 | "LBA:%d-%d %s" 51 | % ( 52 | r["lbas"][i]["lba"], 53 | r["lbas"][i]["lba"] + r["lbas"][i]["num_blocks"] - 1, 54 | P_STATUS[r["lbas"][i]["p_status"]], 55 | ) 56 | ) 57 | 58 | 59 | if __name__ == "__main__": 60 | main() 61 | -------------------------------------------------------------------------------- /tools/swp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-FileCopyrightText: 2014 The python-scsi Authors 4 | # 5 | # SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | # coding: utf-8 8 | 9 | # 10 | # A simple example to show/set/clear the software write protect flag SWP 11 | # 12 | import sys 13 | 14 | from pyscsi.pyscsi import scsi_enum_modesense as MODESENSE6 15 | from pyscsi.pyscsi.scsi import SCSI 16 | from pyscsi.pyscsi.scsi_device import SCSIDevice 17 | from pyscsi.utils import init_device 18 | 19 | 20 | def usage(): 21 | print("Usage: swp.py [--help] [--on|--off] ") 22 | 23 | 24 | def main(): 25 | swp_on = 0 26 | swp_off = 0 27 | i = 1 28 | while i < len(sys.argv): 29 | if sys.argv[i] == "--help": 30 | return usage() 31 | if sys.argv[i] == "--on": 32 | del sys.argv[i] 33 | swp_on = 1 34 | continue 35 | if sys.argv[i] == "--off": 36 | del sys.argv[i] 37 | swp_off = 1 38 | continue 39 | i += 1 40 | 41 | if len(sys.argv) < 2: 42 | return usage() 43 | 44 | device = sys.argv[1] 45 | 46 | sd = init_device(device) 47 | s = SCSI(sd) 48 | i = s.modesense6(page_code=MODESENSE6.PAGE_CODE.CONTROL).result 49 | 50 | if swp_on: 51 | i["mode_pages"][0]["swp"] = 1 52 | s.modeselect6(i) 53 | print("Set SWP ON") 54 | return 55 | 56 | if swp_off: 57 | i["mode_pages"][0]["swp"] = 0 58 | s.modeselect6(i) 59 | print("Set SWP OFF") 60 | return 61 | 62 | print("SWP is %s" % ("ON" if i["mode_pages"][0]["swp"] else "OFF")) 63 | 64 | 65 | if __name__ == "__main__": 66 | main() 67 | --------------------------------------------------------------------------------