├── .gitignore ├── .gitreview ├── .stestr.conf ├── .zuul.yaml ├── LICENSE ├── README.rst ├── doc ├── requirements.txt └── source │ ├── conf.py │ ├── index.rst │ ├── reference │ └── index.rst │ └── user │ ├── drivers.rst │ └── index.rst ├── etc └── glance │ ├── rootwrap.conf │ └── rootwrap.d │ └── glance_cinder_store.filters ├── glance_store ├── __init__.py ├── _drivers │ ├── __init__.py │ ├── cinder │ │ ├── __init__.py │ │ ├── base.py │ │ ├── nfs.py │ │ ├── scaleio.py │ │ └── store.py │ ├── filesystem.py │ ├── http.py │ ├── rbd.py │ ├── s3.py │ ├── swift │ │ ├── __init__.py │ │ ├── buffered.py │ │ ├── connection_manager.py │ │ ├── store.py │ │ └── utils.py │ └── vmware_datastore.py ├── backend.py ├── capabilities.py ├── common │ ├── __init__.py │ ├── attachment_state_manager.py │ ├── cinder_utils.py │ ├── fs_mount.py │ └── utils.py ├── driver.py ├── exceptions.py ├── i18n.py ├── locale │ ├── en_GB │ │ └── LC_MESSAGES │ │ │ └── glance_store.po │ └── ko_KR │ │ └── LC_MESSAGES │ │ └── glance_store.po ├── location.py ├── multi_backend.py └── tests │ ├── __init__.py │ ├── base.py │ ├── etc │ └── glance-swift.conf │ ├── fakes.py │ ├── functional │ ├── README.rst │ ├── __init__.py │ ├── base.py │ ├── filesystem │ │ ├── __init__.py │ │ └── test_functional_filesystem.py │ └── swift │ │ ├── __init__.py │ │ └── test_functional_swift.py │ ├── unit │ ├── __init__.py │ ├── cinder │ │ ├── __init__.py │ │ ├── test_base.py │ │ ├── test_cinder_base.py │ │ ├── test_cinder_store.py │ │ ├── test_multistore_cinder.py │ │ ├── test_nfs.py │ │ └── test_scaleio.py │ ├── common │ │ ├── __init__.py │ │ ├── test_attachment_state_manager.py │ │ ├── test_cinder_utils.py │ │ ├── test_fs_mount.py │ │ └── test_utils.py │ ├── test_backend.py │ ├── test_connection_manager.py │ ├── test_driver.py │ ├── test_exceptions.py │ ├── test_filesystem_store.py │ ├── test_http_store.py │ ├── test_location.py │ ├── test_multistore_filesystem.py │ ├── test_multistore_rbd.py │ ├── test_multistore_s3.py │ ├── test_multistore_vmware.py │ ├── test_opts.py │ ├── test_rbd_store.py │ ├── test_s3_store.py │ ├── test_store_base.py │ ├── test_store_capabilities.py │ ├── test_swift_store.py │ ├── test_swift_store_multibackend.py │ ├── test_swift_store_utils.py │ ├── test_test_utils.py │ └── test_vmware_store.py │ └── utils.py ├── releasenotes ├── notes │ ├── .placeholder │ ├── 0.29.1-notes-ded2a1d473a306c7.yaml │ ├── Stein_final_release-c7df5838028b8c7e.yaml │ ├── add-store-weight-d443fbea8cc8d4c9.yaml │ ├── block-creating-encrypted-nfs-volumes-d0ff370ab762042e.yaml │ ├── bug-1620999-8b76a0ad14826197.yaml │ ├── bug-1820817-0ee70781918d232e.yaml │ ├── bug-1915602-fcc807a435d8a6bf.yaml │ ├── bug-1954883-3666d63a3c0233f1.yaml │ ├── bug-2004555-4fd67fce86c07461.yaml │ ├── cinder-fix-nfs-sparse-vol-create-76631ce05f86257c.yaml │ ├── cinder-nfs-block-qcow2-vol-4fed58b0afafc980.yaml │ ├── cinder-support-extend-in-use-volume-c6292f950ff75cca.yaml │ ├── deprecate-rados_connect_timeout-767ed1eaa026196e.yaml │ ├── deprecate-sheepdog-driver-1f9689c327f313d4.yaml │ ├── deprecate-store_add_to_backend-f419e5c4210613d2.yaml │ ├── deprecate-store_capabilities_update_min_interval-039389fa296e2494.yaml │ ├── deprecate-vmware-store-2f720c6074b843b0.yaml │ ├── drop-py-2-7-345cafc9c1d3f892.yaml │ ├── drop-python-3-6-and-3-7-41af87576c4fd7b1.yaml │ ├── fix-exception-logging-during-attach-9546e24189db83c4.yaml │ ├── fix-interval-in-retries-471155ff34d9f0e9.yaml │ ├── fix-ip-in-connector-info-36b95d9959f10f63.yaml │ ├── fix-legacy-image-update-49a149ec267dccb6.yaml │ ├── fix-rados_connect_timeout-39e5074bc1a3b65b.yaml │ ├── fix-rbd-lockup-3aa2bb86f7d29e19.yaml │ ├── fix-scaleio-download-image-2563cb2681895d0e.yaml │ ├── fix-wait-device-resize-c282940b71a3748e.yaml │ ├── fs-drv-chunk-sz-a1b2f6a72fad92d5.yaml │ ├── handle-sparse-image-a3ecfc4ae1c00d48.yaml │ ├── improved-configuration-options-3635b56aba3072c9.yaml │ ├── lock_path-cef9d6f5f52c3211.yaml │ ├── move-rootwrap-config-f2cf435c548aab5c.yaml │ ├── multi-store-0c004fc8aba2a25d.yaml │ ├── multi-tenant-store-058b67ce5b7f3bd0.yaml │ ├── multiattach-volume-handling-1a8446a64463f2cf.yaml │ ├── multihash-support-629e9cbc283a8b47.yaml │ ├── pike-relnote-9f547df14184d18c.yaml │ ├── prevent-unauthorized-errors-ebb9cf2236595cd0.yaml │ ├── queens-relnote-5fa2d009d9a9e458.yaml │ ├── rbd-trash-snapshots-158a39da4248fb0c.yaml │ ├── release-1.0.0-7ab43e91523eb3c8.yaml │ ├── release-1.0.1-098b1487ac8cc9a1.yaml │ ├── release-1.2.0-8d239f01cd8ff0bf.yaml │ ├── releasenote-0.17.0-efee3f557ea2096a.yaml │ ├── remove-cinder-experimental-fbf9dea32c84dc9b.yaml │ ├── remove-gridfs-driver-09286e27613b4353.yaml │ ├── remove-py38-ad3b92513d4381e3.yaml │ ├── remove-s3-driver-f432afa1f53ecdf8.yaml │ ├── remove-store-cap-update-min-interval-21fea4173ed4a09b.yaml │ ├── rethinking-filesystem-access-5ab872fd0c0d27db.yaml │ ├── rocky-bugfixes-adefa8f47db16a2d.yaml │ ├── set-documented-default-directory-for-filesystem-9b417a29416d3a94.yaml │ ├── sorted-drivers-for-configs-a905f07d3bf9c973.yaml │ ├── start-using-reno-73ef709807e37b74.yaml │ ├── support-cinder-multiple-stores-6cc8489f8f4f8ff3.yaml │ ├── support-cinder-upload-c85849d9c88bbd7e.yaml │ ├── support-cinder-user-domain-420c76053dd50534.yaml │ ├── support-s3-driver-a4158f9fa35931d5.yaml │ ├── update-stein-deprecations-3c2f6ffeab22b558.yaml │ ├── victoria-milestone-1-c1f9de5b90e8c326.yaml │ ├── vmware-store-requests-369485d2cfdb6175.yaml │ ├── volume-type-validation-check-011a400d7fb3b307.yaml │ ├── wallaby-final-release-00f0f851ff7d93ab.yaml │ └── xena-final-release-3c6e19dfba43b40d.yaml └── source │ ├── 2023.1.rst │ ├── 2023.2.rst │ ├── 2024.1.rst │ ├── 2024.2.rst │ ├── 2025.1.rst │ ├── _static │ └── .placeholder │ ├── _templates │ └── .placeholder │ ├── conf.py │ ├── index.rst │ ├── liberty.rst │ ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ └── releasenotes.po │ ├── en_GB │ │ └── LC_MESSAGES │ │ │ └── releasenotes.po │ └── zh_CN │ │ └── LC_MESSAGES │ │ └── releasenotes.po │ ├── mitaka.rst │ ├── newton.rst │ ├── ocata.rst │ ├── pike.rst │ ├── queens.rst │ ├── rocky.rst │ ├── stein.rst │ ├── train.rst │ ├── unreleased.rst │ ├── ussuri.rst │ ├── victoria.rst │ ├── wallaby.rst │ ├── xena.rst │ ├── yoga.rst │ └── zed.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt ├── tools └── with_venv.sh └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | *.dat 4 | TAGS 5 | *.egg-info 6 | *.egg 7 | .eggs/* 8 | build 9 | .coverage 10 | .tox 11 | .stestr 12 | cover 13 | venv 14 | .venv 15 | output.xml 16 | *.sublime-workspace 17 | *.sqlite 18 | *.html 19 | .*.swp 20 | .DS_Store 21 | .testrepository 22 | versioninfo 23 | var/* 24 | ChangeLog 25 | AUTHORS 26 | subunit.log 27 | covhtml/ 28 | doc/source/reference/api 29 | 30 | # Files created by releasenotes build 31 | releasenotes/build 32 | 33 | #Files created by functional tests 34 | functional_testing.conf 35 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/glance_store.git 5 | -------------------------------------------------------------------------------- /.stestr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_path=./glance_store/tests/unit 3 | top_dir=./ 4 | -------------------------------------------------------------------------------- /.zuul.yaml: -------------------------------------------------------------------------------- 1 | - job: 2 | name: glance_store-dsvm-functional-base 3 | parent: devstack-tox-functional 4 | description: | 5 | Base job for devstack-based functional tests for glance_store 6 | 7 | Can only be used directly if a 'functional' testenv is defined 8 | in tox.ini (which currently is not the case). 9 | required-projects: 10 | - openstack/glance_store 11 | timeout: 4200 12 | vars: 13 | devstack_localrc: 14 | LIBS_FROM_GIT: glance_store 15 | # Hardcode glance_store path so the job can be run on glance patches 16 | zuul_work_dir: src/opendev.org/openstack/glance_store 17 | 18 | - job: 19 | name: glance_store-dsvm-functional-filesystem 20 | parent: glance_store-dsvm-functional-base 21 | vars: 22 | tox_envlist: functional-filesystem 23 | 24 | - job: 25 | name: glance_store-dsvm-functional-swift 26 | parent: glance_store-dsvm-functional-base 27 | required-projects: 28 | - openstack/swift 29 | vars: 30 | tox_envlist: functional-swift 31 | devstack_services: 32 | s-account: true 33 | s-container: true 34 | s-object: true 35 | s-proxy: true 36 | 37 | - job: 38 | name: glance_store-tox-cinder-tips-base 39 | parent: tox 40 | abstract: true 41 | description: Abstract job for glance_store vs. cinder 42 | nodeset: ubuntu-jammy 43 | required-projects: 44 | - name: openstack/os-brick 45 | - name: openstack/python-cinderclient 46 | 47 | - job: 48 | name: glance_store-tox-py3-cinder-tips 49 | parent: glance_store-tox-cinder-tips-base 50 | description: | 51 | glance_store py3 unit tests vs. cinder masters 52 | vars: 53 | tox_envlist: py3 54 | 55 | - job: 56 | name: glance_store-tox-keystone-tips-base 57 | parent: tox 58 | abstract: true 59 | description: Abstract job for glance_store vs. keystone 60 | nodeset: ubuntu-jammy 61 | required-projects: 62 | - name: openstack/keystoneauth 63 | - name: openstack/python-keystoneclient 64 | 65 | - job: 66 | name: glance_store-tox-py3-keystone-tips 67 | parent: glance_store-tox-keystone-tips-base 68 | description: | 69 | glance_store py3 unit tests vs. keystone masters 70 | vars: 71 | tox_envlist: py3 72 | 73 | - job: 74 | name: glance_store-tox-oslo-tips-base 75 | parent: tox 76 | abstract: true 77 | description: Abstract job for glance_store vs. oslo 78 | nodeset: ubuntu-jammy 79 | required-projects: 80 | - name: openstack/oslo.concurrency 81 | - name: openstack/oslo.config 82 | - name: openstack/oslo.i18n 83 | - name: openstack/oslo.privsep 84 | - name: openstack/oslo.rootwrap 85 | - name: openstack/oslo.serialization 86 | - name: openstack/oslo.utils 87 | - name: openstack/oslo.vmware 88 | - name: openstack/stevedore 89 | 90 | - job: 91 | name: glance_store-tox-py3-oslo-tips 92 | parent: glance_store-tox-oslo-tips-base 93 | description: | 94 | glance_store py3 unit tests vs. oslo masters 95 | vars: 96 | tox_envlist: py3 97 | 98 | - job: 99 | name: glance_store-tox-swift-tips-base 100 | parent: tox 101 | abstract: true 102 | description: Abstract job for glance_store vs. swift 103 | nodeset: ubuntu-jammy 104 | required-projects: 105 | - name: openstack/python-swiftclient 106 | 107 | - job: 108 | name: glance_store-tox-py3-swift-tips 109 | parent: glance_store-tox-swift-tips-base 110 | description: | 111 | glance_store py3 unit tests vs. swift masters 112 | vars: 113 | tox_envlist: py3 114 | 115 | - job: 116 | name: glance_store-src-ceph-tempest 117 | parent: devstack-plugin-ceph-tempest-py3 118 | description: | 119 | Runs tempest tests with the latest glance_store and the Ceph backend 120 | Former names for this job were: 121 | * legacy-tempest-dsvm-full-ceph-plugin-src-glance_store 122 | required-projects: 123 | - opendev.org/openstack/glance_store 124 | timeout: 10800 125 | vars: 126 | tempest_test_regex: (^tempest\.(api|scenario)|(^cinder_tempest_plugin)) 127 | devstack_local_conf: 128 | post-config: 129 | $GLANCE_API_CONF: 130 | DEFAULT: 131 | do_secure_hash: False 132 | 133 | - job: 134 | name: cross-glance-tox-functional 135 | parent: openstack-tox 136 | description: | 137 | Run cross-project glance functional tests on glance_store. 138 | vars: 139 | zuul_work_dir: src/opendev.org/openstack/glance 140 | tox_envlist: functional 141 | required-projects: 142 | - openstack/glance 143 | - openstack/glance_store 144 | 145 | - project: 146 | templates: 147 | - check-requirements 148 | - lib-forward-testing-python3 149 | - openstack-python3-jobs 150 | - publish-openstack-docs-pti 151 | - release-notes-jobs-python3 152 | check: 153 | jobs: 154 | - cross-glance-tox-functional 155 | - glance_store-src-ceph-tempest: 156 | irrelevant-files: &tempest-irrelevant-files 157 | - ^doc/.*$ 158 | - ^releasenotes/.*$ 159 | - ^.*\.rst$ 160 | - ^(test-|)requirements.txt$ 161 | - ^setup.cfg$ 162 | - ^tox.ini$ 163 | experimental: 164 | jobs: 165 | - glance_store-dsvm-functional-filesystem 166 | - glance_store-dsvm-functional-swift 167 | periodic: 168 | jobs: 169 | # NOTE(rosmaita): we only want the "tips" jobs to be run against 170 | # master, hence the 'branches' qualifiers below. Without them, when 171 | # a stable branch is cut, the tests would be run against the stable 172 | # branch as well, which is pointless because these libraries are 173 | # frozen (more or less) in the stable branches. 174 | # 175 | # The "tips" jobs can be removed from the stable branch .zuul.yaml 176 | # files if someone is so inclined, but that would require manual 177 | # maintenance, so we do not do it by default. Another option is 178 | # to define these jobs in the openstack/project-config repo. 179 | # That would make us less agile in adjusting these tests, so we 180 | # aren't doing that either. 181 | - glance_store-tox-py3-cinder-tips: 182 | branches: master 183 | - glance_store-tox-py3-keystone-tips: 184 | branches: master 185 | - glance_store-tox-py3-oslo-tips: 186 | branches: master 187 | - glance_store-tox-py3-swift-tips: 188 | branches: master 189 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Team and repository tags 3 | ======================== 4 | 5 | .. image:: https://governance.openstack.org/tc/badges/glance_store.svg 6 | :target: https://governance.openstack.org/tc/reference/tags/index.html 7 | :alt: The following tags have been asserted for the Glance Store 8 | Library: 9 | "project:official", 10 | "stable:follows-policy", 11 | "vulnerability:managed". 12 | Follow the link for an explanation of these tags. 13 | .. NOTE(rosmaita): the alt text above will have to be updated when 14 | additional tags are asserted for glance_store. (The SVG in the 15 | governance repo is updated automatically.) 16 | 17 | .. Change things from this point on 18 | 19 | Glance Store Library 20 | ==================== 21 | 22 | Glance's stores library 23 | 24 | This library has been extracted from the Glance source code for the 25 | specific use of the Glance and Glare projects. 26 | 27 | The API it exposes is not stable, has some shortcomings, and is not a 28 | general purpose interface. We would eventually like to change this, 29 | but for now using this library outside of Glance or Glare will not be 30 | supported by the core team. 31 | 32 | * License: Apache License, Version 2.0 33 | * Documentation: https://docs.openstack.org/glance_store/latest/ 34 | * Source: https://opendev.org/openstack/glance_store/ 35 | * Bugs: https://bugs.launchpad.net/glance-store 36 | * Release notes: https://docs.openstack.org/releasenotes/glance_store/index.html 37 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | # The order of packages is significant, because pip processes them in the order 2 | # of appearance. Changing the order has an impact on the overall integration 3 | # process, which may cause wedges in the gate later. 4 | sphinx>=2.0.0,!=2.1.0 # BSD 5 | openstackdocstheme>=2.2.1 # Apache-2.0 6 | reno>=3.1.0 # Apache-2.0 7 | sphinxcontrib-apidoc>=0.2.0 # BSD 8 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import os 17 | import sys 18 | 19 | sys.path.insert(0, os.path.abspath('../..')) 20 | # -- General configuration ---------------------------------------------------- 21 | 22 | # Add any Sphinx extension module names here, as strings. They can be 23 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 24 | extensions = ['openstackdocstheme', 25 | 'sphinxcontrib.apidoc'] 26 | 27 | # openstackdocstheme options 28 | openstackdocs_repo_name = 'openstack/glance_store' 29 | openstackdocs_auto_name = False 30 | openstackdocs_bug_project = 'glance-store' 31 | openstackdocs_bug_tag = '' 32 | 33 | # sphinxcontrib.apidoc options 34 | apidoc_module_dir = '../../glance_store' 35 | apidoc_output_dir = 'reference/api' 36 | apidoc_excluded_paths = [ 37 | 'test', 38 | 'tests/*'] 39 | apidoc_separate_modules = True 40 | 41 | # autodoc generation is a bit aggressive and a nuisance when doing heavy 42 | # text edit cycles. 43 | # execute "export SPHINX_DEBUG=1" in your terminal to disable 44 | 45 | # Add any paths that contain templates here, relative to this directory. 46 | # templates_path = [] 47 | 48 | # The suffix of source filenames. 49 | source_suffix = '.rst' 50 | 51 | # The master toctree document. 52 | master_doc = 'index' 53 | 54 | # General information about the project. 55 | project = 'glance_store' 56 | copyright = '2014, OpenStack Foundation' 57 | 58 | # If true, '()' will be appended to :func: etc. cross-reference text. 59 | add_function_parentheses = True 60 | 61 | # If true, the current module name will be prepended to all description 62 | # unit titles (such as .. function::). 63 | add_module_names = True 64 | 65 | # The name of the Pygments (syntax highlighting) style to use. 66 | pygments_style = 'native' 67 | 68 | # -- Options for HTML output -------------------------------------------------- 69 | 70 | # The theme to use for HTML and HTML Help pages. Major themes that come with 71 | # Sphinx are currently 'default' and 'sphinxdoc'. 72 | # html_theme_path = ["."] 73 | # html_theme = '_theme' 74 | # html_static_path = ['static'] 75 | html_theme = 'openstackdocs' 76 | 77 | # Output file base name for HTML help builder. 78 | htmlhelp_basename = '%sdoc' % project 79 | 80 | modindex_common_prefix = ['glance_store.'] 81 | 82 | # Grouping the document tree into LaTeX files. List of tuples 83 | # (source start file, target name, title, author, documentclass 84 | # [howto/manual]). 85 | latex_documents = [ 86 | ('index', 87 | '%s.tex' % project, 88 | '%s Documentation' % project, 89 | 'OpenStack Foundation', 'manual'), 90 | ] 91 | 92 | # The autodoc module imports every module to check for import 93 | # errors. Since the fs_mount module is self initializing, it 94 | # requires configurations that aren't loaded till that time. 95 | # It would never happen in a real scenario as it is only imported 96 | # from cinder store after the config are loaded but to handle doc 97 | # failures, we mock it here. 98 | # The cinder_utils module imports external dependencies like 99 | # cinderclient, retrying etc which are not recognized by 100 | # autodoc, hence, are mocked here. These dependencies are installed 101 | # during an actual deployment and won't cause any issue during usage. 102 | autodoc_mock_imports = ['glance_store.common.fs_mount', 103 | 'glance_store.common.cinder_utils'] 104 | 105 | # Since version 4.2.0, Sphinx emits a warning when encountering a mocked 106 | # object, leading to the following error: 107 | # "A mocked object is detected: 'glance_store.common.cinder_utils'" 108 | # To prevent this, we disable all warnings from the autodoc extension, since 109 | # there is no finer grain yet. 110 | suppress_warnings = ['autodoc.*'] 111 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | glance_store 3 | ============== 4 | 5 | The glance_store library supports the creation, deletion and gather of data 6 | assets from/to a set of several, different, storage technologies. 7 | 8 | .. warning:: 9 | This library has been extracted from the Glance source code for the 10 | specific use of the Glance and Glare projects. 11 | 12 | The API it exposes is not stable, has some shortcomings, and is not a 13 | general purpose interface. We would eventually like to change this, but for 14 | now using this library outside of Glance or Glare will not be supported by 15 | the core team. 16 | 17 | .. toctree:: 18 | :maxdepth: 1 19 | 20 | user/index 21 | reference/index 22 | 23 | .. rubric:: Indices and tables 24 | 25 | * :ref:`genindex` 26 | * :ref:`modindex` 27 | * :ref:`search` 28 | -------------------------------------------------------------------------------- /doc/source/reference/index.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | glance-store Reference Guide 3 | ============================== 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | api/modules 9 | -------------------------------------------------------------------------------- /doc/source/user/drivers.rst: -------------------------------------------------------------------------------- 1 | 2 | Glance Store Drivers 3 | ==================== 4 | 5 | Glance store supports several different drivers. These drivers live 6 | within the library's code base and they are maintained by either 7 | members of the Glance community or OpenStack in general. Please, find 8 | below the table of supported drivers and maintainers: 9 | 10 | .. list-table:: 11 | :header-rows: 1 12 | 13 | * - Driver 14 | - Status 15 | - Maintainer 16 | - Email 17 | - IRC Nick 18 | * - File System 19 | - Supported 20 | - Glance Team 21 | - openstack-discuss@lists.openstack.org 22 | - openstack-glance 23 | * - HTTP 24 | - Supported 25 | - Glance Team 26 | - openstack-discuss@lists.openstack.org 27 | - openstack-glance 28 | * - RBD 29 | - Supported 30 | - Glance Team 31 | - openstack-discuss@lists.openstack.org 32 | - openstack-glance 33 | * - Cinder 34 | - Supported 35 | - Rajat Dhasmana 36 | - rajatdhasmana@gmail.com 37 | - whoami-rajat 38 | * - Swift 39 | - Supported 40 | - Matthew Oliver 41 | - matt@oliver.net.au 42 | - mattoliverau 43 | * - VMware 44 | - Deprecated 45 | - N/A 46 | - N/A 47 | - 48 | * - S3 49 | - Supported 50 | - Naohiro Sameshima 51 | - naohiro.sameshima@global.ntt 52 | - nao-shark 53 | 54 | .. note:: 55 | VMWare driver was deprecated in 2024.1 release, because of lack of CI and 56 | active maintainers 57 | -------------------------------------------------------------------------------- /doc/source/user/index.rst: -------------------------------------------------------------------------------- 1 | ================================= 2 | glance-store User Documentation 3 | ================================= 4 | 5 | .. toctree:: 6 | 7 | drivers 8 | -------------------------------------------------------------------------------- /etc/glance/rootwrap.conf: -------------------------------------------------------------------------------- 1 | # Configuration for glance-rootwrap 2 | # This file should be owned by (and only-writable by) the root user 3 | 4 | [DEFAULT] 5 | # List of directories to load filter definitions from (separated by ','). 6 | # These directories MUST all be only writeable by root ! 7 | filters_path=/etc/glance/rootwrap.d,/usr/share/glance/rootwrap 8 | 9 | # List of directories to search executables in, in case filters do not 10 | # explicitely specify a full path (separated by ',') 11 | # If not specified, defaults to system PATH environment variable. 12 | # These directories MUST all be only writeable by root ! 13 | exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin,/usr/local/bin,/usr/local/sbin 14 | 15 | # Enable logging to syslog 16 | # Default value is False 17 | use_syslog=False 18 | 19 | # Which syslog facility to use. 20 | # Valid values include auth, authpriv, syslog, local0, local1... 21 | # Default value is 'syslog' 22 | syslog_log_facility=syslog 23 | 24 | # Which messages to log. 25 | # INFO means log all usage 26 | # ERROR means only log unsuccessful attempts 27 | syslog_log_level=ERROR 28 | -------------------------------------------------------------------------------- /etc/glance/rootwrap.d/glance_cinder_store.filters: -------------------------------------------------------------------------------- 1 | # glance-rootwrap command filters for glance cinder store 2 | # This file should be owned by (and only-writable by) the root user 3 | 4 | [Filters] 5 | # cinder store driver 6 | disk_chown: RegExpFilter, chown, root, chown, \d+, /dev/(?!.*/\.\.).* 7 | 8 | # os-brick library commands 9 | # os_brick.privileged.run_as_root oslo.privsep context 10 | # This line ties the superuser privs with the config files, context name, 11 | # and (implicitly) the actual python code invoked. 12 | privsep-rootwrap: RegExpFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, os_brick.privileged.default, --privsep_sock_path, /tmp/.* 13 | 14 | chown: CommandFilter, chown, root 15 | mount: CommandFilter, mount, root 16 | umount: CommandFilter, umount, root -------------------------------------------------------------------------------- /glance_store/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from .multi_backend import * # noqa 17 | from .backend import * # noqa 18 | from .driver import * # noqa 19 | from .exceptions import * # noqa 20 | -------------------------------------------------------------------------------- /glance_store/_drivers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/glance_store/_drivers/__init__.py -------------------------------------------------------------------------------- /glance_store/_drivers/cinder/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from glance_store._drivers.cinder.store import * # noqa -------------------------------------------------------------------------------- /glance_store/_drivers/cinder/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from oslo_utils import importutils 17 | 18 | from os_brick.initiator import connector 19 | 20 | NFS = 'nfs' 21 | SCALEIO = "scaleio" 22 | 23 | BASE = 'glance_store._drivers.cinder.base.BaseBrickConnectorInterface' 24 | 25 | _connector_mapping = { 26 | NFS: 'glance_store._drivers.cinder.nfs.NfsBrickConnector', 27 | SCALEIO: 'glance_store._drivers.cinder.scaleio.ScaleIOBrickConnector', 28 | } 29 | 30 | 31 | def factory(*args, **kwargs): 32 | connection_info = kwargs.get('connection_info') 33 | protocol = connection_info['driver_volume_type'] 34 | connector = _connector_mapping.get(protocol, BASE) 35 | conn_cls = importutils.import_class(connector) 36 | return conn_cls(*args, **kwargs) 37 | 38 | 39 | class BaseBrickConnectorInterface(object): 40 | def __init__(self, *args, **kwargs): 41 | self.connection_info = kwargs.get('connection_info') 42 | self.root_helper = kwargs.get('root_helper') 43 | self.use_multipath = kwargs.get('use_multipath') 44 | self.conn = connector.InitiatorConnector.factory( 45 | self.connection_info['driver_volume_type'], self.root_helper, 46 | conn=self.connection_info, use_multipath=self.use_multipath) 47 | 48 | def connect_volume(self, volume): 49 | device = self.conn.connect_volume(self.connection_info) 50 | return device 51 | 52 | def disconnect_volume(self, device): 53 | # Bug #2004555: use force so there aren't any leftovers 54 | self.conn.disconnect_volume(self.connection_info, device, force=True) 55 | 56 | def extend_volume(self): 57 | self.conn.extend_volume(self.connection_info) 58 | 59 | def yield_path(self, volume, volume_path): 60 | """ 61 | This method returns the volume file path. 62 | 63 | The reason for it's implementation is to fix Bug#2000584. More 64 | information is added in the ScaleIO connector which makes actual 65 | use of it's implementation. 66 | """ 67 | return volume_path 68 | -------------------------------------------------------------------------------- /glance_store/_drivers/cinder/nfs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import hashlib 17 | import logging 18 | import os 19 | import socket 20 | 21 | from oslo_config import cfg 22 | 23 | from glance_store._drivers.cinder import base 24 | from glance_store.common import cinder_utils 25 | from glance_store.common import fs_mount as mount 26 | from glance_store.common import utils 27 | from glance_store import exceptions 28 | from glance_store.i18n import _ 29 | 30 | CONF = cfg.CONF 31 | LOG = logging.getLogger(__name__) 32 | 33 | 34 | class NfsBrickConnector(base.BaseBrickConnectorInterface): 35 | 36 | def __init__(self, *args, **kwargs): 37 | self.volume = kwargs.get('volume') 38 | self.connection_info = kwargs.get('connection_info') 39 | self.root_helper = kwargs.get('root_helper') 40 | self.mount_point_base = kwargs.get('mountpoint_base') 41 | self.attachment_obj = kwargs.get('attachment_obj') 42 | self.client = kwargs.get('client') 43 | self.host = socket.gethostname() 44 | self.volume_api = cinder_utils.API() 45 | 46 | def _get_mount_path(self, share, mount_point_base): 47 | """Returns the mount path prefix using the mount point base and share. 48 | 49 | :returns: The mount path prefix. 50 | """ 51 | return os.path.join(self.mount_point_base, 52 | NfsBrickConnector.get_hash_str(share)) 53 | 54 | @staticmethod 55 | def get_hash_str(base_str): 56 | """Returns string representing SHA256 hash of base_str in hex format. 57 | 58 | If base_str is a Unicode string, encode it to UTF-8. 59 | """ 60 | if isinstance(base_str, str): 61 | base_str = base_str.encode('utf-8') 62 | return hashlib.sha256(base_str).hexdigest() 63 | 64 | def connect_volume(self, volume): 65 | # The format info of nfs volumes is exposed via attachment_get 66 | # API hence it is not available in the connection info of 67 | # attachment object received from attachment_update and we 68 | # need to do this call 69 | vol_attachment = self.volume_api.attachment_get( 70 | self.client, self.attachment_obj.id) 71 | if (volume.encrypted or 72 | vol_attachment.connection_info['format'] == 'qcow2'): 73 | issue_type = 'Encrypted' if volume.encrypted else 'qcow2' 74 | msg = (_('%(issue_type)s volume creation for cinder nfs ' 75 | 'is not supported from glance_store. Failed to ' 76 | 'create volume %(volume_id)s') 77 | % {'issue_type': issue_type, 78 | 'volume_id': volume.id}) 79 | LOG.error(msg) 80 | raise exceptions.BackendException(msg) 81 | 82 | @utils.synchronized(self.connection_info['export']) 83 | def connect_volume_nfs(): 84 | export = self.connection_info['export'] 85 | vol_name = self.connection_info['name'] 86 | mountpoint = self._get_mount_path( 87 | export, os.path.join(self.mount_point_base, 'nfs')) 88 | options = self.connection_info['options'] 89 | mount.mount( 90 | 'nfs', export, vol_name, mountpoint, self.host, 91 | self.root_helper, options) 92 | return {'path': os.path.join(mountpoint, vol_name)} 93 | 94 | device = connect_volume_nfs() 95 | return device 96 | 97 | def disconnect_volume(self, device): 98 | @utils.synchronized(self.connection_info['export']) 99 | def disconnect_volume_nfs(): 100 | path, vol_name = device['path'].rsplit('/', 1) 101 | mount.umount(vol_name, path, self.host, 102 | self.root_helper) 103 | disconnect_volume_nfs() 104 | 105 | def extend_volume(self): 106 | raise NotImplementedError 107 | -------------------------------------------------------------------------------- /glance_store/_drivers/cinder/scaleio.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import logging 17 | import math 18 | import os 19 | import time 20 | 21 | from oslo_config import cfg 22 | from oslo_utils import units 23 | 24 | from glance_store._drivers.cinder import base 25 | from glance_store import exceptions 26 | from glance_store.i18n import _ 27 | 28 | CONF = cfg.CONF 29 | LOG = logging.getLogger(__name__) 30 | 31 | 32 | class ScaleIOBrickConnector(base.BaseBrickConnectorInterface): 33 | 34 | @staticmethod 35 | def _get_device_size(device_file): 36 | # os.fstat doesn't work with the block devices exported by 37 | # the ScaleIO/PowerFlex protocol and returns 0 as st_size 38 | # so we use the seek/tell logic. 39 | # Get the current position 40 | current_pos = device_file.tell() 41 | try: 42 | # Seek to the end of the file 43 | device_file.seek(0, os.SEEK_END) 44 | # Get the size of file (in bytes) 45 | device_size = device_file.tell() 46 | # Convert the bytes size into GB 47 | device_size = int(math.ceil(float(device_size) / units.Gi)) 48 | finally: 49 | # Restore the file pointer to original position 50 | device_file.seek(current_pos, os.SEEK_SET) 51 | return device_size 52 | 53 | @staticmethod 54 | def _wait_resize_device(volume, device_file): 55 | timeout = 20 56 | max_recheck_wait = 10 57 | tries = 0 58 | elapsed = 0 59 | while ScaleIOBrickConnector._get_device_size( 60 | device_file) < volume.size: 61 | wait = min(0.5 * 2 ** tries, max_recheck_wait) 62 | time.sleep(wait) 63 | tries += 1 64 | elapsed += wait 65 | if elapsed >= timeout: 66 | msg = (_('Timeout while waiting while volume %(volume_id)s ' 67 | 'to resize the device in %(tries)s tries.') 68 | % {'volume_id': volume.id, 'tries': tries}) 69 | LOG.error(msg) 70 | raise exceptions.BackendException(msg) 71 | 72 | def yield_path(self, volume, volume_path): 73 | """ 74 | This method waits for the LUN size to match the volume size. 75 | 76 | This method is created to fix Bug#2000584 where NFS sparse volumes 77 | timeout waiting for the file size to match the volume.size field. 78 | The reason is that the volume is sparse and only takes up space of 79 | data which is written to it (similar to thin provisioned volumes). 80 | """ 81 | # Sometimes the extended LUN on storage side takes time 82 | # to reflect in the device so we wait until the device 83 | # size is equal to the extended volume size. 84 | ScaleIOBrickConnector._wait_resize_device(volume, volume_path) 85 | return volume_path 86 | -------------------------------------------------------------------------------- /glance_store/_drivers/swift/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from glance_store._drivers.swift import utils # noqa 17 | from glance_store._drivers.swift.store import * # noqa 18 | -------------------------------------------------------------------------------- /glance_store/_drivers/swift/buffered.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import logging 14 | import socket 15 | import tempfile 16 | 17 | from oslo_config import cfg 18 | from oslo_utils import encodeutils 19 | 20 | from glance_store import exceptions 21 | from glance_store.i18n import _ 22 | 23 | LOG = logging.getLogger(__name__) 24 | READ_SIZE = 65536 25 | 26 | BUFFERING_OPTS = [ 27 | cfg.StrOpt('swift_upload_buffer_dir', 28 | help=""" 29 | Directory to buffer image segments before upload to Swift. 30 | 31 | Provide a string value representing the absolute path to the 32 | directory on the glance node where image segments will be 33 | buffered briefly before they are uploaded to swift. 34 | 35 | NOTES: 36 | * This is required only when the configuration option 37 | ``swift_buffer_on_upload`` is set to True. 38 | * This directory should be provisioned keeping in mind the 39 | ``swift_store_large_object_chunk_size`` and the maximum 40 | number of images that could be uploaded simultaneously by 41 | a given glance node. 42 | 43 | Possible values: 44 | * String value representing an absolute directory path 45 | 46 | Related options: 47 | * swift_buffer_on_upload 48 | * swift_store_large_object_chunk_size 49 | 50 | """), 51 | ] 52 | CONF = cfg.CONF 53 | 54 | 55 | def validate_buffering(buffer_dir): 56 | if buffer_dir is None: 57 | msg = _('Configuration option "swift_upload_buffer_dir" is ' 58 | 'not set. Please set it to a valid path to buffer ' 59 | 'during Swift uploads.') 60 | raise exceptions.BadStoreConfiguration(store_name='swift', 61 | reason=msg) 62 | 63 | # NOTE(dharinic): Ensure that the provided directory path for 64 | # buffering is valid 65 | try: 66 | _tmpfile = tempfile.TemporaryFile(dir=buffer_dir) 67 | except OSError as err: 68 | msg = (_('Unable to use buffer directory set with ' 69 | '"swift_upload_buffer_dir". Error: %s') % 70 | encodeutils.exception_to_unicode(err)) 71 | raise exceptions.BadStoreConfiguration(store_name='swift', 72 | reason=msg) 73 | else: 74 | _tmpfile.close() 75 | return True 76 | 77 | 78 | class BufferedReader(object): 79 | """Buffer a chunk (segment) worth of data to disk before sending it swift. 80 | This creates the ability to back the input stream up and re-try put object 81 | requests. (Swiftclient will try to reset the file pointer on any upload 82 | failure if seek and tell methods are provided on the input file.) 83 | 84 | Chunks are temporarily buffered to disk. Disk space consumed will be 85 | roughly (segment size * number of in-flight upload requests). 86 | 87 | There exists a possibility where the disk space consumed for buffering MAY 88 | eat into the disk space available for glance cache. This may affect image 89 | download performance. So, extra care should be taken while deploying this 90 | to ensure there is enough disk space available. 91 | """ 92 | 93 | def __init__(self, fd, checksum, os_hash_value, total, verifier=None, 94 | backend_group=None): 95 | self.fd = fd 96 | self.total = total 97 | self.checksum = checksum 98 | self.os_hash_value = os_hash_value 99 | self.verifier = verifier 100 | self.backend_group = backend_group 101 | # maintain a pointer to use to update checksum and verifier 102 | self.update_position = 0 103 | 104 | if self.backend_group: 105 | buffer_dir = getattr(CONF, 106 | self.backend_group).swift_upload_buffer_dir 107 | else: 108 | buffer_dir = CONF.glance_store.swift_upload_buffer_dir 109 | 110 | self._tmpfile = tempfile.TemporaryFile(dir=buffer_dir) 111 | 112 | self._buffered = False 113 | self.is_zero_size = False 114 | self._buffer() 115 | # Setting the file pointer back to the beginning of file 116 | self._tmpfile.seek(0) 117 | 118 | def read(self, size): 119 | """Read up to a chunk's worth of data from the input stream into a 120 | file buffer. Then return data out of that buffer. 121 | """ 122 | remaining = self.total - self._tmpfile.tell() 123 | read_size = min(remaining, size) 124 | # read out of the buffered chunk 125 | result = self._tmpfile.read(read_size) 126 | # update the checksum and verifier with only the bytes 127 | # they have not seen 128 | update = self.update_position - self._tmpfile.tell() 129 | if update < 0: 130 | self.checksum.update(result[update:]) 131 | self.os_hash_value.update(result[update:]) 132 | if self.verifier: 133 | self.verifier.update(result[update:]) 134 | self.update_position += abs(update) 135 | return result 136 | 137 | def _buffer(self): 138 | to_buffer = self.total 139 | LOG.debug("Buffering %s bytes of image segment" % to_buffer) 140 | buffer_read_count = 0 141 | while not self._buffered: 142 | read_size = min(to_buffer, READ_SIZE) 143 | try: 144 | buf = self.fd.read(read_size) 145 | except IOError as e: 146 | # We actually don't know what exactly self.fd is. And as a 147 | # result we don't know which exception it may raise. To pass 148 | # the retry mechanism inside swift client we must limit the 149 | # possible set of errors. 150 | raise socket.error(*e.args) 151 | if len(buf) == 0: 152 | if self._tmpfile.tell() == 0: 153 | self.is_zero_size = True 154 | self._tmpfile.seek(0) 155 | self._buffered = True 156 | break 157 | self._tmpfile.write(buf) 158 | to_buffer -= len(buf) 159 | buffer_read_count = buffer_read_count + 1 160 | if buffer_read_count == 0: 161 | self.is_zero_size = True 162 | 163 | # NOTE(belliott) seek and tell get used by python-swiftclient to "reset" 164 | # if there is a put_object error 165 | def seek(self, offset): 166 | LOG.debug("Seek from %s to %s" % (self._tmpfile.tell(), offset)) 167 | self._tmpfile.seek(offset) 168 | 169 | def tell(self): 170 | return self._tmpfile.tell() 171 | 172 | @property 173 | def bytes_read(self): 174 | return self.tell() 175 | 176 | def __enter__(self): 177 | self._tmpfile.__enter__() 178 | return self 179 | 180 | def __exit__(self, type, value, traceback): 181 | # close and delete the temporary file used to buffer data 182 | self._tmpfile.__exit__(type, value, traceback) 183 | -------------------------------------------------------------------------------- /glance_store/capabilities.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 IBM, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Glance Store capability""" 17 | 18 | import logging 19 | import threading 20 | 21 | import enum 22 | from oslo_utils import reflection 23 | 24 | from glance_store import exceptions 25 | from glance_store.i18n import _LW 26 | 27 | _STORE_CAPABILITES_UPDATE_SCHEDULING_BOOK = {} 28 | _STORE_CAPABILITES_UPDATE_SCHEDULING_LOCK = threading.Lock() 29 | LOG = logging.getLogger(__name__) 30 | 31 | 32 | class BitMasks(enum.IntEnum): 33 | NONE = 0b00000000 34 | ALL = 0b11111111 35 | READ_ACCESS = 0b00000001 36 | # Included READ_ACCESS 37 | READ_OFFSET = 0b00000011 38 | # Included READ_ACCESS 39 | READ_CHUNK = 0b00000101 40 | # READ_OFFSET | READ_CHUNK 41 | READ_RANDOM = 0b00000111 42 | WRITE_ACCESS = 0b00001000 43 | # Included WRITE_ACCESS 44 | WRITE_OFFSET = 0b00011000 45 | # Included WRITE_ACCESS 46 | WRITE_CHUNK = 0b00101000 47 | # WRITE_OFFSET | WRITE_CHUNK 48 | WRITE_RANDOM = 0b00111000 49 | # READ_ACCESS | WRITE_ACCESS 50 | RW_ACCESS = 0b00001001 51 | # READ_OFFSET | WRITE_OFFSET 52 | RW_OFFSET = 0b00011011 53 | # READ_CHUNK | WRITE_CHUNK 54 | RW_CHUNK = 0b00101101 55 | # RW_OFFSET | RW_CHUNK 56 | RW_RANDOM = 0b00111111 57 | # driver is stateless and can be reused safely 58 | DRIVER_REUSABLE = 0b01000000 59 | 60 | 61 | class StoreCapability(object): 62 | 63 | def __init__(self): 64 | # Set static store capabilities base on 65 | # current driver implementation. 66 | self._capabilities = getattr(self.__class__, "_CAPABILITIES", 0) 67 | 68 | @property 69 | def capabilities(self): 70 | return self._capabilities 71 | 72 | @staticmethod 73 | def contains(x, y): 74 | return x & y == y 75 | 76 | def update_capabilities(self): 77 | """ 78 | Update dynamic storage capabilities based on current 79 | driver configuration and backend status when needed. 80 | 81 | As a hook, the function will be triggered in two cases: 82 | calling once after store driver get configured, it was 83 | used to update dynamic storage capabilities based on 84 | current driver configuration, or calling when the 85 | capabilities checking of an operation failed every time, 86 | this was used to refresh dynamic storage capabilities 87 | based on backend status then. 88 | 89 | This function shouldn't raise any exception out. 90 | """ 91 | LOG.debug(("Store %s doesn't support updating dynamic " 92 | "storage capabilities. Please overwrite " 93 | "'update_capabilities' method of the store to " 94 | "implement updating logics if needed.") % 95 | reflection.get_class_name(self)) 96 | 97 | def is_capable(self, *capabilities): 98 | """ 99 | Check if requested capability(s) are supported by 100 | current driver instance. 101 | 102 | :param capabilities: required capability(s). 103 | """ 104 | caps = 0 105 | 106 | for cap in capabilities: 107 | caps |= int(cap) 108 | 109 | return self.contains(self.capabilities, caps) 110 | 111 | def set_capabilities(self, *dynamic_capabilites): 112 | """ 113 | Set dynamic storage capabilities based on current 114 | driver configuration and backend status. 115 | 116 | :param dynamic_capabilites: dynamic storage capability(s). 117 | """ 118 | for cap in dynamic_capabilites: 119 | self._capabilities |= int(cap) 120 | 121 | def unset_capabilities(self, *dynamic_capabilites): 122 | """ 123 | Unset dynamic storage capabilities. 124 | 125 | :param dynamic_capabilites: dynamic storage capability(s). 126 | """ 127 | caps = 0 128 | 129 | for cap in dynamic_capabilites: 130 | caps |= int(cap) 131 | 132 | # TODO(zhiyan): Cascaded capability removal is 133 | # skipped currently, we can add it back later 134 | # when a concrete requirement comes out. 135 | # For example, when removing READ_ACCESS, all 136 | # read related capabilities need to be removed 137 | # together, e.g. READ_RANDOM. 138 | 139 | self._capabilities &= ~caps 140 | 141 | 142 | def check(store_op_fun): 143 | 144 | def op_checker(store, *args, **kwargs): 145 | get_capabilities = [ 146 | BitMasks.READ_ACCESS, 147 | BitMasks.READ_OFFSET if kwargs.get('offset') else BitMasks.NONE, 148 | BitMasks.READ_CHUNK if kwargs.get('chunk_size') else BitMasks.NONE 149 | ] 150 | 151 | op_cap_map = { 152 | 'get': get_capabilities, 153 | 'add': [BitMasks.WRITE_ACCESS], 154 | 'delete': [BitMasks.WRITE_ACCESS]} 155 | 156 | op_exec_map = { 157 | 'get': (exceptions.StoreRandomGetNotSupported 158 | if kwargs.get('offset') or kwargs.get('chunk_size') else 159 | exceptions.StoreGetNotSupported), 160 | 'add': exceptions.StoreAddDisabled, 161 | 'delete': exceptions.StoreDeleteNotSupported} 162 | 163 | op = store_op_fun.__name__.lower() 164 | 165 | try: 166 | req_cap = op_cap_map[op] 167 | except KeyError: 168 | LOG.warning(_LW('The capability of operation "%s" ' 169 | 'could not be checked.'), op) 170 | else: 171 | if not store.is_capable(*req_cap): 172 | kwargs.setdefault('offset', 0) 173 | kwargs.setdefault('chunk_size', None) 174 | raise op_exec_map[op](**kwargs) 175 | 176 | return store_op_fun(store, *args, **kwargs) 177 | 178 | return op_checker 179 | -------------------------------------------------------------------------------- /glance_store/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/glance_store/common/__init__.py -------------------------------------------------------------------------------- /glance_store/common/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010 United States Government as represented by the 2 | # Administrator of the National Aeronautics and Space Administration. 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """ 18 | System-level utilities and helper functions. 19 | """ 20 | 21 | import hashlib 22 | import logging 23 | import uuid 24 | 25 | from oslo_concurrency import lockutils 26 | 27 | from time import sleep 28 | 29 | from glance_store.i18n import _ 30 | 31 | 32 | LOG = logging.getLogger(__name__) 33 | 34 | synchronized = lockutils.synchronized_with_prefix('glance_store-') 35 | 36 | 37 | def is_uuid_like(val): 38 | """Returns validation of a value as a UUID. 39 | 40 | For our purposes, a UUID is a canonical form string: 41 | aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 42 | """ 43 | 44 | try: 45 | return str(uuid.UUID(val)) == val 46 | except (TypeError, ValueError, AttributeError): 47 | return False 48 | 49 | 50 | def chunkreadable(iter, chunk_size=65536): 51 | """ 52 | Wrap a readable iterator with a reader yielding chunks of 53 | a preferred size, otherwise leave iterator unchanged. 54 | 55 | :param iter: an iter which may also be readable 56 | :param chunk_size: maximum size of chunk 57 | """ 58 | return chunkiter(iter, chunk_size) if hasattr(iter, 'read') else iter 59 | 60 | 61 | def chunkiter(fp, chunk_size=65536): 62 | """ 63 | Return an iterator to a file-like obj which yields fixed size chunks 64 | 65 | :param fp: a file-like object 66 | :param chunk_size: maximum size of chunk 67 | """ 68 | while True: 69 | chunk = fp.read(chunk_size) 70 | if chunk: 71 | yield chunk 72 | else: 73 | break 74 | 75 | 76 | def cooperative_iter(iter): 77 | """ 78 | Return an iterator which schedules after each 79 | iteration. This can prevent eventlet thread starvation. 80 | 81 | :param iter: an iterator to wrap 82 | """ 83 | try: 84 | for chunk in iter: 85 | sleep(0) 86 | yield chunk 87 | except Exception as err: 88 | msg = _("Error: cooperative_iter exception %s") % err 89 | LOG.error(msg) 90 | raise 91 | 92 | 93 | def cooperative_read(fd): 94 | """ 95 | Wrap a file descriptor's read with a partial function which schedules 96 | after each read. This can prevent eventlet thread starvation. 97 | 98 | :param fd: a file descriptor to wrap 99 | """ 100 | def readfn(*args): 101 | result = fd.read(*args) 102 | sleep(0) 103 | return result 104 | return readfn 105 | 106 | 107 | def get_hasher(hash_algo, usedforsecurity=True): 108 | """ 109 | Returns the required hasher, given the hashing algorithm. 110 | This is primarily to ensure that the hash algorithm is correctly 111 | chosen when executed on a FIPS enabled system 112 | 113 | :param hash_algo: hash algorithm requested 114 | :param usedforsecurity: whether the hashes are used in a security context 115 | """ 116 | if str(hash_algo) == 'md5': 117 | return hashlib.md5(usedforsecurity=usedforsecurity) 118 | else: 119 | return hashlib.new(str(hash_algo)) 120 | 121 | 122 | class CooperativeReader(object): 123 | """ 124 | An eventlet thread friendly class for reading in image data. 125 | 126 | When accessing data either through the iterator or the read method 127 | we perform a sleep to allow a co-operative yield. When there is more than 128 | one image being uploaded/downloaded this prevents eventlet thread 129 | starvation, ie allows all threads to be scheduled periodically rather than 130 | having the same thread be continuously active. 131 | """ 132 | def __init__(self, fd): 133 | """ 134 | :param fd: Underlying image file object 135 | """ 136 | self.fd = fd 137 | self.iterator = None 138 | # NOTE(markwash): if the underlying supports read(), overwrite the 139 | # default iterator-based implementation with cooperative_read which 140 | # is more straightforward 141 | if hasattr(fd, 'read'): 142 | self.read = cooperative_read(fd) 143 | 144 | def read(self, length=None): 145 | """Return the next chunk of the underlying iterator. 146 | 147 | This is replaced with cooperative_read in __init__ if the underlying 148 | fd already supports read(). 149 | """ 150 | if self.iterator is None: 151 | self.iterator = self.__iter__() 152 | try: 153 | return next(self.iterator) 154 | except StopIteration: 155 | return b'' 156 | 157 | def __iter__(self): 158 | return cooperative_iter(self.fd.__iter__()) 159 | -------------------------------------------------------------------------------- /glance_store/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Glance Store exception subclasses""" 17 | 18 | import urllib.parse 19 | 20 | from glance_store.i18n import _ 21 | 22 | 23 | class BackendException(Exception): 24 | pass 25 | 26 | 27 | class UnsupportedBackend(BackendException): 28 | pass 29 | 30 | 31 | class RedirectException(Exception): 32 | def __init__(self, url): 33 | self.url = urllib.parse.urlparse(url) 34 | 35 | 36 | class GlanceStoreException(Exception): 37 | """ 38 | Base Glance Store Exception 39 | 40 | To correctly use this class, inherit from it and define 41 | a 'message' property. That message will get printf'd 42 | with the keyword arguments provided to the constructor. 43 | """ 44 | message = _("An unknown exception occurred") 45 | 46 | def __init__(self, message=None, **kwargs): 47 | if not message: 48 | message = self.message 49 | try: 50 | if kwargs: 51 | message = message % kwargs 52 | except Exception: 53 | pass 54 | self.msg = message 55 | super(GlanceStoreException, self).__init__(message) 56 | 57 | 58 | class MissingCredentialError(GlanceStoreException): 59 | message = _("Missing required credential: %(required)s") 60 | 61 | 62 | class BadAuthStrategy(GlanceStoreException): 63 | message = _("Incorrect auth strategy, expected \"%(expected)s\" but " 64 | "received \"%(received)s\"") 65 | 66 | 67 | class AuthorizationRedirect(GlanceStoreException): 68 | message = _("Redirecting to %(uri)s for authorization.") 69 | 70 | 71 | class NotFound(GlanceStoreException): 72 | message = _("Image %(image)s not found") 73 | 74 | 75 | class UnknownHashingAlgo(GlanceStoreException): 76 | message = _("Unknown hashing algorithm identifier: %(algo)s") 77 | 78 | 79 | class UnknownScheme(GlanceStoreException): 80 | message = _("Unknown scheme '%(scheme)s' found in URI") 81 | 82 | 83 | class BadStoreUri(GlanceStoreException): 84 | message = _("The Store URI was malformed: %(uri)s") 85 | 86 | 87 | class Duplicate(GlanceStoreException): 88 | message = _("Image %(image)s already exists") 89 | 90 | 91 | class StorageFull(GlanceStoreException): 92 | message = _("There is not enough disk space on the image storage media.") 93 | 94 | 95 | class StorageWriteDenied(GlanceStoreException): 96 | message = _("Permission to write image storage media denied.") 97 | 98 | 99 | class AuthBadRequest(GlanceStoreException): 100 | message = _("Connect error/bad request to Auth service at URL %(url)s.") 101 | 102 | 103 | class AuthUrlNotFound(GlanceStoreException): 104 | message = _("Auth service at URL %(url)s not found.") 105 | 106 | 107 | class AuthorizationFailure(GlanceStoreException): 108 | message = _("Authorization failed.") 109 | 110 | 111 | class NotAuthenticated(GlanceStoreException): 112 | message = _("You are not authenticated.") 113 | 114 | 115 | class Forbidden(GlanceStoreException): 116 | message = _("You are not authorized to complete this action.") 117 | 118 | 119 | class Invalid(GlanceStoreException): 120 | # NOTE(NiallBunting) This could be deprecated however the debtcollector 121 | # seems to have problems deprecating this as well as the subclasses. 122 | message = _("Data supplied was not valid.") 123 | 124 | 125 | class BadStoreConfiguration(GlanceStoreException): 126 | message = _("Store %(store_name)s could not be configured correctly. " 127 | "Reason: %(reason)s") 128 | 129 | 130 | class DriverLoadFailure(GlanceStoreException): 131 | message = _("Driver %(driver_name)s could not be loaded.") 132 | 133 | 134 | class StoreDeleteNotSupported(GlanceStoreException): 135 | message = _("Deleting images from this store is not supported.") 136 | 137 | 138 | class StoreGetNotSupported(GlanceStoreException): 139 | message = _("Getting images from this store is not supported.") 140 | 141 | 142 | class StoreRandomGetNotSupported(StoreGetNotSupported): 143 | message = _("Getting images randomly from this store is not supported. " 144 | "Offset: %(offset)s, length: %(chunk_size)s") 145 | 146 | 147 | class StoreAddDisabled(GlanceStoreException): 148 | message = _("Configuration for store failed. Adding images to this " 149 | "store is disabled.") 150 | 151 | 152 | class MaxRedirectsExceeded(GlanceStoreException): 153 | message = _("Maximum redirects (%(redirects)s) was exceeded.") 154 | 155 | 156 | class NoServiceEndpoint(GlanceStoreException): 157 | message = _("Response from Keystone does not contain a Glance endpoint.") 158 | 159 | 160 | class RegionAmbiguity(GlanceStoreException): 161 | message = _("Multiple 'image' service matches for region %(region)s. This " 162 | "generally means that a region is required and you have not " 163 | "supplied one.") 164 | 165 | 166 | class RemoteServiceUnavailable(GlanceStoreException): 167 | message = _("Remote server where the image is present is unavailable.") 168 | 169 | 170 | class HasSnapshot(GlanceStoreException): 171 | message = _("The image cannot be deleted because it has snapshot(s).") 172 | 173 | 174 | class InUseByStore(GlanceStoreException): 175 | message = _("The image cannot be deleted because it is in use through " 176 | "the backend store outside of Glance.") 177 | 178 | 179 | class HostNotInitialized(GlanceStoreException): 180 | message = _("The glance cinder store host %(host)s which will used to " 181 | "perform nfs mount/umount operations isn't initialized.") 182 | -------------------------------------------------------------------------------- /glance_store/i18n.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import oslo_i18n as i18n 17 | 18 | _translators = i18n.TranslatorFactory(domain='glance_store') 19 | 20 | # The primary translation function using the well-known name "_" 21 | _ = _translators.primary 22 | 23 | # Translators for log levels. 24 | # 25 | # The abbreviated names are meant to reflect the usual use of a short 26 | # name like '_'. The "L" is for "log" and the other letter comes from 27 | # the level. 28 | _LI = _translators.log_info 29 | _LW = _translators.log_warning 30 | _LE = _translators.log_error 31 | _LC = _translators.log_critical 32 | -------------------------------------------------------------------------------- /glance_store/locale/ko_KR/LC_MESSAGES/glance_store.po: -------------------------------------------------------------------------------- 1 | # Andreas Jaeger , 2016. #zanata 2 | # Jongwoo Han , 2017. #zanata 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: glance_store VERSION\n" 6 | "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" 7 | "POT-Creation-Date: 2018-02-28 18:24+0000\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "PO-Revision-Date: 2017-08-12 04:24+0000\n" 12 | "Last-Translator: Jongwoo Han \n" 13 | "Language-Team: Korean (South Korea)\n" 14 | "Language: ko_KR\n" 15 | "X-Generator: Zanata 4.3.3\n" 16 | "Plural-Forms: nplurals=1; plural=0\n" 17 | 18 | #, python-format 19 | msgid "" 20 | "A bad metadata structure was returned from the %(driver)s storage driver: " 21 | "%(metadata)s. %(e)s." 22 | msgstr "" 23 | "스토리지 드라이버 %(driver)s 가 잘못된 메타데이터 structure 를 반환했습니" 24 | "다 : %(metadata)s. %(e)s." 25 | 26 | msgid "An unknown exception occurred" 27 | msgstr "알 수 없는 예외가 발생했음" 28 | 29 | #, python-format 30 | msgid "Auth service at URL %(url)s not found." 31 | msgstr "URL %(url)s의 Auth 서비스를 찾을 수 없습니다." 32 | 33 | msgid "Authorization failed." 34 | msgstr "권한 부여에 실패했습니다. " 35 | 36 | msgid "" 37 | "Configuration for store failed. Adding images to this store is disabled." 38 | msgstr "" 39 | "저장소 설정이 실패했습니다. 이 저장소에 이미지를 추가하는 기능은 꺼집니다." 40 | 41 | #, python-format 42 | msgid "Connect error/bad request to Auth service at URL %(url)s." 43 | msgstr "연결 오류/URL %(url)s에서 Auth 서비스에 대한 잘못된 요청입니다." 44 | 45 | msgid "Data supplied was not valid." 46 | msgstr "제공된 데이터가 올바르지 않습니다." 47 | 48 | msgid "Deleting images from this store is not supported." 49 | msgstr "이 저장소에서 이미지를 지우는 것은 지원되지 않습니다." 50 | 51 | #, python-format 52 | msgid "Driver %(driver_name)s could not be loaded." 53 | msgstr " %(driver_name)s 드라이버를 로드하지 못했습니다." 54 | 55 | #, python-format 56 | msgid "Error: cooperative_iter exception %s" 57 | msgstr "오류: cooperative_iter 예외 %s" 58 | 59 | #, python-format 60 | msgid "Failed to configure store correctly: %s Disabling add method." 61 | msgstr "" 62 | "저장소를 제대로 설정하지 못했습니다. : %s 에서 add method를 제외합니다." 63 | 64 | msgid "Getting images from this store is not supported." 65 | msgstr "이 저장소에서 이미지를 가져오는 것은 지원되지 않습니다." 66 | 67 | #, python-format 68 | msgid "" 69 | "Getting images randomly from this store is not supported. Offset: " 70 | "%(offset)s, length: %(chunk_size)s" 71 | msgstr "" 72 | "이 저장소에서 이미지를 랜덤하게 가져오는 것은 지원되지 않습니다. 위치: " 73 | "%(offset)s, 길이: %(chunk_size)s" 74 | 75 | #, python-format 76 | msgid "Image %(image)s already exists" 77 | msgstr "이미지 %(image)s 가 이미 있습니다." 78 | 79 | #, python-format 80 | msgid "Image %(image)s not found" 81 | msgstr "이미지 %(image)s 가 없습니다." 82 | 83 | #, python-format 84 | msgid "" 85 | "Incorrect auth strategy, expected \"%(expected)s\" but received " 86 | "\"%(received)s\"" 87 | msgstr "" 88 | "인증 전략이 올바르지 않음. 예상: \"%(expected)s\", 수신: \"%(received)s\"" 89 | 90 | #, python-format 91 | msgid "Maximum redirects (%(redirects)s) was exceeded." 92 | msgstr "최대 경로 재지정(%(redirects)s)에 도달했습니다." 93 | 94 | #, python-format 95 | msgid "Missing required credential: %(required)s" 96 | msgstr "필수 신임 정보 누락: %(required)s" 97 | 98 | #, python-format 99 | msgid "" 100 | "Multiple 'image' service matches for region %(region)s. This generally means " 101 | "that a region is required and you have not supplied one." 102 | msgstr "" 103 | "다중 '이미지' 서비스가 %(region)s 리젼에 일치합니다. 이는 일반적으로 리젼이 " 104 | "필요하지만 아직 리젼을 제공하지 않은 경우 발생합니다." 105 | 106 | msgid "Permission to write image storage media denied." 107 | msgstr "이미지 스토리지 미디어에 쓰기 권한이 거부되었습니다." 108 | 109 | #, python-format 110 | msgid "Redirecting to %(uri)s for authorization." 111 | msgstr "권한 부여를 위해 %(uri)s(으)로 경로 재지정 중입니다." 112 | 113 | msgid "Remote server where the image is present is unavailable." 114 | msgstr "이 이미지가 있는 원격 서버에 접속할 수 없습니다." 115 | 116 | msgid "Response from Keystone does not contain a Glance endpoint." 117 | msgstr "Keystone의 응답에 Glance 엔드포인트가 들어있지 않습니다." 118 | 119 | msgid "Skipping store.set_acls... not implemented." 120 | msgstr " store.set_acls... 는 구현되지 않았으므로 skip합니다." 121 | 122 | #, python-format 123 | msgid "" 124 | "Store %(store_name)s could not be configured correctly. Reason: %(reason)s" 125 | msgstr "저장소 %(store_name)s 가 제대로 설정되지 않습니다. 원인: %(reason)s" 126 | 127 | #, python-format 128 | msgid "Store for scheme %s not found" 129 | msgstr "%s 스키마에 대한 저장소를 찾을 수 없음" 130 | 131 | #, python-format 132 | msgid "The Store URI was malformed: %(uri)s" 133 | msgstr "저장소 URI가 잘못된 형식입니다: %(uri)s" 134 | 135 | msgid "The image cannot be deleted because it has snapshot(s)." 136 | msgstr "스냅샷이 있으므로 이 이미지를 지울 수 없습니다." 137 | 138 | msgid "" 139 | "The image cannot be deleted because it is in use through the backend store " 140 | "outside of Glance." 141 | msgstr "" 142 | "이미지는 Glance가 사용하지 않는 백엔드 저장소에서 사용중이므로 삭제할 수 없습" 143 | "니다." 144 | 145 | #, python-format 146 | msgid "" 147 | "The image metadata key %(key)s has an invalid type of %(type)s. Only dict, " 148 | "list, and unicode are supported." 149 | msgstr "" 150 | "이미지 메타데이터 키 %(key)s 가 잘못된 타입인 %(type)s 타입입니다. dict, " 151 | "list, unicode만이 지원됩니다." 152 | 153 | #, python-format 154 | msgid "" 155 | "The storage driver %(driver)s returned invalid metadata %(metadata)s. This " 156 | "must be a dictionary type" 157 | msgstr "" 158 | "스토리지 드라이버 %(driver)s 가 잘못된 메타데이터 %(metadata)s 를 반환했습니" 159 | "다. 이 값은 반드시 dict 타입이어야 합니다." 160 | 161 | msgid "There is not enough disk space on the image storage media." 162 | msgstr "이미지 스토리지 매체에 충분한 저장 공간이 부족합니다." 163 | 164 | #, python-format 165 | msgid "Unknown scheme '%(scheme)s' found in URI" 166 | msgstr "URI에 알 수 없는 스킴인 '%(scheme)s' 가 있습니다." 167 | 168 | msgid "You are not authenticated." 169 | msgstr "인증되지 않은 사용자입니다." 170 | 171 | msgid "You are not authorized to complete this action." 172 | msgstr "이 조치를 완료할 권한이 없습니다. " 173 | -------------------------------------------------------------------------------- /glance_store/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/glance_store/tests/__init__.py -------------------------------------------------------------------------------- /glance_store/tests/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2011 OpenStack Foundation 2 | # Copyright 2014 Red Hat, Inc 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import os 18 | import shutil 19 | 20 | import fixtures 21 | from oslo_config import cfg 22 | from oslotest import base 23 | 24 | import glance_store as store 25 | from glance_store import location 26 | 27 | 28 | class StoreBaseTest(base.BaseTestCase): 29 | 30 | # NOTE(flaper87): temporary until we 31 | # can move to a fully-local lib. 32 | # (Swift store's fault) 33 | _CONF = cfg.ConfigOpts() 34 | 35 | def setUp(self): 36 | super(StoreBaseTest, self).setUp() 37 | self.conf = self._CONF 38 | self.conf(args=[]) 39 | store.register_opts(self.conf) 40 | self.config(stores=[]) 41 | 42 | # Ensure stores + locations cleared 43 | location.SCHEME_TO_CLS_MAP = {} 44 | 45 | store.create_stores(self.conf) 46 | self.addCleanup(setattr, location, 'SCHEME_TO_CLS_MAP', dict()) 47 | self.test_dir = self.useFixture(fixtures.TempDir()).path 48 | self.addCleanup(self.conf.reset) 49 | 50 | def copy_data_file(self, file_name, dst_dir): 51 | src_file_name = os.path.join('glance_store/tests/etc', file_name) 52 | shutil.copy(src_file_name, dst_dir) 53 | dst_file_name = os.path.join(dst_dir, file_name) 54 | return dst_file_name 55 | 56 | def config(self, **kw): 57 | """Override some configuration values. 58 | 59 | The keyword arguments are the names of configuration options to 60 | override and their values. 61 | 62 | If a group argument is supplied, the overrides are applied to 63 | the specified configuration option group. 64 | 65 | All overrides are automatically cleared at the end of the current 66 | test by the fixtures cleanup process. 67 | """ 68 | group = kw.pop('group', 'glance_store') 69 | for k, v in kw.items(): 70 | self.conf.set_override(k, v, group) 71 | 72 | def register_store_schemes(self, store, store_entry): 73 | schemes = store.get_schemes() 74 | scheme_map = {} 75 | 76 | loc_cls = store.get_store_location_class() 77 | for scheme in schemes: 78 | scheme_map[scheme] = { 79 | 'store': store, 80 | 'location_class': loc_cls, 81 | 'store_entry': store_entry 82 | } 83 | location.register_scheme_map(scheme_map) 84 | 85 | 86 | class MultiStoreBaseTest(base.BaseTestCase): 87 | 88 | def copy_data_file(self, file_name, dst_dir): 89 | src_file_name = os.path.join('glance_store/tests/etc', file_name) 90 | shutil.copy(src_file_name, dst_dir) 91 | dst_file_name = os.path.join(dst_dir, file_name) 92 | return dst_file_name 93 | 94 | def config(self, **kw): 95 | """Override some configuration values. 96 | 97 | The keyword arguments are the names of configuration options to 98 | override and their values. 99 | 100 | If a group argument is supplied, the overrides are applied to 101 | the specified configuration option group. 102 | 103 | All overrides are automatically cleared at the end of the current 104 | test by the fixtures cleanup process. 105 | """ 106 | group = kw.pop('group', None) 107 | for k, v in kw.items(): 108 | if group: 109 | self.conf.set_override(k, v, group) 110 | else: 111 | self.conf.set_override(k, v) 112 | 113 | def register_store_backend_schemes(self, store, store_entry, 114 | store_identifier): 115 | schemes = store.get_schemes() 116 | scheme_map = {} 117 | 118 | loc_cls = store.get_store_location_class() 119 | for scheme in schemes: 120 | scheme_map[scheme] = {} 121 | scheme_map[scheme][store_identifier] = { 122 | 'store': store, 123 | 'location_class': loc_cls, 124 | 'store_entry': store_entry 125 | } 126 | location.register_scheme_backend_map(scheme_map) 127 | -------------------------------------------------------------------------------- /glance_store/tests/etc/glance-swift.conf: -------------------------------------------------------------------------------- 1 | [ref1] 2 | user = tenant:user1 3 | key = key1 4 | auth_address = example.com 5 | 6 | [ref2] 7 | user = user2 8 | key = key2 9 | user_domain_id = default 10 | project_domain_id = default 11 | auth_version = 3 12 | auth_address = http://example.com 13 | 14 | [ref3] 15 | user = "user3" 16 | key = "key3" 17 | auth_address = "http://example.com" 18 | 19 | [ref4] 20 | user = user4 21 | key = key4 22 | user_domain_id = userdomainid 23 | project_domain_id = projdomainid 24 | auth_version = 3 25 | auth_address = "http://example.com" 26 | 27 | [ref5] 28 | user = user5 29 | key = key5 30 | user_domain_name = userdomain 31 | project_domain_name = projdomain 32 | auth_version = 3 33 | auth_address = "http://example.com" 34 | 35 | [store_2] 36 | user = tenant:user1 37 | key = key1 38 | auth_address= https://localhost:8080 39 | 40 | [store_3] 41 | user= tenant:user2 42 | key= key2 43 | auth_address= https://localhost:8080 44 | 45 | [store_4] 46 | user = tenant:user1 47 | key = key1 48 | auth_address = http://localhost:80 49 | 50 | [store_5] 51 | user = tenant:user1 52 | key = key1 53 | auth_address = http://localhost 54 | 55 | [store_6] 56 | user = tenant:user1 57 | key = key1 58 | auth_address = https://localhost/v1 59 | -------------------------------------------------------------------------------- /glance_store/tests/fakes.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Red hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from glance_store import driver 17 | from glance_store import exceptions 18 | 19 | 20 | class UnconfigurableStore(driver.Store): 21 | def configure(self, re_raise_bsc=False): 22 | raise exceptions.BadStoreConfiguration() 23 | -------------------------------------------------------------------------------- /glance_store/tests/functional/README.rst: -------------------------------------------------------------------------------- 1 | =============================== 2 | glance_store functional testing 3 | =============================== 4 | 5 | Writing functional tests for glance_store 6 | ----------------------------------------- 7 | 8 | The functional tests verify glance_store against a "live" backend. The tests 9 | are isolated so that a development environment doesn't have to all the backends 10 | available, just the particular backend whose driver the developer is working 11 | on. 12 | 13 | To add tests for a driver: 14 | 15 | 1. Create a new module in ``glance_store/tests/functional`` with the driver 16 | name. 17 | 18 | 2. Create a submodule ``test_functional_{driver-name}`` containing a class 19 | that inherits from ``glance_store.tests.functional.BaseFunctionalTests``. 20 | The actual tests are in the ``BaseFunctionalTests`` class. The test 21 | classes for each driver do any extra setup/teardown necessary for that 22 | particular driver. (The idea is that all the backends should be able to 23 | pass the same tests.) 24 | 25 | 3. Add a testenv to ``tox.ini`` named ``functional-{driver-name}`` so 26 | that tox can run the tests for your driver. (Use the other functional 27 | testenvs as examples.) 28 | 29 | 4. If your driver is well-supported by devstack, it shouldn't be too hard 30 | to set up a gate job for the functional tests in ``.zuul.yaml``. (Use 31 | the other jobs defined in that file as examples.) 32 | 33 | 34 | Configuration 35 | ------------- 36 | 37 | The functional tests have been designed to work well with devstack so that 38 | we can run them in the gate. Thus the tests expect to find a yaml file 39 | containing valid credentials just like the ``clouds.yaml`` file created by 40 | devstack in the ``/etc/openstack`` directory. The test code knows where 41 | to find it, so if you're using devstack, you should be all set. 42 | 43 | If you are not using devstack you should create a yaml file with the following 44 | format:: 45 | 46 | clouds: 47 | devstack-admin: 48 | auth: 49 | auth_url: https://172.16.132.143/identity 50 | password: example 51 | project_domain_id: default 52 | project_name: admin 53 | user_domain_id: default 54 | username: admin 55 | identity_api_version: '3' 56 | region_name: RegionOne 57 | volume_api_version: '3' 58 | 59 | The clouds.yaml format allows for a set of credentials to be defined for each 60 | named cloud. By default, the tests will use the credentials for the cloud 61 | named **devstack-admin** (that's the cloud shown in the example above). You 62 | can change which cloud is read from ``clouds.yaml`` by exporting the 63 | environment variable ``OS_TEST_GLANCE_STORE_FUNC_TEST_CLOUD`` set to the name 64 | of the cloud you want used. 65 | 66 | Where to put clouds.yaml 67 | ------------------------ 68 | 69 | The tests will look for a file named ``clouds.yaml`` in the 70 | following locations (in this order, first found wins): 71 | 72 | * current directory 73 | * ~/.config/openstack 74 | * /etc/openstack 75 | 76 | You may also set the environment variable ``OS_CLIENT_CONFIG_FILE`` 77 | to the absolute pathname of a file and that location will be 78 | inserted at the front of the search list. 79 | -------------------------------------------------------------------------------- /glance_store/tests/functional/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/glance_store/tests/functional/__init__.py -------------------------------------------------------------------------------- /glance_store/tests/functional/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import io 17 | from os import environ 18 | 19 | import glance_store 20 | import os_client_config 21 | from oslo_config import cfg 22 | import testtools 23 | 24 | CONF = cfg.CONF 25 | 26 | UUID1 = '961973d8-3360-4364-919e-2c197825dbb4' 27 | UUID2 = 'e03cf3b1-3070-4497-a37d-9703edfb615b' 28 | UUID3 = '0d7f89b2-e236-45e9-b081-561cd3102e92' 29 | UUID4 = '165e9681-ea56-46b0-a84c-f148c752ef8b' 30 | IMAGE_BITS = b'I am a bootable image, I promise' 31 | 32 | 33 | class Base(testtools.TestCase): 34 | 35 | def __init__(self, driver_name, *args, **kwargs): 36 | super(Base, self).__init__(*args, **kwargs) 37 | self.driver_name = driver_name 38 | 39 | # check whether a particular cloud should be used 40 | cloud = environ.get('OS_TEST_GLANCE_STORE_FUNC_TEST_CLOUD', 41 | 'devstack-admin') 42 | creds = os_client_config.OpenStackConfig().get_one_cloud( 43 | cloud=cloud) 44 | auth = creds.get_auth_args() 45 | self.username = auth["username"] 46 | self.password = auth["password"] 47 | self.project_name = auth["project_name"] 48 | self.user_domain_id = auth["user_domain_id"] 49 | self.project_domain_id = auth["project_domain_id"] 50 | self.keystone_version = creds.get_api_version('identity') 51 | self.cinder_version = creds.get_api_version('volume') 52 | self.region_name = creds.get_region_name() 53 | # auth_url in devstack clouds.yaml is unversioned 54 | if auth["auth_url"].endswith('/v3'): 55 | self.auth_url = auth["auth_url"] 56 | else: 57 | self.auth_url = '{}/v3'.format(auth["auth_url"]) 58 | 59 | # finally, load the configuration options 60 | glance_store.register_opts(CONF) 61 | 62 | def setUp(self): 63 | super(Base, self).setUp() 64 | 65 | CONF.set_override('stores', [self.driver_name], group='glance_store') 66 | CONF.set_override('default_store', 67 | self.driver_name, 68 | group='glance_store' 69 | ) 70 | glance_store.create_stores() 71 | self.store = glance_store.backend._load_store(CONF, self.driver_name) 72 | self.store.configure() 73 | 74 | 75 | class BaseFunctionalTests(Base): 76 | 77 | def test_add(self): 78 | image_file = io.BytesIO(IMAGE_BITS) 79 | loc, written, _, _ = self.store.add(UUID1, image_file, len(IMAGE_BITS)) 80 | self.assertEqual(len(IMAGE_BITS), written) 81 | 82 | def test_delete(self): 83 | image_file = io.BytesIO(IMAGE_BITS) 84 | loc, written, _, _ = self.store.add(UUID2, image_file, len(IMAGE_BITS)) 85 | location = glance_store.location.get_location_from_uri(loc) 86 | 87 | self.store.delete(location) 88 | 89 | def test_get_size(self): 90 | image_file = io.BytesIO(IMAGE_BITS) 91 | loc, written, _, _ = self.store.add(UUID3, image_file, len(IMAGE_BITS)) 92 | location = glance_store.location.get_location_from_uri(loc) 93 | 94 | size = self.store.get_size(location) 95 | self.assertEqual(len(IMAGE_BITS), size) 96 | 97 | def test_get(self): 98 | image_file = io.BytesIO(IMAGE_BITS) 99 | loc, written, _, _ = self.store.add(UUID3, image_file, len(IMAGE_BITS)) 100 | location = glance_store.location.get_location_from_uri(loc) 101 | 102 | image, size = self.store.get(location) 103 | 104 | self.assertEqual(len(IMAGE_BITS), size) 105 | 106 | data = b'' 107 | for chunk in image: 108 | data += chunk 109 | 110 | self.assertEqual(IMAGE_BITS, data) 111 | -------------------------------------------------------------------------------- /glance_store/tests/functional/filesystem/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/glance_store/tests/functional/filesystem/__init__.py -------------------------------------------------------------------------------- /glance_store/tests/functional/filesystem/test_functional_filesystem.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | 17 | import logging 18 | import shutil 19 | import tempfile 20 | 21 | from oslo_config import cfg 22 | 23 | from glance_store.tests.functional import base 24 | 25 | CONF = cfg.CONF 26 | 27 | logging.basicConfig() 28 | 29 | 30 | class TestFilesystem(base.BaseFunctionalTests): 31 | 32 | def __init__(self, *args, **kwargs): 33 | super(TestFilesystem, self).__init__('file', *args, **kwargs) 34 | 35 | def setUp(self): 36 | self.tmp_image_dir = tempfile.mkdtemp(prefix='glance_store_') 37 | CONF.set_override('filesystem_store_datadir', 38 | self.tmp_image_dir, 39 | group='glance_store') 40 | super(TestFilesystem, self).setUp() 41 | 42 | def tearDown(self): 43 | shutil.rmtree(self.tmp_image_dir) 44 | super(TestFilesystem, self).tearDown() 45 | -------------------------------------------------------------------------------- /glance_store/tests/functional/swift/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/glance_store/tests/functional/swift/__init__.py -------------------------------------------------------------------------------- /glance_store/tests/functional/swift/test_functional_swift.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | 17 | import logging 18 | import random 19 | import time 20 | 21 | from keystoneauth1.identity import v3 22 | from keystoneauth1 import session 23 | from oslo_config import cfg 24 | import swiftclient 25 | 26 | from glance_store.tests.functional import base 27 | 28 | CONF = cfg.CONF 29 | 30 | logging.basicConfig() 31 | 32 | 33 | class TestSwift(base.BaseFunctionalTests): 34 | 35 | def __init__(self, *args, **kwargs): 36 | super(TestSwift, self).__init__('swift', *args, **kwargs) 37 | 38 | CONF.set_override('swift_store_user', 39 | '{1}:{0}'.format(self.username, self.project_name), 40 | group='glance_store') 41 | CONF.set_override('swift_store_auth_address', 42 | self.auth_url, 43 | group='glance_store') 44 | CONF.set_override('swift_store_auth_version', 45 | self.keystone_version, 46 | group='glance_store') 47 | CONF.set_override('swift_store_key', 48 | self.password, 49 | group='glance_store') 50 | CONF.set_override('swift_store_region', 51 | self.region_name, 52 | group='glance_store') 53 | CONF.set_override('swift_store_create_container_on_put', 54 | True, 55 | group='glance_store') 56 | 57 | def setUp(self): 58 | self.container = ("glance_store_container_" + 59 | str(int(random.random() * 1000))) 60 | 61 | CONF.set_override('swift_store_container', 62 | self.container, 63 | group='glance_store') 64 | 65 | super(TestSwift, self).setUp() 66 | 67 | def tearDown(self): 68 | auth = v3.Password(auth_url=self.auth_url, 69 | username=self.username, 70 | password=self.password, 71 | project_name=self.project_name, 72 | user_domain_id=self.user_domain_id, 73 | project_domain_id=self.project_domain_id) 74 | sess = session.Session(auth=auth) 75 | swift = swiftclient.client.Connection(session=sess) 76 | 77 | for x in range(1, 4): 78 | time.sleep(x) 79 | try: 80 | _, objects = swift.get_container(self.container) 81 | for obj in objects: 82 | swift.delete_object(self.container, obj.get('name')) 83 | swift.delete_container(self.container) 84 | except Exception: 85 | if x < 3: 86 | pass 87 | else: 88 | raise 89 | else: 90 | break 91 | super(TestSwift, self).tearDown() 92 | -------------------------------------------------------------------------------- /glance_store/tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/glance_store/tests/unit/__init__.py -------------------------------------------------------------------------------- /glance_store/tests/unit/cinder/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/glance_store/tests/unit/cinder/__init__.py -------------------------------------------------------------------------------- /glance_store/tests/unit/cinder/test_base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 RedHat Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import sys 17 | from unittest import mock 18 | 19 | import ddt 20 | 21 | from glance_store._drivers.cinder import base 22 | from glance_store._drivers.cinder import scaleio 23 | from glance_store.tests import base as test_base 24 | 25 | sys.modules['glance_store.common.fs_mount'] = mock.Mock() 26 | from glance_store._drivers.cinder import store as cinder # noqa 27 | from glance_store._drivers.cinder import nfs # noqa 28 | 29 | 30 | @ddt.ddt 31 | class TestConnectorBase(test_base.StoreBaseTest): 32 | @ddt.data( 33 | ('iscsi', base.BaseBrickConnectorInterface), 34 | ('nfs', nfs.NfsBrickConnector), 35 | ('scaleio', scaleio.ScaleIOBrickConnector), 36 | ) 37 | @ddt.unpack 38 | def test_factory(self, protocol, expected_class): 39 | connector_class = base.factory( 40 | connection_info={'driver_volume_type': protocol}) 41 | self.assertIsInstance(connector_class, expected_class) 42 | 43 | 44 | class TestBaseBrickConnectorInterface(test_base.StoreBaseTest): 45 | 46 | def get_connection_info(self): 47 | """Return iSCSI connection information""" 48 | return { 49 | 'target_discovered': False, 50 | 'target_portal': '0.0.0.0:3260', 51 | 'target_iqn': 'iqn.2010-10.org.openstack:volume-fake-vol', 52 | 'target_lun': 0, 53 | 'volume_id': '007dedb8-ddc0-445c-88f1-d07acbe4efcb', 54 | 'auth_method': 'CHAP', 55 | 'auth_username': '2ttANgVaDRqxtMNK3hUj', 56 | 'auth_password': 'fake-password', 57 | 'encrypted': False, 58 | 'qos_specs': None, 59 | 'access_mode': 'rw', 60 | 'cacheable': False, 61 | 'driver_volume_type': 'iscsi', 62 | 'attachment_id': '7f45b2fe-111a-42df-be3e-f02b312ad8ea'} 63 | 64 | def setUp(self, connection_info={}, **kwargs): 65 | super().setUp() 66 | self.connection_info = connection_info or self.get_connection_info() 67 | self.root_helper = 'fake_rootwrap' 68 | self.use_multipath = False 69 | self.properties = { 70 | 'connection_info': self.connection_info, 71 | 'root_helper': self.root_helper, 72 | 'use_multipath': self.use_multipath} 73 | self.properties.update(kwargs) 74 | self.mock_object(base.connector.InitiatorConnector, 'factory') 75 | self.connector = base.factory(**self.properties) 76 | 77 | def mock_object(self, obj, attr_name, *args, **kwargs): 78 | """Use python mock to mock an object attribute 79 | 80 | Mocks the specified objects attribute with the given value. 81 | Automatically performs 'addCleanup' for the mock. 82 | """ 83 | patcher = mock.patch.object(obj, attr_name, *args, **kwargs) 84 | result = patcher.start() 85 | self.addCleanup(patcher.stop) 86 | return result 87 | 88 | def test_connect_volume(self): 89 | if self.connection_info['driver_volume_type'] == 'nfs': 90 | self.skip('NFS tests have custom implementation of this method.') 91 | fake_vol = mock.MagicMock() 92 | fake_path = {'path': 'fake_dev_path'} 93 | self.mock_object(self.connector.conn, 'connect_volume', 94 | return_value=fake_path) 95 | fake_dev_path = self.connector.connect_volume(fake_vol) 96 | self.connector.conn.connect_volume.assert_called_once_with( 97 | self.connector.connection_info) 98 | self.assertEqual(fake_path['path'], fake_dev_path['path']) 99 | 100 | def test_disconnect_volume(self): 101 | fake_device = 'fake_dev_path' 102 | self.mock_object(self.connector.conn, 'disconnect_volume') 103 | self.connector.disconnect_volume(fake_device) 104 | self.connector.conn.disconnect_volume.assert_called_once_with( 105 | self.connection_info, fake_device, force=True) 106 | 107 | def test_extend_volume(self): 108 | self.mock_object(self.connector.conn, 'extend_volume') 109 | self.connector.extend_volume() 110 | self.connector.conn.extend_volume.assert_called_once_with( 111 | self.connection_info) 112 | 113 | def test_yield_path(self): 114 | fake_vol = mock.MagicMock() 115 | fake_device = 'fake_dev_path' 116 | fake_dev_path = self.connector.yield_path(fake_vol, fake_device) 117 | self.assertEqual(fake_device, fake_dev_path) 118 | -------------------------------------------------------------------------------- /glance_store/tests/unit/cinder/test_nfs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 RedHat Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import os 17 | import sys 18 | from unittest import mock 19 | 20 | import ddt 21 | 22 | from glance_store import exceptions 23 | from glance_store.tests.unit.cinder import test_base as test_base_connector 24 | 25 | sys.modules['glance_store.common.fs_mount'] = mock.Mock() 26 | from glance_store._drivers.cinder import store as cinder # noqa 27 | from glance_store._drivers.cinder import nfs # noqa 28 | 29 | 30 | @ddt.ddt 31 | class TestNfsBrickConnector( 32 | test_base_connector.TestBaseBrickConnectorInterface): 33 | 34 | def setUp(self): 35 | self.connection_info = { 36 | 'export': 'localhost:/srv/fake-nfs-path', 37 | 'name': 'volume-1fa96ca8-9e07-4dad-a0ed-990c6e86b938', 38 | 'options': None, 39 | 'format': 'raw', 40 | 'qos_specs': None, 41 | 'access_mode': 'rw', 42 | 'encrypted': False, 43 | 'cacheable': False, 44 | 'driver_volume_type': 'nfs', 45 | 'mount_point_base': '/opt/stack/data/cinder/mnt', 46 | 'attachment_id': '7eb574ce-f32d-4173-a68b-870ead29fd84'} 47 | fake_attachment = mock.MagicMock(id='fake_attachment_uuid') 48 | self.mountpath = 'fake_mount_path' 49 | super().setUp(connection_info=self.connection_info, 50 | attachment_obj=fake_attachment, 51 | mountpoint_base=self.mountpath) 52 | 53 | @ddt.data( 54 | (False, 'raw'), 55 | (False, 'qcow2'), 56 | (True, 'raw'), 57 | (True, 'qcow2')) 58 | @ddt.unpack 59 | def test_connect_volume(self, encrypted, file_format): 60 | fake_vol = mock.MagicMock(id='fake_vol_uuid', encrypted=encrypted) 61 | fake_attachment = mock.MagicMock( 62 | id='fake_attachment_uuid', 63 | connection_info={'format': file_format}) 64 | self.mock_object(self.connector.volume_api, 'attachment_get', 65 | return_value=fake_attachment) 66 | if encrypted or file_format == 'qcow2': 67 | self.assertRaises(exceptions.BackendException, 68 | self.connector.connect_volume, 69 | fake_vol) 70 | else: 71 | fake_hash = 'fake_hash' 72 | fake_path = {'path': os.path.join( 73 | self.mountpath, fake_hash, self.connection_info['name'])} 74 | self.mock_object(nfs.NfsBrickConnector, 'get_hash_str', 75 | return_value=fake_hash) 76 | fake_dev_path = self.connector.connect_volume(fake_vol) 77 | nfs.mount.mount.assert_called_once_with( 78 | 'nfs', self.connection_info['export'], 79 | self.connection_info['name'], 80 | os.path.join(self.mountpath, fake_hash), 81 | self.connector.host, self.connector.root_helper, 82 | self.connection_info['options']) 83 | self.assertEqual(fake_path['path'], fake_dev_path['path']) 84 | 85 | def test_disconnect_volume(self): 86 | fake_hash = 'fake_hash' 87 | fake_path = {'path': os.path.join( 88 | self.mountpath, fake_hash, self.connection_info['name'])} 89 | mount_path, vol_name = fake_path['path'].rsplit('/', 1) 90 | self.connector.disconnect_volume(fake_path) 91 | nfs.mount.umount.assert_called_once_with( 92 | vol_name, mount_path, self.connector.host, 93 | self.connector.root_helper) 94 | 95 | def test_extend_volume(self): 96 | self.assertRaises(NotImplementedError, self.connector.extend_volume) 97 | -------------------------------------------------------------------------------- /glance_store/tests/unit/cinder/test_scaleio.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 RedHat Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import io 17 | import math 18 | import time 19 | from unittest import mock 20 | 21 | from oslo_utils import units 22 | 23 | from glance_store._drivers.cinder import scaleio 24 | from glance_store import exceptions 25 | from glance_store.tests.unit.cinder import test_base as test_base_connector 26 | 27 | 28 | class TestScaleioBrickConnector( 29 | test_base_connector.TestBaseBrickConnectorInterface): 30 | 31 | def setUp(self): 32 | connection_info = { 33 | 'scaleIO_volname': 'TZpPr43ISgmNSgpo0LP2uw==', 34 | 'hostIP': None, 'serverIP': 'l4-pflex154gw', 35 | 'serverPort': 443, 36 | 'serverUsername': 'admin', 37 | 'iopsLimit': None, 38 | 'bandwidthLimit': None, 39 | 'scaleIO_volume_id': '3b2f23b00000000d', 40 | 'config_group': 'powerflex1', 41 | 'failed_over': False, 42 | 'discard': True, 43 | 'qos_specs': None, 44 | 'access_mode': 'rw', 45 | 'encrypted': False, 46 | 'cacheable': False, 47 | 'driver_volume_type': 'scaleio', 48 | 'attachment_id': '22914c3a-5818-4840-9188-2ac9833b9f7b'} 49 | self.scaleio_connector = scaleio.ScaleIOBrickConnector 50 | super().setUp(connection_info=connection_info) 51 | 52 | def test__get_device_size(self): 53 | fake_data = b"fake binary data" 54 | fake_len = int(math.ceil(float(len(fake_data)) / units.Gi)) 55 | fake_file = io.BytesIO(fake_data) 56 | # Get current file pointer 57 | original_pos = fake_file.tell() 58 | dev_size = self.scaleio_connector._get_device_size(fake_file) 59 | self.assertEqual(fake_len, dev_size) 60 | # Verify that file pointer points to the original location 61 | self.assertEqual(original_pos, fake_file.tell()) 62 | 63 | def test__get_device_size_exception(self): 64 | fake_data = b"fake binary data" 65 | fake_file = io.BytesIO(fake_data) 66 | # Get current file pointer 67 | original_pos = fake_file.tell() 68 | with mock.patch.object( 69 | math, 'ceil', side_effect=exceptions.BackendException): 70 | self.assertRaises( 71 | exceptions.BackendException, 72 | self.scaleio_connector._get_device_size, fake_file) 73 | # Verify that file pointer points to the original location 74 | self.assertEqual(original_pos, fake_file.tell()) 75 | 76 | @mock.patch.object(time, 'sleep') 77 | def test__wait_resize_device_resized(self, mock_sleep): 78 | fake_vol = mock.MagicMock() 79 | fake_vol.size = 2 80 | fake_file = io.BytesIO(b"fake binary data") 81 | with mock.patch.object( 82 | self.scaleio_connector, 83 | '_get_device_size') as mock_get_dev_size: 84 | mock_get_dev_size.side_effect = [1, 2] 85 | self.scaleio_connector._wait_resize_device( 86 | fake_vol, fake_file) 87 | 88 | @mock.patch.object(time, 'sleep') 89 | def test__wait_resize_device_fails(self, mock_sleep): 90 | fake_vol = mock.MagicMock() 91 | fake_vol.size = 2 92 | fake_file = io.BytesIO(b"fake binary data") 93 | with mock.patch.object( 94 | self.scaleio_connector, '_get_device_size', 95 | return_value=1): 96 | self.assertRaises( 97 | exceptions.BackendException, 98 | self.scaleio_connector._wait_resize_device, 99 | fake_vol, fake_file) 100 | 101 | def test_yield_path(self): 102 | fake_vol = mock.MagicMock(size=1) 103 | fake_device = io.BytesIO(b"fake binary data") 104 | # Get current file pointer 105 | original_pos = fake_device.tell() 106 | fake_dev_path = self.connector.yield_path(fake_vol, fake_device) 107 | self.assertEqual(fake_device, fake_dev_path) 108 | # Verify that file pointer points to the original location 109 | self.assertEqual(original_pos, fake_device.tell()) 110 | -------------------------------------------------------------------------------- /glance_store/tests/unit/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/glance_store/tests/unit/common/__init__.py -------------------------------------------------------------------------------- /glance_store/tests/unit/common/test_attachment_state_manager.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 RedHat Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from unittest import mock 17 | 18 | from oslo_config import cfg 19 | from oslotest import base 20 | 21 | from cinderclient import exceptions as cinder_exception 22 | from glance_store.common import attachment_state_manager as attach_manager 23 | from glance_store.common import cinder_utils 24 | from glance_store import exceptions 25 | 26 | CONF = cfg.CONF 27 | 28 | 29 | class AttachmentStateManagerTestCase(base.BaseTestCase): 30 | 31 | class FakeAttachmentState: 32 | def __init__(self): 33 | self.attachments = {mock.sentinel.attachments} 34 | 35 | def setUp(self): 36 | super(AttachmentStateManagerTestCase, self).setUp() 37 | self.__manager__ = attach_manager.__manager__ 38 | 39 | def get_state(self): 40 | with self.__manager__.get_state() as state: 41 | return state 42 | 43 | def test_get_state_host_not_initialized(self): 44 | self.__manager__.state = None 45 | self.assertRaises(exceptions.HostNotInitialized, 46 | self.get_state) 47 | 48 | def test_get_state(self): 49 | self.__manager__.state = self.FakeAttachmentState() 50 | state = self.get_state() 51 | self.assertEqual({mock.sentinel.attachments}, state.attachments) 52 | 53 | 54 | class AttachmentStateTestCase(base.BaseTestCase): 55 | 56 | def setUp(self): 57 | super(AttachmentStateTestCase, self).setUp() 58 | self.attachments = set() 59 | self.m = attach_manager._AttachmentState() 60 | self.attach_call_1 = [mock.sentinel.client, mock.sentinel.volume_id] 61 | self.attach_call_2 = {'mode': mock.sentinel.mode} 62 | self.disconnect_vol_call = [mock.sentinel.device] 63 | self.detach_call = [mock.sentinel.client, mock.sentinel.attachment_id] 64 | self.attachment_dict = {'id': mock.sentinel.attachment_id} 65 | 66 | def _sentinel_attach(self): 67 | attachment_id = self.m.attach( 68 | mock.sentinel.client, mock.sentinel.volume_id, 69 | mock.sentinel.host, mode=mock.sentinel.mode) 70 | return attachment_id 71 | 72 | def _sentinel_detach(self, conn): 73 | self.m.detach(mock.sentinel.client, mock.sentinel.attachment_id, 74 | mock.sentinel.volume_id, mock.sentinel.host, 75 | conn, mock.sentinel.connection_info, 76 | mock.sentinel.device) 77 | 78 | @mock.patch.object(cinder_utils.API, 'attachment_create') 79 | def test_attach(self, mock_attach_create): 80 | mock_attach_create.return_value = self.attachment_dict 81 | attachment = self._sentinel_attach() 82 | mock_attach_create.assert_called_once_with( 83 | *self.attach_call_1, **self.attach_call_2) 84 | self.assertEqual(mock.sentinel.attachment_id, attachment['id']) 85 | 86 | @mock.patch.object(cinder_utils.API, 'attachment_delete') 87 | def test_detach_without_attach(self, mock_attach_delete): 88 | ex = exceptions.BackendException 89 | conn = mock.MagicMock() 90 | mock_attach_delete.side_effect = ex() 91 | self.assertRaises(ex, self._sentinel_detach, conn) 92 | conn.disconnect_volume.assert_called_once_with( 93 | *self.disconnect_vol_call) 94 | 95 | @mock.patch.object(cinder_utils.API, 'attachment_create') 96 | @mock.patch.object(cinder_utils.API, 'attachment_delete') 97 | def test_detach_with_attach(self, mock_attach_delete, mock_attach_create): 98 | conn = mock.MagicMock() 99 | mock_attach_create.return_value = self.attachment_dict 100 | attachment = self._sentinel_attach() 101 | self._sentinel_detach(conn) 102 | mock_attach_create.assert_called_once_with( 103 | *self.attach_call_1, **self.attach_call_2) 104 | self.assertEqual(mock.sentinel.attachment_id, attachment['id']) 105 | conn.disconnect_volume.assert_called_once_with( 106 | *self.disconnect_vol_call) 107 | mock_attach_delete.assert_called_once_with( 108 | *self.detach_call) 109 | 110 | @mock.patch.object(cinder_utils.API, 'attachment_create') 111 | def test_attach_fails(self, mock_attach_create): 112 | mock_attach_create.side_effect = cinder_exception.BadRequest(code=400) 113 | self.assertRaises( 114 | cinder_exception.BadRequest, self.m.attach, 115 | mock.sentinel.client, mock.sentinel.volume_id, 116 | mock.sentinel.host, mode=mock.sentinel.mode) 117 | -------------------------------------------------------------------------------- /glance_store/tests/unit/common/test_fs_mount.py: -------------------------------------------------------------------------------- 1 | # All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import sys 16 | from unittest import mock 17 | 18 | import fixtures 19 | from oslo_concurrency import processutils 20 | from oslo_config import cfg 21 | from oslotest import base 22 | 23 | from glance_store import exceptions 24 | 25 | CONF = cfg.CONF 26 | 27 | 28 | class HostMountManagerTestCase(base.BaseTestCase): 29 | 30 | class FakeHostMountState: 31 | def __init__(self): 32 | self.mountpoints = {mock.sentinel.mountpoint} 33 | 34 | def setUp(self): 35 | super(HostMountManagerTestCase, self).setUp() 36 | CONF.register_opt(cfg.DictOpt('enabled_backends')) 37 | CONF.set_override('enabled_backends', 'fake:file') 38 | # Since this is mocked in other tests, we unmock it here 39 | if 'glance_store.common.fs_mount' in sys.modules: 40 | sys.modules.pop('glance_store.common.fs_mount') 41 | # Since the _HostMountStateManager class instantiates on its 42 | # import, this import is done here to register the enabled_backends 43 | # config option before it is used during initialization 44 | from glance_store.common import fs_mount as mount # noqa 45 | self.__manager__ = mount.__manager__ 46 | 47 | def get_state(self): 48 | with self.__manager__.get_state() as state: 49 | return state 50 | 51 | def test_get_state_host_not_initialized(self): 52 | self.__manager__.state = None 53 | self.assertRaises(exceptions.HostNotInitialized, 54 | self.get_state) 55 | 56 | def test_get_state(self): 57 | self.__manager__.state = self.FakeHostMountState() 58 | state = self.get_state() 59 | self.assertEqual({mock.sentinel.mountpoint}, state.mountpoints) 60 | 61 | 62 | class HostMountStateTestCase(base.BaseTestCase): 63 | 64 | def setUp(self): 65 | super(HostMountStateTestCase, self).setUp() 66 | CONF.register_opt(cfg.DictOpt('enabled_backends')) 67 | CONF.set_override('enabled_backends', 'fake:file') 68 | # Since this is mocked in other tests, we unmock it here 69 | if 'glance_store.common.fs_mount' in sys.modules: 70 | sys.modules.pop('glance_store.common.fs_mount') 71 | # Since the _HostMountStateManager class instantiates on its 72 | # import, this import is done here to register the enabled_backends 73 | # config option before it is used during initialization 74 | from glance_store.common import fs_mount as mount # noqa 75 | self.mounted = set() 76 | self.m = mount._HostMountState() 77 | 78 | def fake_execute(cmd, *args, **kwargs): 79 | if cmd == 'mount': 80 | path = args[-1] 81 | if path in self.mounted: 82 | raise processutils.ProcessExecutionError('Already mounted') 83 | self.mounted.add(path) 84 | elif cmd == 'umount': 85 | path = args[-1] 86 | if path not in self.mounted: 87 | raise processutils.ProcessExecutionError('Not mounted') 88 | self.mounted.remove(path) 89 | 90 | def fake_ismount(path): 91 | return path in self.mounted 92 | 93 | mock_execute = mock.MagicMock(side_effect=fake_execute) 94 | 95 | self.useFixture(fixtures.MonkeyPatch( 96 | 'oslo_concurrency.processutils.execute', 97 | mock_execute)) 98 | self.useFixture(fixtures.MonkeyPatch('os.path.ismount', fake_ismount)) 99 | 100 | @staticmethod 101 | def _expected_sentinel_mount_calls(mountpoint=mock.sentinel.mountpoint): 102 | return [mock.call('mount', '-t', mock.sentinel.fstype, 103 | mock.sentinel.option1, mock.sentinel.option2, 104 | mock.sentinel.export, mountpoint, 105 | root_helper=mock.sentinel.rootwrap_helper, 106 | run_as_root=True)] 107 | 108 | @staticmethod 109 | def _expected_sentinel_umount_calls(mountpoint=mock.sentinel.mountpoint): 110 | return [mock.call('umount', mountpoint, attempts=3, 111 | delay_on_retry=True, 112 | root_helper=mock.sentinel.rootwrap_helper, 113 | run_as_root=True)] 114 | 115 | def _sentinel_mount(self): 116 | self.m.mount(mock.sentinel.fstype, mock.sentinel.export, 117 | mock.sentinel.vol, mock.sentinel.mountpoint, 118 | mock.sentinel.host, mock.sentinel.rootwrap_helper, 119 | [mock.sentinel.option1, mock.sentinel.option2]) 120 | 121 | def _sentinel_umount(self): 122 | self.m.umount(mock.sentinel.vol, mock.sentinel.mountpoint, 123 | mock.sentinel.host, mock.sentinel.rootwrap_helper) 124 | 125 | @mock.patch('os.makedirs') 126 | def test_mount(self, mock_makedirs): 127 | self._sentinel_mount() 128 | mock_makedirs.assert_called_once() 129 | processutils.execute.assert_has_calls( 130 | self._expected_sentinel_mount_calls()) 131 | 132 | def test_unmount_without_mount(self): 133 | self._sentinel_umount() 134 | processutils.execute.assert_not_called() 135 | 136 | @mock.patch('os.rmdir') 137 | @mock.patch('os.makedirs') 138 | def test_umount_with_mount(self, mock_makedirs, mock_rmdir): 139 | self._sentinel_mount() 140 | self._sentinel_umount() 141 | mock_makedirs.assert_called_once() 142 | mock_rmdir.assert_called_once() 143 | processutils.execute.assert_has_calls( 144 | self._expected_sentinel_mount_calls() + 145 | self._expected_sentinel_umount_calls()) 146 | -------------------------------------------------------------------------------- /glance_store/tests/unit/common/test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 RedHat Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import tempfile 17 | 18 | from oslotest import base 19 | 20 | from glance_store.common import utils 21 | 22 | 23 | class TestUtils(base.BaseTestCase): 24 | def test_cooperative_reader_returns_bytes(self): 25 | with tempfile.TemporaryFile() as fd: 26 | reader = utils.CooperativeReader(fd) 27 | # Make sure CooperativeReader does not use cooperative_read instead 28 | # of its own read method. 29 | reader.read = utils.CooperativeReader.read 30 | out = reader.read(reader) 31 | self.assertEqual(out, b'') 32 | -------------------------------------------------------------------------------- /glance_store/tests/unit/test_backend.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 OpenStack, LLC 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """Tests the backend store API's""" 17 | 18 | from unittest import mock 19 | 20 | from glance_store import backend 21 | from glance_store import exceptions 22 | from glance_store.tests import base 23 | 24 | 25 | class TestStoreAddToBackend(base.StoreBaseTest): 26 | 27 | def setUp(self): 28 | super(TestStoreAddToBackend, self).setUp() 29 | self.image_id = "animage" 30 | self.data = "dataandstuff" 31 | self.size = len(self.data) 32 | self.location = "file:///ab/cde/fgh" 33 | self.checksum = "md5" 34 | self.multihash = 'multihash' 35 | self.default_hash_algo = 'md5' 36 | self.hash_algo = 'sha256' 37 | 38 | def _bad_metadata(self, in_metadata): 39 | mstore = mock.Mock() 40 | mstore.add.return_value = (self.location, self.size, self.checksum, 41 | in_metadata) 42 | mstore.__str__ = lambda self: "hello" 43 | mstore.__unicode__ = lambda self: "hello" 44 | 45 | self.assertRaises(exceptions.BackendException, 46 | backend.store_add_to_backend, 47 | self.image_id, 48 | self.data, 49 | self.size, 50 | mstore) 51 | 52 | mstore.add.assert_called_once_with(self.image_id, mock.ANY, 53 | self.size, 54 | context=None, verifier=None) 55 | 56 | newstore = mock.Mock() 57 | newstore.add.return_value = (self.location, self.size, self.checksum, 58 | self.multihash, in_metadata) 59 | newstore.__str__ = lambda self: "hello" 60 | newstore.__unicode__ = lambda self: "hello" 61 | 62 | self.assertRaises(exceptions.BackendException, 63 | backend.store_add_to_backend_with_multihash, 64 | self.image_id, 65 | self.data, 66 | self.size, 67 | self.hash_algo, 68 | newstore) 69 | 70 | newstore.add.assert_called_once_with(self.image_id, mock.ANY, 71 | self.size, self.hash_algo, 72 | context=None, verifier=None) 73 | 74 | def _good_metadata(self, in_metadata): 75 | mstore = mock.Mock() 76 | mstore.add.return_value = (self.location, self.size, self.checksum, 77 | in_metadata) 78 | 79 | (location, 80 | size, 81 | checksum, 82 | metadata) = backend.store_add_to_backend(self.image_id, 83 | self.data, 84 | self.size, 85 | mstore) 86 | 87 | mstore.add.assert_called_once_with(self.image_id, mock.ANY, 88 | self.size, context=None, 89 | verifier=None) 90 | 91 | self.assertEqual(self.location, location) 92 | self.assertEqual(self.size, size) 93 | self.assertEqual(self.checksum, checksum) 94 | self.assertEqual(in_metadata, metadata) 95 | 96 | newstore = mock.Mock() 97 | newstore.add.return_value = (self.location, self.size, self.checksum, 98 | self.multihash, in_metadata) 99 | (location, 100 | size, 101 | checksum, 102 | multihash, 103 | metadata) = backend.store_add_to_backend_with_multihash( 104 | self.image_id, 105 | self.data, 106 | self.size, 107 | self.hash_algo, 108 | newstore) 109 | 110 | newstore.add.assert_called_once_with(self.image_id, mock.ANY, 111 | self.size, self.hash_algo, 112 | context=None, verifier=None) 113 | 114 | self.assertEqual(self.location, location) 115 | self.assertEqual(self.size, size) 116 | self.assertEqual(self.checksum, checksum) 117 | self.assertEqual(self.multihash, multihash) 118 | self.assertEqual(in_metadata, metadata) 119 | 120 | def test_empty(self): 121 | metadata = {} 122 | self._good_metadata(metadata) 123 | 124 | def test_string(self): 125 | metadata = {'key': 'somevalue'} 126 | self._good_metadata(metadata) 127 | 128 | def test_list(self): 129 | m = {'key': ['somevalue', '2']} 130 | self._good_metadata(m) 131 | 132 | def test_unicode_dict(self): 133 | inner = {'key1': 'somevalue', 'key2': 'somevalue'} 134 | m = {'topkey': inner} 135 | self._good_metadata(m) 136 | 137 | def test_unicode_dict_list(self): 138 | inner = {'key1': 'somevalue', 'key2': 'somevalue'} 139 | m = {'topkey': inner, 'list': ['somevalue', '2'], 'u': '2'} 140 | self._good_metadata(m) 141 | 142 | def test_nested_dict(self): 143 | inner = {'key1': 'somevalue', 'key2': 'somevalue'} 144 | inner = {'newkey': inner} 145 | inner = {'anotherkey': inner} 146 | m = {'topkey': inner} 147 | self._good_metadata(m) 148 | 149 | def test_bad_top_level_nonunicode(self): 150 | metadata = {'key': b'a string'} 151 | self._bad_metadata(metadata) 152 | 153 | def test_bad_nonunicode_dict_list(self): 154 | inner = {'key1': 'somevalue', 'key2': 'somevalue', 155 | 'k3': [1, object()]} 156 | m = {'topkey': inner, 'list': ['somevalue', '2'], 'u': '2'} 157 | self._bad_metadata(m) 158 | 159 | def test_bad_metadata_not_dict(self): 160 | self._bad_metadata([]) 161 | -------------------------------------------------------------------------------- /glance_store/tests/unit/test_exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | from oslo_utils import encodeutils 16 | from oslotest import base 17 | 18 | import glance_store 19 | 20 | 21 | class TestExceptions(base.BaseTestCase): 22 | """Test routines in glance_store.common.utils.""" 23 | def test_backend_exception(self): 24 | msg = glance_store.BackendException() 25 | self.assertIn('', encodeutils.exception_to_unicode(msg)) 26 | 27 | def test_unsupported_backend_exception(self): 28 | msg = glance_store.UnsupportedBackend() 29 | self.assertIn('', encodeutils.exception_to_unicode(msg)) 30 | 31 | def test_redirect_exception(self): 32 | # Just checks imports work ok 33 | glance_store.RedirectException(url='http://localhost') 34 | 35 | def test_exception_no_message(self): 36 | msg = glance_store.NotFound() 37 | self.assertIn('Image %(image)s not found', 38 | encodeutils.exception_to_unicode(msg)) 39 | 40 | def test_exception_not_found_with_image(self): 41 | msg = glance_store.NotFound(image='123') 42 | self.assertIn('Image 123 not found', 43 | encodeutils.exception_to_unicode(msg)) 44 | 45 | def test_exception_with_message(self): 46 | msg = glance_store.NotFound('Some message') 47 | self.assertIn('Some message', encodeutils.exception_to_unicode(msg)) 48 | 49 | def test_exception_with_kwargs(self): 50 | msg = glance_store.NotFound('Message: %(foo)s', foo='bar') 51 | self.assertIn('Message: bar', encodeutils.exception_to_unicode(msg)) 52 | 53 | def test_non_unicode_error_msg(self): 54 | exc = glance_store.NotFound(str('test')) 55 | self.assertIsInstance(encodeutils.exception_to_unicode(exc), 56 | str) 57 | -------------------------------------------------------------------------------- /glance_store/tests/unit/test_location.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from glance_store import exceptions 14 | from glance_store import location 15 | from glance_store.tests import base 16 | 17 | 18 | class TestStoreLocation(base.StoreBaseTest): 19 | 20 | def setUp(self): 21 | super(TestStoreLocation, self).setUp() 22 | 23 | def test_scheme_validation(self): 24 | valid_schemas = ("file://", "http://") 25 | correct_uri = "file://test" 26 | location.StoreLocation.validate_schemas(correct_uri, valid_schemas) 27 | incorrect_uri = "fake://test" 28 | self.assertRaises(exceptions.BadStoreUri, 29 | location.StoreLocation.validate_schemas, 30 | incorrect_uri, valid_schemas) 31 | -------------------------------------------------------------------------------- /glance_store/tests/unit/test_opts.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import stevedore 17 | from testtools import matchers 18 | 19 | from glance_store import backend 20 | from glance_store.tests import base 21 | 22 | 23 | def on_load_failure_callback(*args, **kwargs): 24 | raise 25 | 26 | 27 | class OptsTestCase(base.StoreBaseTest): 28 | 29 | def _check_opt_groups(self, opt_list, expected_opt_groups): 30 | self.assertThat(opt_list, matchers.HasLength(len(expected_opt_groups))) 31 | 32 | groups = [g for (g, _l) in opt_list] 33 | self.assertThat(groups, matchers.HasLength(len(expected_opt_groups))) 34 | 35 | for idx, group in enumerate(groups): 36 | self.assertEqual(expected_opt_groups[idx], group) 37 | 38 | def _check_opt_names(self, opt_list, expected_opt_names): 39 | opt_names = [o.name for (g, l) in opt_list for o in l] 40 | self.assertThat(opt_names, matchers.HasLength(len(expected_opt_names))) 41 | 42 | for opt in opt_names: 43 | self.assertIn(opt, expected_opt_names) 44 | 45 | def _test_entry_point(self, namespace, 46 | expected_opt_groups, expected_opt_names): 47 | opt_list = None 48 | mgr = stevedore.NamedExtensionManager( 49 | 'oslo.config.opts', 50 | names=[namespace], 51 | invoke_on_load=False, 52 | on_load_failure_callback=on_load_failure_callback, 53 | ) 54 | for ext in mgr: 55 | list_fn = ext.plugin 56 | opt_list = list_fn() 57 | break 58 | 59 | self.assertIsNotNone(opt_list) 60 | 61 | self._check_opt_groups(opt_list, expected_opt_groups) 62 | self._check_opt_names(opt_list, expected_opt_names) 63 | 64 | def test_list_api_opts(self): 65 | opt_list = backend._list_opts() 66 | expected_opt_groups = ['glance_store', 'glance_store'] 67 | expected_opt_names = [ 68 | 'default_store', 69 | 'stores', 70 | 'cinder_api_insecure', 71 | 'cinder_ca_certificates_file', 72 | 'cinder_catalog_info', 73 | 'cinder_endpoint_template', 74 | 'cinder_http_retries', 75 | 'cinder_mount_point_base', 76 | 'cinder_os_region_name', 77 | 'cinder_state_transition_timeout', 78 | 'cinder_store_auth_address', 79 | 'cinder_store_user_name', 80 | 'cinder_store_user_domain_name', 81 | 'cinder_store_password', 82 | 'cinder_store_project_name', 83 | 'cinder_store_project_domain_name', 84 | 'cinder_volume_type', 85 | 'cinder_use_multipath', 86 | 'cinder_enforce_multipath', 87 | 'cinder_do_extend_attached', 88 | 'default_swift_reference', 89 | 'https_insecure', 90 | 'filesystem_store_chunk_size', 91 | 'filesystem_store_datadir', 92 | 'filesystem_store_datadirs', 93 | 'filesystem_store_file_perm', 94 | 'filesystem_store_metadata_file', 95 | 'filesystem_thin_provisioning', 96 | 'http_proxy_information', 97 | 'https_ca_certificates_file', 98 | 'rbd_store_ceph_conf', 99 | 'rbd_store_chunk_size', 100 | 'rbd_store_pool', 101 | 'rbd_store_user', 102 | 'rbd_thin_provisioning', 103 | 'rados_connect_timeout', 104 | 'rootwrap_config', 105 | 's3_store_access_key', 106 | 's3_store_bucket', 107 | 's3_store_bucket_url_format', 108 | 's3_store_create_bucket_on_put', 109 | 's3_store_host', 110 | 's3_store_region_name', 111 | 's3_store_secret_key', 112 | 's3_store_large_object_size', 113 | 's3_store_large_object_chunk_size', 114 | 's3_store_thread_pools', 115 | 's3_store_cacert', 116 | 'swift_store_expire_soon_interval', 117 | 'swift_store_admin_tenants', 118 | 'swift_store_auth_address', 119 | 'swift_store_cacert', 120 | 'swift_store_auth_insecure', 121 | 'swift_store_auth_version', 122 | 'swift_store_config_file', 123 | 'swift_store_container', 124 | 'swift_store_create_container_on_put', 125 | 'swift_store_endpoint', 126 | 'swift_store_endpoint_type', 127 | 'swift_store_key', 128 | 'swift_store_large_object_chunk_size', 129 | 'swift_store_large_object_size', 130 | 'swift_store_multi_tenant', 131 | 'swift_store_multiple_containers_seed', 132 | 'swift_store_region', 133 | 'swift_store_retry_get_count', 134 | 'swift_store_service_type', 135 | 'swift_store_ssl_compression', 136 | 'swift_store_use_trusts', 137 | 'swift_store_user', 138 | 'swift_buffer_on_upload', 139 | 'swift_upload_buffer_dir', 140 | 'vmware_insecure', 141 | 'vmware_ca_file', 142 | 'vmware_api_retry_count', 143 | 'vmware_datastores', 144 | 'vmware_server_host', 145 | 'vmware_server_password', 146 | 'vmware_server_username', 147 | 'vmware_store_image_dir', 148 | 'vmware_task_poll_interval' 149 | ] 150 | 151 | self._check_opt_groups(opt_list, expected_opt_groups) 152 | self._check_opt_names(opt_list, expected_opt_names) 153 | self._test_entry_point('glance.store', 154 | expected_opt_groups, expected_opt_names) 155 | -------------------------------------------------------------------------------- /glance_store/tests/unit/test_store_base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2013 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from unittest import mock 17 | 18 | from oslo_config import cfg 19 | 20 | import glance_store as store 21 | from glance_store import backend 22 | from glance_store import location 23 | from glance_store import multi_backend 24 | from glance_store.tests import base 25 | 26 | 27 | class TestStoreBase(base.StoreBaseTest): 28 | 29 | def setUp(self): 30 | super(TestStoreBase, self).setUp() 31 | self.config(default_store='file', group='glance_store') 32 | 33 | @mock.patch.object(store.driver, 'LOG') 34 | def test_configure_does_not_raise_on_missing_driver_conf(self, mock_log): 35 | self.config(stores=['file'], group='glance_store') 36 | self.config(filesystem_store_datadir=None, group='glance_store') 37 | self.config(filesystem_store_datadirs=None, group='glance_store') 38 | for (__, store_instance) in backend._load_stores(self.conf): 39 | store_instance.configure() 40 | mock_log.warning.assert_called_once_with( 41 | "Failed to configure store correctly: Store filesystem " 42 | "could not be configured correctly. Reason: Specify " 43 | "at least 'filesystem_store_datadir' or " 44 | "'filesystem_store_datadirs' option Disabling add method.") 45 | 46 | 47 | class TestMultiStoreBase(base.MultiStoreBaseTest): 48 | _CONF = multi_backend.CONF 49 | 50 | def setUp(self): 51 | super(TestMultiStoreBase, self).setUp() 52 | enabled_backends = { 53 | "fast": "file", 54 | "cheap": "file", 55 | } 56 | 57 | self.reserved_stores = { 58 | 'consuming_service_reserved_store': 'file' 59 | } 60 | 61 | self.conf = self._CONF 62 | self.conf(args=[]) 63 | self.conf.register_opt(cfg.DictOpt('enabled_backends')) 64 | self.config(enabled_backends=enabled_backends) 65 | 66 | store.register_store_opts(self.conf, 67 | reserved_stores=self.reserved_stores) 68 | self.config(default_backend='file1', group='glance_store') 69 | 70 | # Ensure stores + locations cleared 71 | location.SCHEME_TO_CLS_BACKEND_MAP = {} 72 | 73 | store.create_multi_stores(self.conf, 74 | reserved_stores=self.reserved_stores) 75 | self.addCleanup(setattr, location, 'SCHEME_TO_CLS_BACKEND_MAP', 76 | dict()) 77 | self.addCleanup(self.conf.reset) 78 | 79 | def test_reserved_stores_loaded(self): 80 | # assert global map has reserved stores registered 81 | store = multi_backend.get_store_from_store_identifier( 82 | 'consuming_service_reserved_store') 83 | 84 | self.assertIsNotNone(store) 85 | self.assertEqual(self.reserved_stores, multi_backend._RESERVED_STORES) 86 | # verify that store config group in conf file is same as 87 | # reserved store name 88 | self.assertEqual('consuming_service_reserved_store', 89 | store.backend_group) 90 | -------------------------------------------------------------------------------- /glance_store/tests/unit/test_store_capabilities.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | 17 | from glance_store import capabilities as caps 18 | from glance_store.tests import base 19 | 20 | 21 | class FakeStoreWithStaticCapabilities(caps.StoreCapability): 22 | _CAPABILITIES = caps.BitMasks.READ_RANDOM | caps.BitMasks.DRIVER_REUSABLE 23 | 24 | 25 | class FakeStoreWithDynamicCapabilities(caps.StoreCapability): 26 | def __init__(self, *cap_list): 27 | super(FakeStoreWithDynamicCapabilities, self).__init__() 28 | if not cap_list: 29 | cap_list = [caps.BitMasks.READ_RANDOM, 30 | caps.BitMasks.DRIVER_REUSABLE] 31 | self.set_capabilities(*cap_list) 32 | 33 | 34 | class FakeStoreWithMixedCapabilities(caps.StoreCapability): 35 | _CAPABILITIES = caps.BitMasks.READ_RANDOM 36 | 37 | def __init__(self): 38 | super(FakeStoreWithMixedCapabilities, self).__init__() 39 | self.set_capabilities(caps.BitMasks.DRIVER_REUSABLE) 40 | 41 | 42 | class TestStoreCapabilitiesChecking(object): 43 | 44 | def test_store_capabilities_checked_on_io_operations(self): 45 | self.assertEqual('op_checker', self.store.add.__name__) 46 | self.assertEqual('op_checker', self.store.get.__name__) 47 | self.assertEqual('op_checker', self.store.delete.__name__) 48 | 49 | 50 | class TestStoreCapabilities(base.StoreBaseTest): 51 | 52 | def _verify_store_capabilities(self, store): 53 | # This function tested is_capable() as well. 54 | self.assertTrue(store.is_capable(caps.BitMasks.READ_RANDOM)) 55 | self.assertTrue(store.is_capable(caps.BitMasks.DRIVER_REUSABLE)) 56 | self.assertFalse(store.is_capable(caps.BitMasks.WRITE_ACCESS)) 57 | 58 | def test_static_capabilities_setup(self): 59 | self._verify_store_capabilities(FakeStoreWithStaticCapabilities()) 60 | 61 | def test_dynamic_capabilities_setup(self): 62 | self._verify_store_capabilities(FakeStoreWithDynamicCapabilities()) 63 | 64 | def test_mixed_capabilities_setup(self): 65 | self._verify_store_capabilities(FakeStoreWithMixedCapabilities()) 66 | 67 | def test_set_unset_capabilities(self): 68 | store = FakeStoreWithStaticCapabilities() 69 | self.assertFalse(store.is_capable(caps.BitMasks.WRITE_ACCESS)) 70 | 71 | # Set and unset single capability on one time 72 | store.set_capabilities(caps.BitMasks.WRITE_ACCESS) 73 | self.assertTrue(store.is_capable(caps.BitMasks.WRITE_ACCESS)) 74 | store.unset_capabilities(caps.BitMasks.WRITE_ACCESS) 75 | self.assertFalse(store.is_capable(caps.BitMasks.WRITE_ACCESS)) 76 | 77 | # Set and unset multiple capabilities on one time 78 | cap_list = [caps.BitMasks.WRITE_ACCESS, caps.BitMasks.WRITE_OFFSET] 79 | store.set_capabilities(*cap_list) 80 | self.assertTrue(store.is_capable(*cap_list)) 81 | store.unset_capabilities(*cap_list) 82 | self.assertFalse(store.is_capable(*cap_list)) 83 | 84 | def test_store_capabilities_property(self): 85 | store1 = FakeStoreWithDynamicCapabilities() 86 | self.assertTrue(hasattr(store1, 'capabilities')) 87 | store2 = FakeStoreWithMixedCapabilities() 88 | self.assertEqual(store1.capabilities, store2.capabilities) 89 | 90 | def test_cascaded_unset_capabilities(self): 91 | # Test read capability 92 | store = FakeStoreWithMixedCapabilities() 93 | self._verify_store_capabilities(store) 94 | store.unset_capabilities(caps.BitMasks.READ_ACCESS) 95 | cap_list = [caps.BitMasks.READ_ACCESS, caps.BitMasks.READ_OFFSET, 96 | caps.BitMasks.READ_CHUNK, caps.BitMasks.READ_RANDOM] 97 | for cap in cap_list: 98 | # To make sure all of them are unsetted. 99 | self.assertFalse(store.is_capable(cap)) 100 | self.assertTrue(store.is_capable(caps.BitMasks.DRIVER_REUSABLE)) 101 | 102 | # Test write capability 103 | store = FakeStoreWithDynamicCapabilities(caps.BitMasks.WRITE_RANDOM, 104 | caps.BitMasks.DRIVER_REUSABLE) 105 | self.assertTrue(store.is_capable(caps.BitMasks.WRITE_RANDOM)) 106 | self.assertTrue(store.is_capable(caps.BitMasks.DRIVER_REUSABLE)) 107 | store.unset_capabilities(caps.BitMasks.WRITE_ACCESS) 108 | cap_list = [caps.BitMasks.WRITE_ACCESS, caps.BitMasks.WRITE_OFFSET, 109 | caps.BitMasks.WRITE_CHUNK, caps.BitMasks.WRITE_RANDOM] 110 | for cap in cap_list: 111 | # To make sure all of them are unsetted. 112 | self.assertFalse(store.is_capable(cap)) 113 | self.assertTrue(store.is_capable(caps.BitMasks.DRIVER_REUSABLE)) 114 | 115 | 116 | class TestStoreCapabilityConstants(base.StoreBaseTest): 117 | 118 | def test_one_single_capability_own_one_bit(self): 119 | cap_list = [ 120 | caps.BitMasks.READ_ACCESS, 121 | caps.BitMasks.WRITE_ACCESS, 122 | caps.BitMasks.DRIVER_REUSABLE, 123 | ] 124 | for cap in cap_list: 125 | self.assertEqual(1, bin(cap).count('1')) 126 | 127 | def test_combined_capability_bits(self): 128 | check = caps.StoreCapability.contains 129 | check(caps.BitMasks.READ_OFFSET, caps.BitMasks.READ_ACCESS) 130 | check(caps.BitMasks.READ_CHUNK, caps.BitMasks.READ_ACCESS) 131 | check(caps.BitMasks.READ_RANDOM, caps.BitMasks.READ_CHUNK) 132 | check(caps.BitMasks.READ_RANDOM, caps.BitMasks.READ_OFFSET) 133 | check(caps.BitMasks.WRITE_OFFSET, caps.BitMasks.WRITE_ACCESS) 134 | check(caps.BitMasks.WRITE_CHUNK, caps.BitMasks.WRITE_ACCESS) 135 | check(caps.BitMasks.WRITE_RANDOM, caps.BitMasks.WRITE_CHUNK) 136 | check(caps.BitMasks.WRITE_RANDOM, caps.BitMasks.WRITE_OFFSET) 137 | check(caps.BitMasks.RW_ACCESS, caps.BitMasks.READ_ACCESS) 138 | check(caps.BitMasks.RW_ACCESS, caps.BitMasks.WRITE_ACCESS) 139 | check(caps.BitMasks.RW_OFFSET, caps.BitMasks.READ_OFFSET) 140 | check(caps.BitMasks.RW_OFFSET, caps.BitMasks.WRITE_OFFSET) 141 | check(caps.BitMasks.RW_CHUNK, caps.BitMasks.READ_CHUNK) 142 | check(caps.BitMasks.RW_CHUNK, caps.BitMasks.WRITE_CHUNK) 143 | check(caps.BitMasks.RW_RANDOM, caps.BitMasks.READ_RANDOM) 144 | check(caps.BitMasks.RW_RANDOM, caps.BitMasks.WRITE_RANDOM) 145 | -------------------------------------------------------------------------------- /glance_store/tests/unit/test_swift_store_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import fixtures 17 | 18 | from glance_store._drivers.swift import utils as sutils 19 | from glance_store import exceptions 20 | from glance_store.tests import base 21 | 22 | 23 | class TestSwiftParams(base.StoreBaseTest): 24 | 25 | def setUp(self): 26 | super(TestSwiftParams, self).setUp() 27 | conf_file = "glance-swift.conf" 28 | test_dir = self.useFixture(fixtures.TempDir()).path 29 | self.swift_config_file = self.copy_data_file(conf_file, test_dir) 30 | self.config(swift_store_config_file=self.swift_config_file) 31 | 32 | def test_multiple_swift_account_enabled(self): 33 | self.config(swift_store_config_file="glance-swift.conf") 34 | self.assertTrue( 35 | sutils.is_multiple_swift_store_accounts_enabled(self.conf)) 36 | 37 | def test_multiple_swift_account_disabled(self): 38 | self.config(swift_store_config_file=None) 39 | self.assertFalse( 40 | sutils.is_multiple_swift_store_accounts_enabled(self.conf)) 41 | 42 | def test_swift_config_file_doesnt_exist(self): 43 | self.config(swift_store_config_file='fake-file.conf') 44 | self.assertRaises(exceptions.BadStoreConfiguration, 45 | sutils.SwiftParams, self.conf) 46 | 47 | def test_swift_config_uses_default_values_multiple_account_disabled(self): 48 | default_user = 'user_default' 49 | default_key = 'key_default' 50 | default_auth_address = 'auth@default.com' 51 | default_account_reference = 'ref_default' 52 | conf = {'swift_store_config_file': None, 53 | 'swift_store_user': default_user, 54 | 'swift_store_key': default_key, 55 | 'swift_store_auth_address': default_auth_address, 56 | 'default_swift_reference': default_account_reference} 57 | self.config(**conf) 58 | swift_params = sutils.SwiftParams(self.conf).params 59 | self.assertEqual(1, len(swift_params.keys())) 60 | self.assertEqual(default_user, 61 | swift_params[default_account_reference]['user'] 62 | ) 63 | self.assertEqual(default_key, 64 | swift_params[default_account_reference]['key'] 65 | ) 66 | self.assertEqual(default_auth_address, 67 | swift_params[default_account_reference] 68 | ['auth_address'] 69 | ) 70 | 71 | def test_swift_store_config_validates_for_creds_auth_address(self): 72 | swift_params = sutils.SwiftParams(self.conf).params 73 | self.assertEqual('tenant:user1', 74 | swift_params['ref1']['user'] 75 | ) 76 | self.assertEqual('key1', 77 | swift_params['ref1']['key'] 78 | ) 79 | self.assertEqual('example.com', 80 | swift_params['ref1']['auth_address']) 81 | self.assertEqual('user2', 82 | swift_params['ref2']['user']) 83 | self.assertEqual('key2', 84 | swift_params['ref2']['key']) 85 | self.assertEqual('http://example.com', 86 | swift_params['ref2']['auth_address'] 87 | ) 88 | 89 | def test_swift_store_config_validates_quotes_removal(self): 90 | swift_params = sutils.SwiftParams(self.conf).params 91 | self.assertEqual('user3', 92 | swift_params['ref3']['user'] 93 | ) 94 | self.assertEqual('key3', 95 | swift_params['ref3']['key'] 96 | ) 97 | self.assertEqual('http://example.com', 98 | swift_params['ref3']['auth_address'] 99 | ) 100 | 101 | def test_swift_store_config_without_domain(self): 102 | swift_params = sutils.SwiftParams(self.conf).params 103 | self.assertEqual('default', swift_params['ref1']['project_domain_id']) 104 | self.assertIsNone(swift_params['ref1']['project_domain_name']) 105 | self.assertEqual('default', swift_params['ref1']['user_domain_id']) 106 | self.assertIsNone(swift_params['ref1']['user_domain_name']) 107 | 108 | def test_swift_store_config_with_domain_ids(self): 109 | swift_params = sutils.SwiftParams(self.conf).params 110 | self.assertEqual('projdomainid', 111 | swift_params['ref4']['project_domain_id']) 112 | self.assertIsNone(swift_params['ref4']['project_domain_name']) 113 | self.assertEqual('userdomainid', 114 | swift_params['ref4']['user_domain_id']) 115 | self.assertIsNone(swift_params['ref4']['user_domain_name']) 116 | 117 | def test_swift_store_config_with_domain_names(self): 118 | swift_params = sutils.SwiftParams(self.conf).params 119 | self.assertIsNone(swift_params['ref5']['project_domain_id']) 120 | self.assertEqual('projdomain', 121 | swift_params['ref5']['project_domain_name']) 122 | self.assertIsNone(swift_params['ref5']['user_domain_id']) 123 | self.assertEqual('userdomain', 124 | swift_params['ref5']['user_domain_name']) 125 | 126 | 127 | class TestSwiftConfigParser(base.StoreBaseTest): 128 | 129 | def setUp(self): 130 | super(TestSwiftConfigParser, self).setUp() 131 | self.method = sutils.SwiftConfigParser._process_quotes 132 | 133 | def test_quotes_processor(self): 134 | self.assertEqual('user', self.method('user')) 135 | self.assertEqual('user', self.method('"user"')) 136 | self.assertEqual("user", self.method("'user'")) 137 | self.assertEqual("user'", self.method("user'")) 138 | self.assertEqual('user"', self.method('user"')) 139 | 140 | def test_quotes_processor_negative(self): 141 | negative_values = [ 142 | '\'user"', '"user\'', '\'user', '"user\'', 143 | "'user", '"user', '"', "'", 144 | ] 145 | for value in negative_values: 146 | self.assertRaises(ValueError, self.method, value) 147 | -------------------------------------------------------------------------------- /glance_store/tests/unit/test_test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Red Hat, Inc 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from glance_store.tests import base 17 | from glance_store.tests import utils as test_utils 18 | 19 | 20 | class TestFakeData(base.StoreBaseTest): 21 | def test_via_read(self): 22 | fd = test_utils.FakeData(1024) 23 | data = [] 24 | for i in range(0, 1025, 256): 25 | chunk = fd.read(256) 26 | data.append(chunk) 27 | if not chunk: 28 | break 29 | 30 | self.assertEqual(5, len(data)) 31 | # Make sure we got a zero-length final read 32 | self.assertEqual(b'', data[-1]) 33 | # Make sure we only got 1024 bytes 34 | self.assertEqual(1024, len(b''.join(data))) 35 | 36 | def test_via_iter(self): 37 | data = b''.join(list(test_utils.FakeData(1024))) 38 | self.assertEqual(1024, len(data)) 39 | -------------------------------------------------------------------------------- /glance_store/tests/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import io 17 | import urllib.parse 18 | 19 | from oslo_utils import units 20 | import requests 21 | 22 | 23 | def sort_url_by_qs_keys(url): 24 | # NOTE(kragniz): this only sorts the keys of the query string of a url. 25 | # For example, an input of '/v2/tasks?sort_key=id&sort_dir=asc&limit=10' 26 | # returns '/v2/tasks?limit=10&sort_dir=asc&sort_key=id'. This is to prevent 27 | # non-deterministic ordering of the query string causing problems with unit 28 | # tests. 29 | parsed = urllib.parse.urlparse(url) 30 | # In python2.6, for arbitrary url schemes, query string 31 | # is not parsed from url. http://bugs.python.org/issue9374 32 | path = parsed.path 33 | query = parsed.query 34 | if not query: 35 | path, query = parsed.path.split('?', 1) 36 | queries = urllib.parse.parse_qsl(query, True) 37 | sorted_query = sorted(queries, key=lambda x: x[0]) 38 | encoded_sorted_query = urllib.parse.urlencode(sorted_query, True) 39 | url_parts = (parsed.scheme, parsed.netloc, path, 40 | parsed.params, encoded_sorted_query, 41 | parsed.fragment) 42 | return urllib.parse.urlunparse(url_parts) 43 | 44 | 45 | class FakeHTTPResponse(object): 46 | def __init__(self, status=200, headers=None, data=None, *args, **kwargs): 47 | data = data or 'I am a teapot, short and stout\n' 48 | self.data = io.StringIO(data) 49 | self.read = self.data.read 50 | self.status = status 51 | self.headers = headers or {'content-length': len(data)} 52 | if not kwargs.get('no_response_body', False): 53 | self.body = None 54 | 55 | def getheader(self, name, default=None): 56 | return self.headers.get(name.lower(), default) 57 | 58 | def getheaders(self): 59 | return self.headers or {} 60 | 61 | def read(self, amt): 62 | self.data.read(amt) 63 | 64 | def release_conn(self): 65 | pass 66 | 67 | def close(self): 68 | self.data.close() 69 | 70 | 71 | def fake_response(status_code=200, headers=None, content=None, **kwargs): 72 | r = requests.models.Response() 73 | r.status_code = status_code 74 | r.headers = headers or {} 75 | r.raw = FakeHTTPResponse(status_code, headers, content, kwargs) 76 | return r 77 | 78 | 79 | class FakeData(object): 80 | """Generate a bunch of data without storing it in memory. 81 | 82 | This acts like a read-only file object which generates fake data 83 | in chunks when read() is called or it is used as a generator. It 84 | can generate an arbitrary amount of data without storing it in 85 | memory. 86 | 87 | :param length: The number of bytes to generate 88 | :param chunk_size: The chunk size to return in iteration mode, or when 89 | read() is called unbounded 90 | 91 | """ 92 | def __init__(self, length, chunk_size=64 * units.Ki): 93 | self._max = length 94 | self._chunk_size = chunk_size 95 | self._len = 0 96 | 97 | def read(self, length=None): 98 | if length is None: 99 | length = self._chunk_size 100 | 101 | length = min(length, self._max - self._len) 102 | 103 | self._len += length 104 | if length == 0: 105 | return b'' 106 | else: 107 | return b'0' * length 108 | 109 | def __iter__(self): 110 | return self 111 | 112 | def __next__(self): 113 | r = self.read() 114 | if len(r) == 0: 115 | raise StopIteration() 116 | else: 117 | return r 118 | -------------------------------------------------------------------------------- /releasenotes/notes/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/releasenotes/notes/.placeholder -------------------------------------------------------------------------------- /releasenotes/notes/0.29.1-notes-ded2a1d473a306c7.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | critical: 3 | - | 4 | glance_store 0.29.0 was released with backwards incompatible changes. 5 | There was no corresponding releasenote to mention this. 0.29.1 has 6 | reverted said change which will be included to 1.0.0 release later 7 | on the cycle. 8 | fixes: 9 | - | 10 | Following bugs were fixed and included after 0.28.0 release: 11 | * Bug 1824533_: Do not include ETag when puting manifest in chunked uploads 12 | * Bug 1818915_: Python3: Fix return type on CooperativeReader.read 13 | * Bug 1805332_: Prevent unicode object error from zero-byte read 14 | 15 | .. _1824533: https://code.launchpad.net/bugs/1824533 16 | .. _1818915: https://code.launchpad.net/bugs/1818915 17 | .. _1805332: https://code.launchpad.net/bugs/1805332 18 | -------------------------------------------------------------------------------- /releasenotes/notes/Stein_final_release-c7df5838028b8c7e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: 3 | This was a quiet development cycle for the ``glance_store`` library. 4 | One new feature was added to the Filesystem store driver. Several bugs 5 | were fixed and some code changes were committed to increase stability. 6 | features: 7 | - | 8 | A chunk size config option was added to the filesystem driver to allow 9 | some performance tweaking. The former hardcoded 64 KB is the current 10 | default value. 11 | deprecations: 12 | - | 13 | Removal of ``stores`` and ``default_store`` has been postponed until 14 | Train cycle to allow time to move multiple backends stores from being 15 | EXPERIMENTAL due to some unresolved issues with the feature. 16 | fixes: 17 | - | 18 | * Bug 1785641_: Fix Defaults for ConfigParser 19 | * Bug 1808456_: Catch rdb NoSpace Exception 20 | * Bug 1813092_: Fix some types in the FS and VMware drivers 21 | * Bug 1815335_: Do not raise StopIteration 22 | * Bug 1816721_: Fix python3 compatibility of rbd get_fsid 23 | 24 | .. _1785641: https://code.launchpad.net/bugs/1785641 25 | .. _1808456: https://code.launchpad.net/bugs/1808456 26 | .. _1813092: https://code.launchpad.net/bugs/1813092 27 | .. _1815335: https://code.launchpad.net/bugs/1815335 28 | .. _1816721: https://code.launchpad.net/bugs/1816721 29 | -------------------------------------------------------------------------------- /releasenotes/notes/add-store-weight-d443fbea8cc8d4c9.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | A `weight` option has been added to the store configuration definition. 5 | This allows configuring stores with *relative* weights to each other for 6 | sorting when an image exists in multiple stores. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/block-creating-encrypted-nfs-volumes-d0ff370ab762042e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | `Bug #1884482 `_: 5 | Blocked creation of images on encrypted nfs volumes when glance store 6 | is cinder. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-1620999-8b76a0ad14826197.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Now the ``project_domain_name`` parameter and the ``user_domain_name`` 5 | parameter are properly used by swift backends. Previously these two 6 | parameters were ignored and the ``*_domain_id`` parameters should be 7 | set to use a keystone domain different from the default one. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-1820817-0ee70781918d232e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Swift backend now can use custom CA bundle to verify SSL connection to 5 | Keystone without adding this bundle to global system ones. 6 | For this it re-uses the CA bundle specified as ``swift_store_cacert`` 7 | config option, so this bundle must verify both certificates of Swift and 8 | Keysotne API endpoints. 9 | 10 | For more details see 11 | [`bug 1820817 `_]. 12 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-1915602-fcc807a435d8a6bf.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Default value of the ``cinder_catalog_info`` parameter has been changed 5 | from ``volumev2::publicURL`` to ``volumev3::publicURL``, so that 6 | the current v3 API is used by default instead of the deprecated v2 API. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-1954883-3666d63a3c0233f1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | * Bug 1954883_: [RBD] Image is unusable if deletion fails 5 | 6 | .. _1954883: https://code.launchpad.net/bugs/1954883 7 | 8 | upgrade: 9 | - | 10 | Deployments which are using Ceph V2 clone feature (i.e. RBD backend for 11 | glance_store as well as cinder driver is RBD or nova is using RBD driver) 12 | and minimum ceph client version is greater than 'luminous' need to grant 13 | glance osd read access to the cinder and nova RBD pool. 14 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-2004555-4fd67fce86c07461.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | - | 3 | Cinder glance_store driver: in order to avoid a situation where a 4 | leftover device could be mapped to a different volume than the one 5 | intended, the cinder glance_store driver now instructs the os-brick 6 | library to force detach volumes, which ensures that devices are 7 | removed from the host. 8 | 9 | See `Bug #2004555 10 | `_ for more 11 | information about this issue. 12 | -------------------------------------------------------------------------------- /releasenotes/notes/cinder-fix-nfs-sparse-vol-create-76631ce05f86257c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | `Bug #2000584 `_: 5 | Fixed image create with cinder NFS store when using sparse volumes. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/cinder-nfs-block-qcow2-vol-4fed58b0afafc980.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | `Bug #1901138 `_: 5 | Blocked creation of images when glance store is cinder, 6 | cinder backend is nfs and volumes created are qcow2 format. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/cinder-support-extend-in-use-volume-c6292f950ff75cca.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Added support for extending in-use volumes in cinder store. 5 | A new boolean config option ``cinder_do_extend_attached`` is 6 | added which allows operators to enable/disable extending 7 | in-use volume support when creating an image. 8 | By default, ``cinder_do_extend_attached`` will be ``False`` 9 | i.e. old flow of detaching, extending and attaching will be 10 | used. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecate-rados_connect_timeout-767ed1eaa026196e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | The 'rados_connect_timeout' config option for the RBD store has been 5 | deprecated and will be removed in the future. It has been silently ignored 6 | for multiple releases. Users willing to set a timeout for the connection to 7 | the cluster can use Ceph's 'client_mount_timeout' option. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecate-sheepdog-driver-1f9689c327f313d4.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | The Sheepdog driver is deprecated in this release and is subject to 5 | removal at the beginning of the 'U' development cycle, following the 6 | `OpenStack standard deprecation policy 7 | `_. 8 | 9 | The driver is being removed because `Sheepdog is not maintained upstream 10 | `_. 11 | Additionally, the Sheepdog driver is no longer tested in the OpenStack 12 | gate. 13 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecate-store_add_to_backend-f419e5c4210613d2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | The glance_store function ``store_add_to_backend``, which is a 5 | wrapper around each store's ``add()`` method, is deprecated in this 6 | release and is subject to removal at the beginning of the Stein 7 | development cycle, following the `OpenStack standard deprecation policy 8 | `_. 9 | 10 | The function is replaced by ``store_add_to_backend_with_multihash``, 11 | which is a similar wrapper, but which takes an additional argument 12 | allowing a caller to specify an secure hashing algorithm. The 13 | hexdigest of this algorithm is returned as one of the multiple 14 | values returned by the function. The function also returns the 15 | md5 checksum for backward compatability. 16 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecate-store_capabilities_update_min_interval-039389fa296e2494.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | The glance_store configuration option 5 | ``store_capabilities_update_min_interval`` is deprecated in this 6 | release and is subject to removal at the beginning of the Stein 7 | development cycle, following the `OpenStack standard deprecation policy 8 | `_. 9 | 10 | The option configures a stub method that has not been implemented 11 | for any existing store drivers. Hence it is non-operational. Given 12 | that it has *never* been operational, it will not be missed. Its 13 | presence is confusing to operators and thus it is hereby deprecated 14 | for removal. 15 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecate-vmware-store-2f720c6074b843b0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | The VMWare Datastore has been deprecated. The vmwareapi virt driver in nova 5 | was marked as experimental due to lack of CI and maintainers and it may be 6 | removed in a future release. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/drop-py-2-7-345cafc9c1d3f892.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Python 2.7 support has been dropped. Last release of glance_store 5 | to support py2.7 is OpenStack Train. The minimum version of Python now 6 | supported by glance_store is Python 3.6. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/drop-python-3-6-and-3-7-41af87576c4fd7b1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Python 3.6 & 3.7 support has been dropped. The minimum version of Python now 5 | supported is Python 3.8. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-exception-logging-during-attach-9546e24189db83c4.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | `Bug #1970698 `_: 5 | Cinder: Fixed exception logging when the image create operation fails 6 | due to failing to attach volume to glance host. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-interval-in-retries-471155ff34d9f0e9.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | `Bug #1969373 `_: 5 | Cinder Driver: Correct the retry interval from fixed 1 second to 6 | exponential backoff for attaching a volume during image create/save 7 | operation. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-ip-in-connector-info-36b95d9959f10f63.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | `Bug #1955668 `_: 5 | Fixed issue with glance cinder store passing hostname instead of IP 6 | address to os-brick while getting connector information. -------------------------------------------------------------------------------- /releasenotes/notes/fix-legacy-image-update-49a149ec267dccb6.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | `Bug #2056179 `_: 5 | Cinder Store: Fix issue when updating legacy image location. 6 | Previously we only used the user context's credentials to make request 7 | to cinder which we have now updated to use the service credentials 8 | configured in the config file else use the user context's credentials. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-rados_connect_timeout-39e5074bc1a3b65b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | RBD driver: the ``rados_connect_timeout`` config option has been 5 | un-deprecated and its behavior has been improved. A value of ``0`` 6 | is now respected as disabling timeout in requests, while a value less 7 | than zero indicates that glance_store will not set a timeout but 8 | instead will use whatever timeouts are set in the Ceph configuration 9 | file. 10 | 11 | upgrade: 12 | - | 13 | RBD driver: the default value of the ``rados_connect_timeout`` option 14 | has been changed from 0 to -1, so that the RBD driver will by default 15 | use the timeout values defined in ``ceph.conf``. Be aware that 16 | setting this option to 0 disables timeouts (that is, the RBD driver 17 | will make requests with a timeout of zero, and all requests wait forever), 18 | thereby overriding any timeouts that are set in the Ceph configuration 19 | file. 20 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-rbd-lockup-3aa2bb86f7d29e19.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | A recent change to the RBD driver introduced a potential threading lockup 5 | when using native threads, and also a (process-)blocking call to an 6 | external library when using greenthreads. That change has been reverted 7 | until a better fix can be made. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-scaleio-download-image-2563cb2681895d0e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Cinder (Dell PowerFlex) `bug #2086759 5 | `_: 6 | Fixed issue when downloading an image. 7 | Previously, we used to fail trying to download an image when using 8 | Glance -> Cinder -> Dell PowerFlex configuration which is now fixed 9 | with better file handling for the volume device. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-wait-device-resize-c282940b71a3748e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | `Bug #1959913 `_: 5 | Added wait between the volume being extended and 6 | the new size being detected while opening the 7 | volume device. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/fs-drv-chunk-sz-a1b2f6a72fad92d5.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | The filesystem driver is now using a configurable chunk size. Increasing it 5 | may avoid bottlenecks. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/handle-sparse-image-a3ecfc4ae1c00d48.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Add new configuration option ``rbd_thin_provisioning`` and 5 | ``filesystem_thin_provisioning`` to rbd and filesystem 6 | store to enable or not sparse upload, default are False. 7 | 8 | A sparse file means that we do not actually write null byte sequences 9 | but only the data itself at a given offset, the "holes" which can 10 | appear will automatically be interpreted by the storage backend as 11 | null bytes, and do not really consume your storage. 12 | 13 | Enabling this feature will also speed up image upload and save 14 | network traffic in addition to save space in the backend, as null 15 | bytes sequences are not sent over the network. 16 | -------------------------------------------------------------------------------- /releasenotes/notes/improved-configuration-options-3635b56aba3072c9.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | Improved configuration options for glance_store. Please 4 | refer to the ``other`` section for more information. 5 | 6 | other: 7 | - The glance_store configuration options have been 8 | improved with detailed help texts, defaults for 9 | sample configuration files, explicit choices 10 | of values for operators to choose from, and a 11 | strict range defined with ``min`` and ``max`` 12 | boundaries. 13 | 14 | It is to be noted that the configuration options 15 | that take integer values now have a strict range defined 16 | with "min" and/or "max" boundaries where appropriate. This 17 | renders the configuration options incapable of taking certain 18 | values that may have been accepted before but were actually 19 | invalid. For example, configuration options specifying counts, 20 | where a negative value was undefined, would have still accepted 21 | the supplied negative value. Such options will no longer accept 22 | negative values. However, options where a negative value was 23 | previously defined (for example, -1 to mean unlimited) will 24 | remain unaffected by this change. 25 | 26 | Values that do not comply with the appropriate restrictions 27 | will prevent the service from starting. The logs will contain 28 | a message indicating the problematic configuration option and 29 | the reason why the supplied value has been rejected. 30 | -------------------------------------------------------------------------------- /releasenotes/notes/lock_path-cef9d6f5f52c3211.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | When using the cinder backend, a custom os-brick file lock location can be 5 | specified using the ``lock_path`` configuration option in the 6 | ``[os_brick]`` configuration section. Helpful when deploying on the same 7 | host as the Cinder service. 8 | upgrade: 9 | - | 10 | When running Cinder and Glance with Cinder backend on the same host an 11 | os-brick shared location can be configured using the ``lock_path`` in the 12 | ``[os_brick]`` configuration section. 13 | -------------------------------------------------------------------------------- /releasenotes/notes/move-rootwrap-config-f2cf435c548aab5c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - Packagers should be aware that the rootwrap configuration 4 | files have been moved from etc/ to etc/glance/ in order to 5 | be consistent with where other projects place these files. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/multi-store-0c004fc8aba2a25d.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | This release contains the base work for multiple back-ends changing how 4 | the back-ends get configured. Please note that in Rocky release the work 5 | is still experimental. Thus it's not advised to utilize the new configs in 6 | production environments before Stein release even though old config options 7 | are deprecated for removal. 8 | features: 9 | - | 10 | EXPERIMENTAL: Multiple back-end stores 11 | deprecations: 12 | - | 13 | 'stores' and 'default_store' config options have been deprecated for 14 | removal. As the replacements are experimental for Rocky release, migration 15 | away from these options in production environments is not advised before 16 | Stein release. 17 | -------------------------------------------------------------------------------- /releasenotes/notes/multi-tenant-store-058b67ce5b7f3bd0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - If using Swift in the multi-tenant mode for storing 4 | images in Glance, please note that the configuration 5 | options ``swift_store_multi_tenant`` and 6 | ``swift_store_config_file`` are now mutually exclusive 7 | and cannot be configured together. If you intend to 8 | use multi-tenant store, please make sure that you have 9 | not set a swift configuration file. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/multiattach-volume-handling-1a8446a64463f2cf.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | This release adds support for handling cinder's multiattach volumes 4 | in glance cinder store. 5 | features: 6 | - | 7 | Glance cinder store now supports handling of multiattach volumes. 8 | fixes: 9 | - | 10 | `Bug #1904546 `_: 11 | Fixed creating multiple instances/volumes from image if multiattach 12 | volumes are used. -------------------------------------------------------------------------------- /releasenotes/notes/multihash-support-629e9cbc283a8b47.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | This release adds support for Glance multihash computation. 4 | features: 5 | - | 6 | A new function, ``store_add_to_backend_with_multihash``, has been 7 | added. This function wraps each store's ``add`` method to provide 8 | consumers with a constant interface. It is similar to the existing 9 | ``store_add_to_backend`` function but requires the caller to 10 | specify an additional ``hashing_algo`` argument whose value is 11 | a hashlib algorithm identifier. The function returns a 5-tuple 12 | containing a ``multihash`` value, which is a hexdigest of the 13 | stored data computed using the specified hashing algorithm. 14 | -------------------------------------------------------------------------------- /releasenotes/notes/pike-relnote-9f547df14184d18c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | This was a quiet development cycle for the ``glance_store`` library. 4 | No new features were added. Several bugs were fixed and some code 5 | changes were committed to increase stability. 6 | fixes: 7 | - | 8 | The following bugs were fixed during the Pike release cycle. 9 | 10 | * Bug 1618666_: Fix SafeConfigParser DeprecationWarning in Python 3.2+ 11 | * Bug 1668848_: PBR 2.0.0 will break projects not using constraints 12 | * Bug 1657710_: Unit test passes only because is launched as non-root user 13 | * Bug 1686063_: RBD driver can't delete image with unprotected snapshot 14 | * Bug 1691132_: Fixed tests failing due to updated oslo.config 15 | * Bug 1693670_: Fix doc generation for Python3 16 | * Bug 1643516_: Cinder driver: TypeError in _open_cinder_volume 17 | * Bug 1620214_: Sheepdog: command execution failure 18 | 19 | .. _1618666: https://code.launchpad.net/bugs/1618666 20 | .. _1668848: https://code.launchpad.net/bugs/1668848 21 | .. _1657710: https://code.launchpad.net/bugs/1657710 22 | .. _1686063: https://code.launchpad.net/bugs/1686063 23 | .. _1691132: https://code.launchpad.net/bugs/1691132 24 | .. _1693670: https://code.launchpad.net/bugs/1693670 25 | .. _1643516: https://code.launchpad.net/bugs/1643516 26 | .. _1620214: https://code.launchpad.net/bugs/1620214 27 | 28 | other: 29 | - | 30 | The following improvements were made during the Pike release cycle. 31 | 32 | * `Fixed string formatting in log message 33 | `_ 34 | * `Correct error msg variable that could be unassigned 35 | `_ 36 | * `Use HostAddressOpt for store opts that accept IP and hostnames 37 | `_ 38 | * `Replace six.iteritems() with .items() 39 | `_ 40 | * `Add python 3.5 in classifier and envlist 41 | `_ 42 | * `Initialize privsep root_helper command 43 | `_ 44 | * `Documentation was reorganized according to the new standard layout 45 | `_ 46 | -------------------------------------------------------------------------------- /releasenotes/notes/prevent-unauthorized-errors-ebb9cf2236595cd0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | Prevent Unauthorized errors during uploading or 4 | donwloading data to Swift store. 5 | features: 6 | - Allow glance_store to refresh token when upload or download data to Swift 7 | store. glance_store identifies if token is going to expire soon when 8 | executing request to Swift and refresh the token. For multi-tenant swift 9 | store glance_store uses trusts, for single-tenant swift store glance_store 10 | uses credentials from swift store configurations. Please also note that 11 | this feature is enabled if and only if Keystone V3 API is available 12 | and enabled. -------------------------------------------------------------------------------- /releasenotes/notes/queens-relnote-5fa2d009d9a9e458.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | This was a quiet development cycle for the ``glance_store`` library. 4 | One new feature was added to the Swift store driver. Several bugs 5 | were fixed and some code changes were committed to increase stability. 6 | features: 7 | - | 8 | A `BufferedReader`_ has been added to the Swift store driver in 9 | order to enable better recovery from errors during uploads of large 10 | image files. Because this reader buffers image data, it could 11 | cause Glance to use a much larger amount of disk space, and so the 12 | Buffered Reader is *not* enabled by default. 13 | 14 | To use the new reader with the Swift store, you must do the following: 15 | 16 | * Set the ``glance_store`` configuration option ``swift_buffer_on_upload`` 17 | to ``True`` 18 | 19 | * Set the ``glance_store`` configuration option ``swift_upload_buffer_dir`` 20 | to a string value representing an absolute directory path. This 21 | directory will be used to hold the buffered data. 22 | 23 | The Buffered Reader works by taking advantage of the way Swift stores 24 | large objects by segmenting them into discrete chunks. Thus, the amount 25 | of disk space a Glance API node will require for buffering is a function 26 | of the ``swift_store_large_object_chunk_size`` setting and the number of 27 | worker threads (configured in **glance-api.conf** as the value of 28 | ``workers``). Disk utilization will cap at the following value 29 | 30 | swift_store_large_object_chunk_size * workers * 1000 31 | 32 | Be aware that depending upon how the file system is configured, the disk 33 | space used for buffering may decrease the actual disk space available for 34 | the Glance image cache, which may affect overall performance. 35 | 36 | For more information, see the `Buffered Reader for Swift Driver`_ spec. 37 | 38 | .. _BufferedReader: https://opendev.org/openstack/glance_store/commit/2e0024c85ca2ddf380014e44213be4fb876f680e 39 | .. _Buffered Reader for Swift Driver: http://specs.openstack.org/openstack/glance-specs/specs/mitaka/approved/buffered-reader-for-swift-driver.html 40 | fixes: 41 | - | 42 | * Bug 1738331_: Fix BufferedReader writing zero size chunks 43 | * Bug 1733502_: Use cached auth_ref instead of getting a new one each time 44 | 45 | .. _1738331: https://code.launchpad.net/bugs/1738331 46 | .. _1733502: https://code.launchpad.net/bugs/1733502 47 | upgrade: 48 | - | 49 | Two new configuration options, ``swift_buffer_on_upload`` and 50 | ``swift_upload_buffer_dir`` have been introduced. These apply only to 51 | users of the Swift store and their use is optional. See the New Features 52 | section for more information. 53 | -------------------------------------------------------------------------------- /releasenotes/notes/rbd-trash-snapshots-158a39da4248fb0c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | The RBD driver now moves images to the trash if they cannot be deleted 5 | immediately due to having snapshots. This fixes the long-standing issue 6 | where base images are unable to be deleted until/unless all snapshots of 7 | it are also deleted. Moving the image to the trash allows Glance to 8 | proceed with the deletion of the image (as far as it is concerned), mark 9 | the RBD image for deletion, which will happen once the last snapshot that 10 | uses it has been deleted. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/release-1.0.0-7ab43e91523eb3c8.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | The Glance Project Team is excited to announce the version 1.0.0 4 | release of the glance_store library. 5 | 6 | This release marks the finalization of changes introduced on an 7 | experimental basis in previous releases beginning with 0.25.0 to 8 | support the Glance `Multi-store backend support 9 | `_ 10 | feature. 11 | 12 | features: 13 | - | 14 | Multiple backend stores may be configured using the 15 | ``glance_store.multi_backend`` module. See the documentation of the 16 | ``create_multi_stores`` function in the `glance_store Reference Guide 17 | `_ 18 | for details. 19 | 20 | deprecations: 21 | - | 22 | The 'stores' and 'default_store' configuration options have been 23 | deprecated for removal since the OpenStack Rocky release. They are 24 | subject to removal early in the 'U' development cycle. When these 25 | options are removed, the ``glance_store.backend`` module, that depends 26 | on them, will be removed as well. 27 | 28 | upgrade: 29 | - | 30 | Consuming services should begin the transition away from the 31 | ``glance_store.backend`` module and instead use the 32 | ``glance_store.multi_backend`` module. The ``backend`` module is 33 | expected to be removed during the 'U' development cycle. 34 | 35 | issues: 36 | - | 37 | The responses from some functions in the ``glance_store.multi_backend`` 38 | module, which was EXPERIMENTAL until this release, have changed. 39 | In particular, the ``glance_store.driver.Store.add`` function which 40 | returns a tuple whose last element is a dictionary of storage system 41 | specific information, no longer contains a 'backend' key. Instead, 42 | this key is named 'store'. This change extends to any convenience 43 | functions that wrap ``Store.add``. 44 | 45 | Consumers relying upon the EXPERIMENTAL behavior should not upgrade 46 | past version 0.29.1. Now that the ``multi_backend`` module is fully 47 | supported in release 1.0.0, it will not undergo any more 48 | backward-incompatible changes. 49 | -------------------------------------------------------------------------------- /releasenotes/notes/release-1.0.1-098b1487ac8cc9a1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | other: 3 | - | 4 | In this version, refactor was made how registering of filesystem 5 | configuration options for reserved stores works. Consumer just 6 | need to pass the key:value pair where key represents the name of the 7 | reserved store and value represents the actual store driver, to the 8 | glance_store. 9 | issues: 10 | - | 11 | At the moment use of reserved stores is only limited to filesystem store 12 | driver. Also default ``filesystem_store_datadir`` path for these stores 13 | is set to ``/var/lib/glance/``, so with if you are using 14 | devstack for the deployment, you need to make sure you have appropriate 15 | permissions to create these reserved stores directories. 16 | -------------------------------------------------------------------------------- /releasenotes/notes/release-1.2.0-8d239f01cd8ff0bf.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Following bugs were fixed and included after 1.0.1 release: 5 | 6 | * Bug 1820817_: Swift backend can not use custom CA bundle to verify 7 | server SSL certs when those are not added to global system certs 8 | * Bug 1866966_: define mount_point for ``*fs`` drivers in glance cinder store 9 | * Bug 1863691_: When Image size greater than the chunk size and the glance 10 | buffered upload for swift is enabled, glance just put 0 bytes 11 | * Bug 1839778_: Python3 swift config quotes removal 12 | * Bug 1863983_: Image upload is failing with 13 | NoFibreChannelVolumeDeviceFound after configuring Cinder(HP3Par FC 14 | storage) as glance backend 15 | 16 | .. _1820817: https://bugs.launchpad.net/glance-store/+bug/1820817 17 | .. _1866966: https://bugs.launchpad.net/glance-store/+bug/1866966 18 | .. _1863691: https://bugs.launchpad.net/fuel/+bug/1863691 19 | .. _1839778: https://bugs.launchpad.net/glance-store/+bug/1839778 20 | .. _1863983: https://bugs.launchpad.net/glance-store/+bug/1863983 21 | 22 | 23 | other: 24 | - | 25 | The following improvements were made during the Ussuri release cycle: 26 | 27 | * Partial refactoring of cinder driver of glance store to use cinderclient 28 | version 3 and some methods have been moved to class level rather than 29 | use them as module level. 30 | * Droped support for python 2.7 and testing for the same. 31 | * Drop support for tempest-full 32 | -------------------------------------------------------------------------------- /releasenotes/notes/releasenote-0.17.0-efee3f557ea2096a.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | Some deprecated exceptions have been removed. See 4 | upgrade section for more details. 5 | upgrade: 6 | - The following list of exceptions have been deprecated 7 | since 0.10.0 release -- ``Conflict``, ``ForbiddenPublicImage`` 8 | ``ProtectedImageDelete``, ``BadDriverConfiguration``, 9 | ``InvalidRedirect``, ``WorkerCreationFailure``, 10 | ``SchemaLoadError``, ``InvalidObject``, 11 | ``UnsupportedHeaderFeature``, ``ImageDataNotFound``, 12 | ``InvalidParameterValue``, ``InvalidImageStatusTransition``. 13 | This release removes these exceptions so any remnant 14 | consumption of the same must be avoided/removed. 15 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-cinder-experimental-fbf9dea32c84dc9b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | From glance_store release 0.26.0 onwards Cinder driver is no longer 4 | considered as experimental. 5 | other: 6 | - | 7 | During Rocky cycle number of issues still warranting experimental status 8 | on Cinder back-end driver was addressed. The team considers the driver 9 | stable and production ready from Rocky release onwards (0.26.0). 10 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-gridfs-driver-09286e27613b4353.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | glance_store._drivers.gridfs 4 | deprecations: 5 | - The gridfs driver has been removed from the tree. 6 | The environments using this driver that were not 7 | migrated will stop working after the upgrade. -------------------------------------------------------------------------------- /releasenotes/notes/remove-py38-ad3b92513d4381e3.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Support for Python 3.8 has been removed. Now the minimum python version 5 | supported is 3.9 . 6 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-s3-driver-f432afa1f53ecdf8.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | glance_store._drivers.s3 removed from tree. 4 | upgrade: 5 | - The S3 driver has been removed completely from the 6 | glance_store source tree. All environments running 7 | and (or) using this s3-driver piece of code and have 8 | not been migrated will stop working after the upgrade. 9 | We recommend you use a different storage backend that 10 | is still being supported by Glance. The standard 11 | deprecation path has been used to remove this. The 12 | process requiring store driver maintainers was initiated 13 | at http://lists.openstack.org/pipermail/openstack-dev/2015-December/081966.html . 14 | Since, S3 driver did not get any maintainer, it was 15 | decided to remove it. 16 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-store-cap-update-min-interval-21fea4173ed4a09b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - The ``store_capabilities_update_min_interval`` configuration option, 4 | deprecated since the Rocky release, has been removed. The option 5 | configured a capability that was not implemented by any glance_store 6 | drivers. Thus its removal will have no impact on any deployments. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/rethinking-filesystem-access-5ab872fd0c0d27db.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Added keyword argument to ``register_store_opts`` and 5 | ``create_multi_stores`` calls to configure reserved stores by the consuming 6 | service. This feature will allow a mix of operator-configured stores via 7 | enabled_backends configuration option set in the [glance_store] section 8 | of the consuming service's configuration file, and stores that are 9 | reserved for use by the consuming service. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/rocky-bugfixes-adefa8f47db16a2d.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | * Bug 1606268_: Failure to upload to swift when keystone uses "insecure" SSL 5 | * Bug 1779455_: cinder backend causes BadRequest due to NULL mountpoint 6 | * Bug 1764200_: Glance Cinder backed images & multiple regions 7 | 8 | .. _1606268: https://bugs.launchpad.net/glance-store/+bug/1606268 9 | .. _1779455: https://bugs.launchpad.net/glance-store/+bug/1779455 10 | .. _1764200: https://bugs.launchpad.net/glance-store/+bug/1764200 11 | -------------------------------------------------------------------------------- /releasenotes/notes/set-documented-default-directory-for-filesystem-9b417a29416d3a94.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | other: 3 | - For years, `/var/lib/glance/images` has been presented as the default dir 4 | for the filesystem store. It was not part of the default value until now. 5 | New deployments and ppl overriding config files should watch for this. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/sorted-drivers-for-configs-a905f07d3bf9c973.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | Return list of store drivers in sorted order for 4 | generating configs. More info in ``Upgrade Notes`` 5 | and ``Bug Fixes`` section. 6 | upgrade: 7 | - This version of glance_store will result in Glance 8 | generating the configs in a sorted (deterministic) 9 | order. So, preferably store releases on or after this 10 | should be used for generating any new configs if the 11 | mismatched ordering of the configs results in an issue 12 | in your environment. 13 | fixes: 14 | - Bug 1619487 is fixed which was causing random order of 15 | the generation of configs in Glance. See ``upgrade`` 16 | section for more details. 17 | -------------------------------------------------------------------------------- /releasenotes/notes/start-using-reno-73ef709807e37b74.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | other: 3 | - Start using reno to manage release notes. 4 | -------------------------------------------------------------------------------- /releasenotes/notes/support-cinder-multiple-stores-6cc8489f8f4f8ff3.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Added support for cinder multiple stores. Operators can now configure 5 | multiple cinder stores by configuring a unique cinder_volume_type for 6 | each cinder store. 7 | upgrade: 8 | - | 9 | Legacy images will be moved to specific stores as per their current 10 | volume's type and the location URL will be updated respectively. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/support-cinder-upload-c85849d9c88bbd7e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - Implemented image uploading, downloading and deletion for cinder store. 4 | It also supports new settings to put image volumes into a specific project 5 | to hide them from users and to control them based on ACL of the images. 6 | Note that cinder store is currently considered experimental, so 7 | current deployers should be aware that the use of it in production right 8 | now may be risky. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/support-cinder-user-domain-420c76053dd50534.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | For the Cinder store, if using an internal user to store images, 5 | it is now possible to have the internal user and the internal project 6 | in Keystone domains other than the ``Default`` one. 7 | Two new config options ``cinder_store_user_domain_name`` and 8 | ``cinder_store_project_domain_name`` are added 9 | (both default to ``Default``) and now are possible to use in the 10 | configuration of the Cinder store. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/support-s3-driver-a4158f9fa35931d5.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Implemented S3 driver to use Amazon S3 or S3 compatible storage as Glance 5 | backend. This is a revival of the S3 driver supported up to Mitaka, with 6 | the addition of a multiple store support. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/update-stein-deprecations-3c2f6ffeab22b558.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Removal of the ``stores`` and ``default_store`` configuration options, 5 | which were deprecated in Rocky, has been postponed until during the 6 | Train development cycle. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/victoria-milestone-1-c1f9de5b90e8c326.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | * Bug 1875281_: API returns 503 if one of the store is mis-configured 5 | * Bug 1870289_: Add lock per share for cinder nfs mount/umount 6 | 7 | .. _1875281: https://bugs.launchpad.net/glance-store/+bug/1875281 8 | .. _1870289: https://bugs.launchpad.net/glance-store/+bug/1870289 9 | 10 | -------------------------------------------------------------------------------- /releasenotes/notes/vmware-store-requests-369485d2cfdb6175.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | security: 3 | - Previously the VMWare Datastore was using HTTPS Connections from httplib 4 | which do not verify the connection. By switching to using requests library 5 | the VMware storage backend now verifies HTTPS connection to vCenter server 6 | and thus addresses the vulnerabilities described in OSSN-0033. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/volume-type-validation-check-011a400d7fb3b307.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Previously, during service startup, the check to validate volume types 5 | used to raise ``BackendException`` or ``BadStoreConfiguration`` exceptions 6 | when an invalid volume type was configured hence failing the service 7 | startup. It now logs a warning and the glance service starts normally. 8 | fixes: 9 | - | 10 | `Bug #1915163 `_: 11 | Added handling to log and raise proper exception during image create when 12 | an invalid volume type is configured. 13 | -------------------------------------------------------------------------------- /releasenotes/notes/wallaby-final-release-00f0f851ff7d93ab.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | This was a quiet development cycle for the ``glance_store`` library. 4 | Several bugs were fixed and some code changes were committed to 5 | increase stability. 6 | fixes: 7 | - | 8 | * Bug 1915602_: Cinder store: Use v3 API by default 9 | 10 | .. _1915602: https://code.launchpad.net/bugs/1915602 -------------------------------------------------------------------------------- /releasenotes/notes/xena-final-release-3c6e19dfba43b40d.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | This was a quiet development cycle for the ``glance_store`` library. 4 | Several bugs were fixed and some code changes were committed to 5 | increase stability. 6 | fixes: 7 | - | 8 | * Bug 1926404_: HTTP 413 : Failed to add object to Swift. Got error from Swift 9 | * Bug 1885651_: swift_store_endpoint doesn't override keystone catalog 10 | * Bug 1934849_: s3 backend takes time exponentially 11 | 12 | .. _1926404: https://code.launchpad.net/bugs/1926404 13 | .. _1885651: https://code.launchpad.net/bugs/1885651 14 | .. _1934849: https://code.launchpad.net/bugs/1934849 15 | -------------------------------------------------------------------------------- /releasenotes/source/2023.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2023.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2023.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/2023.2.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2023.2 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2023.2 7 | -------------------------------------------------------------------------------- /releasenotes/source/2024.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2024.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2024.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/2024.2.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2024.2 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2024.2 7 | -------------------------------------------------------------------------------- /releasenotes/source/2025.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2025.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2025.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/_static/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/releasenotes/source/_static/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/_templates/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/glance_store/8ac98ddf6170f66d4bdefac159b633c713a75dd5/releasenotes/source/_templates/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/index.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Glance_store Release Notes 3 | ============================ 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | unreleased 9 | 2025.1 10 | 2024.2 11 | 2024.1 12 | 2023.2 13 | 2023.1 14 | zed 15 | yoga 16 | xena 17 | wallaby 18 | victoria 19 | ussuri 20 | train 21 | stein 22 | rocky 23 | queens 24 | pike 25 | ocata 26 | newton 27 | mitaka 28 | liberty 29 | -------------------------------------------------------------------------------- /releasenotes/source/liberty.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Liberty Series Release Notes 3 | ============================== 4 | 5 | .. release-notes:: 6 | :branch: origin/stable/liberty 7 | -------------------------------------------------------------------------------- /releasenotes/source/locale/de/LC_MESSAGES/releasenotes.po: -------------------------------------------------------------------------------- 1 | # Andreas Jaeger , 2019. #zanata 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: glance_store\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2019-12-19 00:11+0000\n" 7 | "MIME-Version: 1.0\n" 8 | "Content-Type: text/plain; charset=UTF-8\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | "PO-Revision-Date: 2019-10-01 06:30+0000\n" 11 | "Last-Translator: Andreas Jaeger \n" 12 | "Language-Team: German\n" 13 | "Language: de\n" 14 | "X-Generator: Zanata 4.3.3\n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 16 | 17 | msgid "0.11.0" 18 | msgstr "0.11.0" 19 | 20 | msgid "0.12.0" 21 | msgstr "0.12.0" 22 | 23 | msgid "0.16.0" 24 | msgstr "0.16.0" 25 | 26 | msgid "0.17.0" 27 | msgstr "0.17.0" 28 | 29 | msgid "0.18.0-5" 30 | msgstr "0.18.0-5" 31 | 32 | msgid "0.19.0" 33 | msgstr "0.19.0" 34 | 35 | msgid "0.21.0" 36 | msgstr "0.21.0" 37 | 38 | msgid "0.23.0" 39 | msgstr "0.23.0" 40 | 41 | msgid "0.25.0" 42 | msgstr "0.25.0" 43 | 44 | msgid "0.26.0" 45 | msgstr "0.26.0" 46 | 47 | msgid "0.26.1" 48 | msgstr "0.26.1" 49 | 50 | msgid "0.28.0" 51 | msgstr "0.28.0" 52 | 53 | msgid "0.29.0" 54 | msgstr "0.29.0" 55 | 56 | msgid "0.29.1" 57 | msgstr "0.29.1" 58 | 59 | msgid "1.0.0" 60 | msgstr "1.0.0" 61 | 62 | msgid "1.0.1" 63 | msgstr "1.0.1" 64 | 65 | msgid "Bug Fixes" 66 | msgstr "Fehlerkorrekturen" 67 | 68 | msgid "Critical Issues" 69 | msgstr "Kritische Probleme" 70 | 71 | msgid "Current Series Release Notes" 72 | msgstr "Aktuelle Serie Releasenotes" 73 | 74 | msgid "Deprecation Notes" 75 | msgstr "Ablaufwarnungen" 76 | 77 | msgid "Glance_store Release Notes" 78 | msgstr "Glance_store Releasenotes" 79 | 80 | msgid "Known Issues" 81 | msgstr "Bekannte Probleme" 82 | 83 | msgid "Liberty Series Release Notes" 84 | msgstr "Liberty Serie Releasenotes" 85 | 86 | msgid "Mitaka Series Release Notes" 87 | msgstr "Mitaka Serie Releasenotes" 88 | 89 | msgid "New Features" 90 | msgstr "Neue Funktionen" 91 | 92 | msgid "Newton Series Release Notes" 93 | msgstr "Newton Serie Releasenotes" 94 | 95 | msgid "Ocata Series Release Notes" 96 | msgstr "Ocata Serie Releasenotes" 97 | 98 | msgid "Other Notes" 99 | msgstr "Andere Notizen" 100 | 101 | msgid "Pike Series Release Notes" 102 | msgstr "Pike Serie Releasenotes" 103 | 104 | msgid "Prelude" 105 | msgstr "Einleitung" 106 | 107 | msgid "Queens Series Release Notes" 108 | msgstr "Queens Serie Releasenotes" 109 | 110 | msgid "Rocky Series Release Notes" 111 | msgstr "Rocky Serie Releasenotes" 112 | 113 | msgid "Security Issues" 114 | msgstr "Sicherheitsrelevante Probleme" 115 | 116 | msgid "Start using reno to manage release notes." 117 | msgstr "Reno wird für die Verwaltung der Releasenotes verwendet." 118 | 119 | msgid "Stein Series Release Notes" 120 | msgstr "Stein Serie Releasenotes" 121 | 122 | msgid "Train Series Release Notes" 123 | msgstr "Train Serie Releasenotes" 124 | 125 | msgid "Upgrade Notes" 126 | msgstr "Aktualisierungsnotizen" 127 | 128 | msgid "glance_store._drivers.gridfs" 129 | msgstr "glance_store._drivers.gridfs" 130 | 131 | msgid "glance_store._drivers.s3 removed from tree." 132 | msgstr "glance_store._drivers.s3 removed from tree." 133 | -------------------------------------------------------------------------------- /releasenotes/source/locale/zh_CN/LC_MESSAGES/releasenotes.po: -------------------------------------------------------------------------------- 1 | # zzxwill , 2016. #zanata 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: Glance_store Release Notes\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2018-02-28 18:24+0000\n" 7 | "MIME-Version: 1.0\n" 8 | "Content-Type: text/plain; charset=UTF-8\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | "PO-Revision-Date: 2016-08-23 02:05+0000\n" 11 | "Last-Translator: zzxwill \n" 12 | "Language-Team: Chinese (China)\n" 13 | "Language: zh_CN\n" 14 | "X-Generator: Zanata 4.3.3\n" 15 | "Plural-Forms: nplurals=1; plural=0\n" 16 | 17 | msgid "0.11.0" 18 | msgstr "0.11.0" 19 | 20 | msgid "0.12.0" 21 | msgstr "0.12.0" 22 | 23 | msgid "0.16.0" 24 | msgstr "0.16.0" 25 | 26 | msgid "0.17.0" 27 | msgstr "0.17.0" 28 | 29 | msgid "Current Series Release Notes" 30 | msgstr "当前版本发布说明" 31 | 32 | msgid "Deprecation Notes" 33 | msgstr "弃用说明" 34 | 35 | msgid "Glance_store Release Notes" 36 | msgstr "Glance_store发布说明" 37 | 38 | msgid "Liberty Series Release Notes" 39 | msgstr "Liberty版本发布说明" 40 | 41 | msgid "Mitaka Series Release Notes" 42 | msgstr "Mitaka 版本发布说明" 43 | 44 | msgid "New Features" 45 | msgstr "新特性" 46 | 47 | msgid "Other Notes" 48 | msgstr "其他说明" 49 | 50 | msgid "Security Issues" 51 | msgstr "安全问题" 52 | 53 | msgid "Start using reno to manage release notes." 54 | msgstr "开始使用reno管理发布说明。" 55 | 56 | msgid "" 57 | "The following list of exceptions have been deprecated since 0.10.0 release " 58 | "-- ``Conflict``, ``ForbiddenPublicImage`` ``ProtectedImageDelete``, " 59 | "``BadDriverConfiguration``, ``InvalidRedirect``, ``WorkerCreationFailure``, " 60 | "``SchemaLoadError``, ``InvalidObject``, ``UnsupportedHeaderFeature``, " 61 | "``ImageDataNotFound``, ``InvalidParameterValue``, " 62 | "``InvalidImageStatusTransition``. This release removes these exceptions so " 63 | "any remnant consumption of the same must be avoided/removed." 64 | msgstr "" 65 | "以下的异常列表自0.10.0版本后已经弃用了 ——``Conflict``, " 66 | "``ForbiddenPublicImage`` ``ProtectedImageDelete``, " 67 | "``BadDriverConfiguration``, ``InvalidRedirect``, ``WorkerCreationFailure``, " 68 | "``SchemaLoadError``, ``InvalidObject``, ``UnsupportedHeaderFeature``, " 69 | "``ImageDataNotFound``, ``InvalidParameterValue``, " 70 | "``InvalidImageStatusTransition``。该版本移除了这些异常,所以任何遗留的相同的" 71 | "使用方式必须避免或去掉。" 72 | 73 | msgid "Upgrade Notes" 74 | msgstr "升级说明" 75 | 76 | msgid "glance_store._drivers.gridfs" 77 | msgstr "glance_store._drivers.gridfs" 78 | 79 | msgid "glance_store._drivers.s3 removed from tree." 80 | msgstr "glance_store._drivers.s3从树上移除了。" 81 | -------------------------------------------------------------------------------- /releasenotes/source/mitaka.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Mitaka Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: origin/stable/mitaka 7 | -------------------------------------------------------------------------------- /releasenotes/source/newton.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Newton Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: origin/stable/newton 7 | -------------------------------------------------------------------------------- /releasenotes/source/ocata.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Ocata Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: origin/stable/ocata 7 | -------------------------------------------------------------------------------- /releasenotes/source/pike.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Pike Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/pike 7 | -------------------------------------------------------------------------------- /releasenotes/source/queens.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Queens Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/queens 7 | -------------------------------------------------------------------------------- /releasenotes/source/rocky.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Rocky Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/rocky 7 | -------------------------------------------------------------------------------- /releasenotes/source/stein.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Stein Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/stein 7 | -------------------------------------------------------------------------------- /releasenotes/source/train.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Train Series Release Notes 3 | ========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/train 7 | -------------------------------------------------------------------------------- /releasenotes/source/unreleased.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Current Series Release Notes 3 | ============================== 4 | 5 | .. release-notes:: 6 | -------------------------------------------------------------------------------- /releasenotes/source/ussuri.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Ussuri Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/ussuri 7 | -------------------------------------------------------------------------------- /releasenotes/source/victoria.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Victoria Series Release Notes 3 | ============================= 4 | 5 | .. release-notes:: 6 | :branch: victoria-eom 7 | -------------------------------------------------------------------------------- /releasenotes/source/wallaby.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Wallaby Series Release Notes 3 | ============================ 4 | 5 | .. release-notes:: 6 | :branch: wallaby-eom 7 | -------------------------------------------------------------------------------- /releasenotes/source/xena.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Xena Series Release Notes 3 | ========================= 4 | 5 | .. release-notes:: 6 | :branch: xena-eom 7 | -------------------------------------------------------------------------------- /releasenotes/source/yoga.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Yoga Series Release Notes 3 | ========================= 4 | 5 | .. release-notes:: 6 | :branch: yoga-eom 7 | -------------------------------------------------------------------------------- /releasenotes/source/zed.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Zed Series Release Notes 3 | ======================== 4 | 5 | .. release-notes:: 6 | :branch: zed-eom 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements lower bounds listed here are our best effort to keep them up to 2 | # date but we do not test them so no guarantee of having them all correct. If 3 | # you find any incorrect lower bounds, let us know or propose a fix. 4 | 5 | oslo.config>=5.2.0 # Apache-2.0 6 | oslo.i18n>=3.15.3 # Apache-2.0 7 | oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 8 | oslo.utils>=4.7.0 # Apache-2.0 9 | oslo.concurrency>=3.26.0 # Apache-2.0 10 | stevedore>=1.20.0 # Apache-2.0 11 | eventlet!=0.18.3,!=0.20.1,>=0.18.2 # MIT 12 | 13 | jsonschema>=3.2.0 # MIT 14 | keystoneauth1>=3.4.0 # Apache-2.0 15 | python-keystoneclient>=3.8.0 # Apache-2.0 16 | requests>=2.14.2 # Apache-2.0 17 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = glance_store 3 | summary = OpenStack Image Service Store Library 4 | description_file = 5 | README.rst 6 | author = OpenStack 7 | author_email = openstack-discuss@lists.openstack.org 8 | home_page = https://docs.openstack.org/glance_store/latest/ 9 | python_requires = >=3.9 10 | classifier = 11 | Development Status :: 5 - Production/Stable 12 | Environment :: OpenStack 13 | Intended Audience :: Developers 14 | Intended Audience :: Information Technology 15 | License :: OSI Approved :: Apache Software License 16 | Operating System :: POSIX :: Linux 17 | Programming Language :: Python 18 | Programming Language :: Python :: Implementation :: CPython 19 | Programming Language :: Python :: 3 :: Only 20 | Programming Language :: Python :: 3 21 | Programming Language :: Python :: 3.9 22 | Programming Language :: Python :: 3.10 23 | Programming Language :: Python :: 3.11 24 | Programming Language :: Python :: 3.12 25 | 26 | [files] 27 | packages = 28 | glance_store 29 | data_files = 30 | etc/glance = 31 | etc/glance/rootwrap.conf 32 | etc/glance/rootwrap.d = 33 | etc/glance/rootwrap.d/glance_cinder_store.filters 34 | 35 | [entry_points] 36 | glance_store.drivers = 37 | file = glance_store._drivers.filesystem:Store 38 | http = glance_store._drivers.http:Store 39 | swift = glance_store._drivers.swift:Store 40 | rbd = glance_store._drivers.rbd:Store 41 | cinder = glance_store._drivers.cinder:Store 42 | vmware = glance_store._drivers.vmware_datastore:Store 43 | s3 = glance_store._drivers.s3:Store 44 | 45 | # TESTS ONLY 46 | no_conf = glance_store.tests.fakes:UnconfigurableStore 47 | 48 | # Backwards compatibility 49 | glance.store.filesystem.Store = glance_store._drivers.filesystem:Store 50 | glance.store.http.Store = glance_store._drivers.http:Store 51 | glance.store.swift.Store = glance_store._drivers.swift:Store 52 | glance.store.rbd.Store = glance_store._drivers.rbd:Store 53 | glance.store.cinder.Store = glance_store._drivers.cinder:Store 54 | glance.store.vmware_datastore.Store = glance_store._drivers.vmware_datastore:Store 55 | glance.store.s3.Store = glance_store._drivers.s3:Store 56 | 57 | oslo.config.opts = 58 | glance.store = glance_store.backend:_list_opts 59 | glance.multi_store = glance_store.multi_backend:_list_config_opts 60 | 61 | console_scripts = 62 | glance-rootwrap = oslo_rootwrap.cmd:main 63 | 64 | [extras] 65 | # Dependencies for each of the optional stores 66 | vmware = 67 | oslo.vmware>=3.6.0 # Apache-2.0 68 | swift = 69 | python-swiftclient>=3.2.0 # Apache-2.0 70 | cinder = 71 | python-cinderclient>=4.1.0 # Apache-2.0 72 | os-brick>=6.3.0 # Apache-2.0 73 | oslo.rootwrap>=5.8.0 # Apache-2.0 74 | oslo.privsep>=1.23.0 # Apache-2.0 75 | s3 = 76 | boto3>=1.9.199 # Apache-2.0 77 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import setuptools 17 | 18 | setuptools.setup( 19 | setup_requires=['pbr>=2.0.0'], 20 | pbr=True) 21 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | hacking>=6.1.0,<6.2.0 # Apache-2.0 2 | 3 | # Documentation style 4 | doc8>=0.6.0 # Apache-2.0 5 | 6 | # Packaging 7 | 8 | # Unit testing 9 | coverage!=4.4,>=4.0 # Apache-2.0 10 | ddt>=1.4.4 # MIT 11 | fixtures>=3.0.0 # Apache-2.0/BSD 12 | python-subunit>=1.0.0 # Apache-2.0/BSD 13 | requests-mock>=1.2.0 # Apache-2.0 14 | retrying>=1.3.3 15 | stestr>=2.0.0 # Apache-2.0 16 | testscenarios>=0.4 # Apache-2.0/BSD 17 | testtools>=2.2.0 # MIT 18 | oslotest>=3.2.0 # Apache-2.0 19 | 20 | # Dependencies for each of the optional stores 21 | boto3>=1.9.199 # Apache-2.0 22 | oslo.vmware>=3.6.0 # Apache-2.0 23 | httplib2>=0.9.1 # MIT 24 | python-swiftclient>=3.2.0 # Apache-2.0 25 | python-cinderclient>=4.1.0 # Apache-2.0 26 | os-brick>=2.6.0 # Apache-2.0 27 | oslo.rootwrap>=5.8.0 # Apache-2.0 28 | oslo.privsep>=1.23.0 # Apache-2.0 29 | -------------------------------------------------------------------------------- /tools/with_venv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)} 3 | VENV_PATH=${VENV_PATH:-${TOOLS_PATH}} 4 | VENV_DIR=${VENV_NAME:-/../.venv} 5 | TOOLS=${TOOLS_PATH} 6 | VENV=${VENV:-${VENV_PATH}/${VENV_DIR}} 7 | source ${VENV}/bin/activate && "$@" 8 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 3.1.1 3 | envlist = py39,pep8 4 | ignore_basepython_conflict = True 5 | 6 | [testenv] 7 | basepython = python3 8 | setenv = VIRTUAL_ENV={envdir} 9 | usedevelop = True 10 | deps = 11 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 12 | -r{toxinidir}/requirements.txt 13 | -r{toxinidir}/test-requirements.txt 14 | passenv = OS_TEST_* 15 | commands = stestr run --slowest {posargs} 16 | 17 | [testenv:docs] 18 | deps = 19 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 20 | -r{toxinidir}/doc/requirements.txt 21 | commands = sphinx-build -W -b html doc/source doc/build/html 22 | 23 | [testenv:releasenotes] 24 | deps = 25 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 26 | -r{toxinidir}/doc/requirements.txt 27 | commands = sphinx-build -a -E -W -d releasenotes/build/.doctrees -b html releasenotes/source releasenotes/build/html 28 | 29 | [testenv:pep8] 30 | commands = 31 | flake8 {posargs} 32 | doc8 {posargs} 33 | 34 | [testenv:cover] 35 | setenv = 36 | PYTHON=coverage run --source glance_store --parallel-mode 37 | commands = 38 | stestr run {posargs} 39 | coverage combine 40 | coverage html -d cover 41 | coverage xml -o cover/coverage.xml 42 | 43 | [testenv:venv] 44 | commands = {posargs} 45 | 46 | # See glance_store/tests/functional/README.rst for information on writing or 47 | # running functional tests. 48 | [testenv:functional-swift] 49 | sitepackages = True 50 | commands = 51 | stestr run --slowest --test-path=./glance_store/tests/functional/swift 52 | 53 | [testenv:functional-filesystem] 54 | commands = 55 | stestr run --slowest --test-path=./glance_store/tests/functional/filesystem 56 | 57 | [doc8] 58 | ignore-path = .venv,.git,.tox,*glance_store/locale*,*lib/python*,glance_store.egg*,doc/build,*requirements.txt 59 | 60 | [flake8] 61 | # TODO(dmllr): Analyze or fix the warnings blacklisted below 62 | # H301 one import per line 63 | # H404 multi line docstring should start with a summary 64 | # H405 multi line docstring summary not separated with an empty line 65 | # W503 line break before binary operator 66 | # W504 line break after binary operator 67 | ignore = H301,H404,H405,W503,W504 68 | exclude = .venv,.git,.tox,dist,doc,etc,*glance_store/locale*,*lib/python*,*egg,build 69 | 70 | --------------------------------------------------------------------------------