Add blockbox to Cinder project
This change creates a contrib directory in the Cinder tree and adds a project called block-box. This addition provides a method to easily create Docker images for a standalone Cinder deployment and employs a docker-compose file to enable rapid build and deployment of Cinder as a standlone service. Change-Id: Id08ffefd322e80a433b58d48b8b89b1ca1695150
This commit is contained in:
parent
c220f751e9
commit
db82e4450a
1
contrib/block-box/.gitignore
vendored
Normal file
1
contrib/block-box/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
db-data/*
|
201
contrib/block-box/LICENSE
Normal file
201
contrib/block-box/LICENSE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
17
contrib/block-box/Makefile
Normal file
17
contrib/block-box/Makefile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
CINDER_BRANCH ?= master # master, stable/ocata, refs/changes/67/418167/1
|
||||||
|
NAME_PREFIX ?= ""
|
||||||
|
PLATFORM ?= debian # ubuntu, centos
|
||||||
|
TAG ?= latest
|
||||||
|
|
||||||
|
all: base lvm devbox
|
||||||
|
|
||||||
|
base:
|
||||||
|
docker build https://git.openstack.org/openstack/loci-cinder.git\#:$(PLATFORM) --tag cinder:$(TAG) --build-arg PROJECT_REF="stable/ocata"
|
||||||
|
|
||||||
|
lvm:
|
||||||
|
docker build -t cinder-lvm -f ./docker_files/Dockerfile.cinder-lvm .
|
||||||
|
|
||||||
|
devbox:
|
||||||
|
cp ../../test-requirements.txt ./docker_files/
|
||||||
|
docker build -t cinder-devenv -f ./docker_files/Dockerfile.cinder-devenv .
|
||||||
|
rm ./docker_files/test-requirements.txt
|
49
contrib/block-box/README.CVOL-LVM.md
Normal file
49
contrib/block-box/README.CVOL-LVM.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
You'll need to modify how you're doing things to get to the iscsi Target.
|
||||||
|
Specifically, using a Docker network hoses everything because the IP of the
|
||||||
|
target is the internal containers IP NOT the IP of the host.
|
||||||
|
|
||||||
|
Setting `network_mode: host` solves this.. but that creates a new problem.
|
||||||
|
Can't use `link` when using network_mode: host.
|
||||||
|
|
||||||
|
Sigh... so; docker run has "add-host=host:IP" that we should be able to find
|
||||||
|
equivalent in compose. We just need to define a network and assign IP's to the
|
||||||
|
other containers, then this should work.
|
||||||
|
|
||||||
|
Compose looks like this:
|
||||||
|
extra_hosts:
|
||||||
|
- "hostname:1.1.1.1"
|
||||||
|
- "anotherhost:2.2.2.2"
|
||||||
|
|
||||||
|
This just adds entries to /etc/hosts for you. Kinda handy
|
||||||
|
|
||||||
|
So, if we create a network and assign IP's to the supporting cast (rabbit,
|
||||||
|
mariadb api etc etc) we can then just use this to make them accessible instead
|
||||||
|
of using `link`
|
||||||
|
|
||||||
|
OHHHH! Add `ipc_mode: host`, shared memory; may speed things up a bit?
|
||||||
|
|
||||||
|
Finally... for reference; The docker run command for this looks something
|
||||||
|
like:
|
||||||
|
`docker run -it \
|
||||||
|
-v /dev/:/dev/ \
|
||||||
|
-v /run/:/run/:shared -v
|
||||||
|
/etc/localtime:/etc/localtime:ro \
|
||||||
|
--net host \
|
||||||
|
--privileged cinder_debian \
|
||||||
|
bash`
|
||||||
|
|
||||||
|
### https://wiki.debian.org/LVM
|
||||||
|
vim /etc/lvm/lvm.conf
|
||||||
|
# disable udev_ stuff
|
||||||
|
/usr/sbin/tgtd
|
||||||
|
tgtadm --lld iscsi --op show --mode target
|
||||||
|
tgtadm --lld iscsi --op new --mode target --tid 1 -T iqn.2001-04.com.example:storage.disk2.amiens.sys1.xyz
|
||||||
|
tgtadm --lld iscsi --mode logicalunit --op new --tid 1 --lun 1 -b /dev/vg-group/lv
|
||||||
|
tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
|
||||||
|
|
||||||
|
## Notes here: https://wiki.debian.org/SAN/iSCSI/open-iscsi
|
||||||
|
|
||||||
|
### Initiator side
|
||||||
|
iscsiadm -m discovery -t sendtargets -p <portalip>
|
||||||
|
iscsiadm -m node --targetname=<targetname> --login
|
||||||
|
|
206
contrib/block-box/README.md
Normal file
206
contrib/block-box/README.md
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
# block-box
|
||||||
|
Standalone Cinder Containerized using Docker Compose
|
||||||
|
|
||||||
|
## Cinder
|
||||||
|
Provides Block Storage as a service as part of the OpenStack Project.
|
||||||
|
This project deployes Cinder in containers using docker-compose and
|
||||||
|
also enabled the use of Cinder's noauth option which eliminates the
|
||||||
|
need for keystone. One could also easily add keystone into the
|
||||||
|
compose file along with an init script to set up endpoints.
|
||||||
|
|
||||||
|
## LOCI (Lightweight Open Compute Initiative)
|
||||||
|
The block-box uses OpenStack Loci to build a base Cinder image to use
|
||||||
|
for each service. The examples use Debian as the base OS, but you can
|
||||||
|
choose between Debian, CentOS and Ubuntu.
|
||||||
|
|
||||||
|
We're currently using Cinder's noauth option, but this pattern provides
|
||||||
|
flexibility to add a Keystone service if desired.
|
||||||
|
|
||||||
|
## To build
|
||||||
|
Start by building the required images. This repo includes a Makefile to
|
||||||
|
enable building of openstack/loci images of Cinder. The
|
||||||
|
Makefile includes variables to select between platform (debian, ubuntu or
|
||||||
|
centos) and also allows what branch of each project to biuld the image from.
|
||||||
|
This includes master, stable/xyz as well as patch versions. Additional
|
||||||
|
variables are provided and can be passed to make using the `-e` option to
|
||||||
|
control things like naming and image tags. See the Makefile for more info.
|
||||||
|
|
||||||
|
If you're going to utilize an external storage device (ie not using LVM), all
|
||||||
|
you need to build is the base Cinder image. Set the varialbe in the Makefile
|
||||||
|
to choose the Cinder Branch you'd like to use and Platforma then simply run:
|
||||||
|
|
||||||
|
```make base```
|
||||||
|
|
||||||
|
You can also build an image to run LVM:
|
||||||
|
|
||||||
|
```make lvm```
|
||||||
|
|
||||||
|
Finally the last image is a devenv image that will mount the cinder repo you've
|
||||||
|
checked out into a container and includes test-requirements.
|
||||||
|
|
||||||
|
```make base```
|
||||||
|
|
||||||
|
For more information and options, check out the openstack/loci page on github:
|
||||||
|
https://github.com/openstack/loci.
|
||||||
|
|
||||||
|
**NOTE** The loci project is moving fairly quickly, and it may or may not
|
||||||
|
continue to be a straight forward light weight method of building conatiner
|
||||||
|
Images. The build has been known to now work at times, and if it becomes
|
||||||
|
bloated or burdonsome it's easy to swap in another image builder (or write your
|
||||||
|
own even).
|
||||||
|
|
||||||
|
This will result in some base images that we'lluse:
|
||||||
|
cinder (openstack/loci image)
|
||||||
|
cinder-lvm (special cinder image with LVM config)
|
||||||
|
cinder-devenv (provides a Cinder development env container)
|
||||||
|
|
||||||
|
### cinder
|
||||||
|
Creates a base image with cinder installed via source. This base image is
|
||||||
|
enough to run all of the services including api, scheudler and Volume with
|
||||||
|
the exception of cinder-volume with the LVM driver which needs some extra
|
||||||
|
packages installed like LVM2 and iSCSI target driver.
|
||||||
|
|
||||||
|
Each Cinder service has an executable entrypoint at /usr/local/bin.
|
||||||
|
|
||||||
|
NOTE
|
||||||
|
If you choose to build images from something other than the default Debian
|
||||||
|
base, you'll need to modify the Dockerfile for this image as well.
|
||||||
|
|
||||||
|
### cinder-lvm
|
||||||
|
This is a special image that is built from the base cinder image and adds the
|
||||||
|
necessary packages for LVM and iSCSI.
|
||||||
|
|
||||||
|
### cinder-devenv
|
||||||
|
You might want to generate a conf file, or if you're like me, use Docker to do
|
||||||
|
some of your Cinder development. You can run this container which has all of
|
||||||
|
the current development packages and python test-requirements for Cinder.
|
||||||
|
|
||||||
|
You can pass in your current source directory from your local machine using -v
|
||||||
|
in your run command, here's a trival example that generates a sample config
|
||||||
|
file. Note we don't use tox because we're already in an isolated environment.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker run -it -v /home/jgriffith/src/cinder:/cinder \
|
||||||
|
cinder-devenv \
|
||||||
|
bash -c "cd cinder && oslo-config-generator \
|
||||||
|
--config-file=cinder/config/cinder-config-generator.conf"
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep in mind the command will execute and then exit, the result is written to
|
||||||
|
the cinder directory specified in the -v argument. In this example for
|
||||||
|
instance the result would be a newly generated cinder.conf.sample file in
|
||||||
|
/home/jgriffith/src/cinder/etc/cinder
|
||||||
|
|
||||||
|
## Accessing via cinderclient
|
||||||
|
You can of course build a cinderclient container with a `cinder` entrypoint and
|
||||||
|
use that for acces, but in order to take advantage of things like the
|
||||||
|
local-attach extension, you'll need to install the client tools on the host.
|
||||||
|
|
||||||
|
The current release version in pypi doesn't include noauth
|
||||||
|
support, so you'll need to install from source, but that's not hard:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo pip install pytz
|
||||||
|
sudo pip install git+https://github.com/openstack/python-cinderclient
|
||||||
|
sudo pip install git+https://github.com/openstack/python-brick-cinderclient-ext
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, you can source the included cinder.rc file to use the client to
|
||||||
|
communicate with your containerized cinder deployment, with noauth!!
|
||||||
|
|
||||||
|
Remember, to perform local-attach/local-detach of volumes you'll need to use
|
||||||
|
sudo. To preserve your env variables don't forget to use `sudo -E cinder xxxxx`
|
||||||
|
|
||||||
|
## To run
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
Don't forget to modify the etc-cinder/cinder.conf file as needed for your
|
||||||
|
specific driver. We'll be adding support for the LVM driver and LIO Tgts
|
||||||
|
shortly, but for now you won't have much luck without using an external
|
||||||
|
device (no worries, there are over 80 to choose from).
|
||||||
|
|
||||||
|
## Adding your own driver
|
||||||
|
We don't do multi-backend in this type of environment; instead we just add
|
||||||
|
another container running the backend we want. We can easily add to the base
|
||||||
|
service we've create using additional compose files.
|
||||||
|
|
||||||
|
The file `docker-compose-add-vol-service.yml` provides an example additional
|
||||||
|
compose file that will create another cinder-volume service configured to run
|
||||||
|
the SolidFire backend.
|
||||||
|
|
||||||
|
After launching the main compose file:
|
||||||
|
```shell
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the services are initialized and the database is synchronized, you can add
|
||||||
|
another backend by running:
|
||||||
|
```shell
|
||||||
|
docker-compose -f ./docker-compose-add-vol-service.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that things like network settings and ports are IMPORTANT here!!
|
||||||
|
|
||||||
|
## Access using the cinderclient container
|
||||||
|
|
||||||
|
You can use your own cinderclient and openrc, or use the provided cinderclient
|
||||||
|
container. You'll need to make sure and specify to use the same network
|
||||||
|
that was used by compose.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker run -it -e OS_AUTH_TYPE=noauth \
|
||||||
|
-e CINDERCLIENT_BYPASS_URL=http://cinder-api:8776/v3 \
|
||||||
|
-e OS_PROJECT_ID=foo \
|
||||||
|
-e OS_VOLUME_API_VERSION=3.27 \
|
||||||
|
--network blockbox_default cinderclient list
|
||||||
|
```
|
||||||
|
|
||||||
|
# Or without docker-compose
|
||||||
|
That's ok, you can always just run the commands yourself using docker run:
|
||||||
|
```shell
|
||||||
|
|
||||||
|
# We set passwords and db creation in the docker-entrypoint-initdb.d script
|
||||||
|
docker run -d -p 3306:3306 \
|
||||||
|
-v ~/block-box/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d \
|
||||||
|
--name dbhost \
|
||||||
|
--hostname dbhost \
|
||||||
|
-e MYSQL_ROOT_PASSWORD=password \
|
||||||
|
mariadb
|
||||||
|
|
||||||
|
# Make sure the environment vars match the startup script for your dbhost
|
||||||
|
docker run -d -p 5000:5000 \
|
||||||
|
-p 35357:35357 \
|
||||||
|
--link dbhost \
|
||||||
|
--name keystone \
|
||||||
|
--hostname keystone \
|
||||||
|
-e OS_PASSWORD=password \
|
||||||
|
-e DEMO_PASSWORD=password \
|
||||||
|
-e DB_HOST=dbhost \
|
||||||
|
-e DB_PASSWORD=password \
|
||||||
|
keystone
|
||||||
|
|
||||||
|
docker run -d -p 5672:5672 --name rabbit --hostname rabbit rabbitmq
|
||||||
|
|
||||||
|
docker run -d -p 8776:8776 \
|
||||||
|
--link dbhost \
|
||||||
|
--link rabbit \
|
||||||
|
--name cinder-api \
|
||||||
|
--hostname cinder-api \
|
||||||
|
-v ~/block-box/etc-cinder:/etc/cinder \
|
||||||
|
-v ~/block-box/init-scripts:/init-scripts
|
||||||
|
cinder_debian sh /init-scripts/cinder-api.sh
|
||||||
|
|
||||||
|
docker run -d --name cinder-scheduler \
|
||||||
|
--hostname cinder-scheduler \
|
||||||
|
--link dbhost \
|
||||||
|
--link rabbit \
|
||||||
|
-v ~/block-box/etc-cinder:/etc/cinder \
|
||||||
|
cinder_debian cinder-scheduler
|
||||||
|
|
||||||
|
docker run -d --name cinder-volume \
|
||||||
|
--hostname cinder-volume \
|
||||||
|
--link dbhost \
|
||||||
|
--link rabbit \
|
||||||
|
-v ~/block-box/etc-cinder:/etc/cinder \
|
||||||
|
cinder-debian cinder-volume
|
||||||
|
```
|
5
contrib/block-box/cinder.rc
Normal file
5
contrib/block-box/cinder.rc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export OS_AUTH_TYPE=noauth
|
||||||
|
export CINDERCLIENT_BYPASS_URL=http://10.117.36.106:8776/v3
|
||||||
|
export OS_PROJECT_ID=cinderflex
|
||||||
|
export OS_USERNAME=kubernetes
|
||||||
|
export OS_VOLUME_API_VERSION=3.27
|
208
contrib/block-box/contrib/cinderflex
Normal file
208
contrib/block-box/contrib/cinderflex
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Copyright 2015 The Kubernetes Authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# Based on this excellent blog post by Ivan - http://blog.e0ne.info/post/Attach-Cinder-Volume-to-the-Ironic-Instance-without-Nova.aspx
|
||||||
|
|
||||||
|
export OS_PROJECT_DOMAIN_ID=default
|
||||||
|
export OS_USER_DOMAIN_ID=default
|
||||||
|
export OS_PROJECT_NAME=admin
|
||||||
|
export OS_PASSWORD=secret
|
||||||
|
export OS_AUTH_URL=http://10.0.0.2/identity
|
||||||
|
export OS_USERNAME=admin
|
||||||
|
export PATH=$PATH:/lib/udev
|
||||||
|
|
||||||
|
# Notes:
|
||||||
|
# - Please install "jq" package before using this driver.
|
||||||
|
usage() {
|
||||||
|
err "Invalid usage. Usage: "
|
||||||
|
err "\t$0 init"
|
||||||
|
err "\t$0 attach <json params> <nodename>"
|
||||||
|
err "\t$0 detach <mount device> <nodename>"
|
||||||
|
err "\t$0 waitforattach <mount device> <json params>"
|
||||||
|
err "\t$0 mountdevice <mount dir> <mount device> <json params>"
|
||||||
|
err "\t$0 unmountdevice <mount dir>"
|
||||||
|
err "\t$0 getvolumename <json params>"
|
||||||
|
err "\t$0 isattached <json params> <nodename>"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
err() {
|
||||||
|
echo -ne $* 1>&2
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo -ne $* >&1
|
||||||
|
}
|
||||||
|
|
||||||
|
ismounted() {
|
||||||
|
MOUNT=`findmnt -n ${MNTPATH} 2>/dev/null | cut -d' ' -f1`
|
||||||
|
if [ "${MOUNT}" == "${MNTPATH}" ]; then
|
||||||
|
echo "1"
|
||||||
|
else
|
||||||
|
echo "0"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
getdevice() {
|
||||||
|
VOLUMEID=$(echo ${JSON_PARAMS} | jq -r '.volumeID')
|
||||||
|
DMDEV="/dev/disk/by-path/$(ls -1 /dev/disk/by-path/ | grep $VOLUMEID)"
|
||||||
|
echo ${DMDEV}
|
||||||
|
}
|
||||||
|
|
||||||
|
attach() {
|
||||||
|
JSON_PARAMS=$1
|
||||||
|
SIZE=$(echo $1 | jq -r '.size')
|
||||||
|
|
||||||
|
VOLUMEID=$(echo ${JSON_PARAMS} | jq -r '.volumeID')
|
||||||
|
sudo -E PATH=$PATH cinder local-attach $VOLUMEID >> /tmp/cinder2.log 2>&1
|
||||||
|
|
||||||
|
DMDEV=$(getdevice)
|
||||||
|
if [ ! -b "${DMDEV}" ]; then
|
||||||
|
err "{\"status\": \"Failure\", \"message\": \"Volume ${VOLUMEID} does not exist\"}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log "{\"status\": \"Success\", \"device\":\"${DMDEV}\"}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
detach() {
|
||||||
|
log "{\"status\": \"Success\"}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
waitforattach() {
|
||||||
|
#shift
|
||||||
|
#attach $*
|
||||||
|
log "{\"status\": \"Success\"}"
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
domountdevice() {
|
||||||
|
MNTPATH=$1
|
||||||
|
JSON_PARAMS=$2
|
||||||
|
DMDEV=$(getdevice)
|
||||||
|
FSTYPE=$(echo $JSON_PARAMS|jq -r '.["kubernetes.io/fsType"]')
|
||||||
|
|
||||||
|
if [ ! -b "${DMDEV}" ]; then
|
||||||
|
err "{\"status\": \"Failure\", \"message\": \"${DMDEV} does not exist\"}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $(ismounted) -eq 1 ] ; then
|
||||||
|
log "{\"status\": \"Success\"}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
VOLFSTYPE=`blkid -o udev ${DMDEV} 2>/dev/null|grep "ID_FS_TYPE"|cut -d"=" -f2`
|
||||||
|
if [ "${VOLFSTYPE}" == "" ]; then
|
||||||
|
mkfs -t ${FSTYPE} ${DMDEV} >/dev/null 2>&1
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err "{ \"status\": \"Failure\", \"message\": \"Failed to create fs ${FSTYPE} on device ${DMDEV}\"}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p ${MNTPATH} &> /dev/null
|
||||||
|
|
||||||
|
mount ${DMDEV} ${MNTPATH} &> /dev/null
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err "{ \"status\": \"Failure\", \"message\": \"Failed to mount device ${DMDEV} at ${MNTPATH}\"}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log "{\"status\": \"Success\"}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
unmountdevice() {
|
||||||
|
MNTPATH=$1
|
||||||
|
if [ ! -d ${MNTPATH} ]; then
|
||||||
|
log "{\"status\": \"Success\"}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $(ismounted) -eq 0 ] ; then
|
||||||
|
log "{\"status\": \"Success\"}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
umount ${MNTPATH} &> /dev/null
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err "{ \"status\": \"Failed\", \"message\": \"Failed to unmount volume at ${MNTPATH}\"}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
$VOLUMEID=$(echo ${MNTPATH} | sed -r "s/^.*volume-(.*)-lun.*/\1/")
|
||||||
|
sudo -E PATH=$PATH cinder local-detach $VOLUMEID >> /tmp/cinder2.log 2>&1
|
||||||
|
|
||||||
|
log "{\"status\": \"Success\"}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
getvolumename() {
|
||||||
|
JSON_PARAMS=$1
|
||||||
|
UUID=$(echo ${JSON_PARAMS} | jq -r '.volumeID')
|
||||||
|
|
||||||
|
log "{\"status\": \"Success\", \"volumeName\":\"${UUID}\"}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
isattached() {
|
||||||
|
log "{\"status\": \"Success\", \"attached\":true}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
op=$1
|
||||||
|
|
||||||
|
if [ "$op" = "init" ]; then
|
||||||
|
log "{\"status\": \"Success\"}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$op" in
|
||||||
|
attach)
|
||||||
|
attach $*
|
||||||
|
;;
|
||||||
|
detach)
|
||||||
|
detach $*
|
||||||
|
;;
|
||||||
|
waitforattach)
|
||||||
|
waitforattach $*
|
||||||
|
;;
|
||||||
|
mountdevice)
|
||||||
|
domountdevice $*
|
||||||
|
;;
|
||||||
|
unmountdevice)
|
||||||
|
unmountdevice $*
|
||||||
|
;;
|
||||||
|
getvolumename)
|
||||||
|
getvolumename $*
|
||||||
|
;;
|
||||||
|
isattached)
|
||||||
|
isattached $*
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "{ \"status\": \"Not supported\" }"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 1
|
28
contrib/block-box/docker-compose-add-vol-service.yml
Normal file
28
contrib/block-box/docker-compose-add-vol-service.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
version: "2"
|
||||||
|
services:
|
||||||
|
cinder-volume-sf:
|
||||||
|
image: cinder
|
||||||
|
hostname: cinder-volume-sf
|
||||||
|
privileged: true
|
||||||
|
volumes:
|
||||||
|
- ./etc-cinder:/etc/cinder
|
||||||
|
- /dev/:/dev/
|
||||||
|
- /run/:/run/:shared
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- /lib/modules:/lib/modules:ro
|
||||||
|
extra_hosts:
|
||||||
|
- "rabbitmq:172.49.49.5"
|
||||||
|
- "mariadb:172.49.49.6"
|
||||||
|
networks:
|
||||||
|
cindernet:
|
||||||
|
ipv4_address: 172.49.49.10
|
||||||
|
command: bash -c "cinder-volume -d --config-file /etc/cinder/cinder.conf.sf"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
cindernet:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
driver: default
|
||||||
|
config:
|
||||||
|
-
|
||||||
|
subnet: 172.49.49.0/24
|
86
contrib/block-box/docker-compose.yml
Normal file
86
contrib/block-box/docker-compose.yml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
version: "2"
|
||||||
|
services:
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq
|
||||||
|
ports:
|
||||||
|
- "5672:5672"
|
||||||
|
hostname: rabbitmq
|
||||||
|
networks:
|
||||||
|
cindernet:
|
||||||
|
ipv4_address: 172.49.49.5
|
||||||
|
mariadb:
|
||||||
|
image: mariadb
|
||||||
|
hostname: mariadb
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=password
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
networks:
|
||||||
|
cindernet:
|
||||||
|
ipv4_address: 172.49.49.6
|
||||||
|
volumes:
|
||||||
|
- ./db-data:/var/lib/mysql
|
||||||
|
- ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
|
||||||
|
cinder-api:
|
||||||
|
image: cinder
|
||||||
|
hostname: cinder-api
|
||||||
|
volumes:
|
||||||
|
- ./etc-cinder:/etc/cinder
|
||||||
|
- ./init-scripts:/init-scripts
|
||||||
|
network_mode: "host"
|
||||||
|
ports:
|
||||||
|
- 8776:8776
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
|
extra_hosts:
|
||||||
|
- "rabbitmq:172.49.49.5"
|
||||||
|
- "mariadb:172.49.49.6"
|
||||||
|
environment:
|
||||||
|
- INIT_DB=true
|
||||||
|
command: sh /init-scripts/cinder-api.sh
|
||||||
|
cinder-scheduler:
|
||||||
|
image: cinder
|
||||||
|
hostname: cinder-scheduler
|
||||||
|
volumes:
|
||||||
|
- ./etc-cinder:/etc/cinder
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
|
- rabbitmq
|
||||||
|
- cinder-api
|
||||||
|
extra_hosts:
|
||||||
|
- "rabbitmq:172.49.49.5"
|
||||||
|
- "mariadb:172.49.49.6"
|
||||||
|
network_mode: "host"
|
||||||
|
depends_on:
|
||||||
|
- cinder-api
|
||||||
|
restart: on-failure
|
||||||
|
command: cinder-scheduler
|
||||||
|
cinder-volume:
|
||||||
|
image: cinder-lvm
|
||||||
|
hostname: cinder-lvm
|
||||||
|
privileged: true
|
||||||
|
volumes:
|
||||||
|
- ./etc-cinder:/etc/cinder
|
||||||
|
- /dev/:/dev/
|
||||||
|
- /run/:/run/:shared
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- /lib/modules:/lib/modules:ro
|
||||||
|
depends_on:
|
||||||
|
- cinder-scheduler
|
||||||
|
ports:
|
||||||
|
- 3260:3260
|
||||||
|
extra_hosts:
|
||||||
|
- "rabbitmq:172.49.49.5"
|
||||||
|
- "mariadb:172.49.49.6"
|
||||||
|
network_mode: "host"
|
||||||
|
restart: on-failure
|
||||||
|
command: bash -c "sleep 5 && /usr/sbin/tgtd && cinder-volume -d"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
cindernet:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
driver: default
|
||||||
|
config:
|
||||||
|
-
|
||||||
|
subnet: 172.49.49.0/24
|
5
contrib/block-box/docker-entrypoint-initdb.d/script.sql
Executable file
5
contrib/block-box/docker-entrypoint-initdb.d/script.sql
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
CREATE DATABASE cinder;
|
||||||
|
GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'localhost' \
|
||||||
|
IDENTIFIED BY 'password';
|
||||||
|
GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%' \
|
||||||
|
IDENTIFIED BY 'password';
|
12
contrib/block-box/docker_files/Dockerfile.cinder-devenv
Normal file
12
contrib/block-box/docker_files/Dockerfile.cinder-devenv
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
FROM python
|
||||||
|
COPY test-requirements.txt /test-requirements.txt
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y \
|
||||||
|
libssl-dev \
|
||||||
|
git-core \
|
||||||
|
libmysqlclient-dev \
|
||||||
|
libpq-dev \
|
||||||
|
libffi-dev \
|
||||||
|
libxslt-dev \
|
||||||
|
&& pip install -r test-requirements.txt \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
11
contrib/block-box/docker_files/Dockerfile.cinder-lvm
Normal file
11
contrib/block-box/docker_files/Dockerfile.cinder-lvm
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
FROM cinder
|
||||||
|
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
vim lvm2 tgt \
|
||||||
|
&& rm -rf /var/lib/apt
|
||||||
|
RUN sed -i -e 's/udev_sync = 1/udev_sync = 0/g' /etc/lvm/lvm.conf \
|
||||||
|
&& sed -i -e 's/udev_rules = 1/udev_rules = 0/g' /etc/lvm/lvm.conf \
|
||||||
|
&& sed -i -e 's/use_lvmetad = 0/use_lvmetad =1/g' /etc/lvm/lvm.conf \
|
||||||
|
&& echo "include /var/lib/cinder/volumes/*" >> /etc/tgt/targets.conf
|
||||||
|
CMD /usr/sbin/tgtd
|
6
contrib/block-box/docker_files/README
Normal file
6
contrib/block-box/docker_files/README
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Need to turn off udev_ rules
|
||||||
|
s/udev_sync = 1/udev_sync = 0/
|
||||||
|
s/udev_rules = 1/udev_rules = 0/
|
||||||
|
|
||||||
|
Need to turn ON use_lvmetad
|
||||||
|
s/use_lvmetad = 0/use_lvmetad = 1/
|
2095
contrib/block-box/docker_files/lvm.conf
Normal file
2095
contrib/block-box/docker_files/lvm.conf
Normal file
File diff suppressed because it is too large
Load Diff
75
contrib/block-box/etc-cinder/api-paste.ini
Normal file
75
contrib/block-box/etc-cinder/api-paste.ini
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#############
|
||||||
|
# OpenStack #
|
||||||
|
#############
|
||||||
|
|
||||||
|
[composite:osapi_volume]
|
||||||
|
use = call:cinder.api:root_app_factory
|
||||||
|
/: apiversions
|
||||||
|
/v1: openstack_volume_api_v1
|
||||||
|
/v2: openstack_volume_api_v2
|
||||||
|
/v3: openstack_volume_api_v3
|
||||||
|
|
||||||
|
[composite:openstack_volume_api_v1]
|
||||||
|
use = call:cinder.api.middleware.auth:pipeline_factory
|
||||||
|
noauth = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler noauth apiv1
|
||||||
|
keystone = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext apiv1
|
||||||
|
keystone_nolimit = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext apiv1
|
||||||
|
|
||||||
|
[composite:openstack_volume_api_v2]
|
||||||
|
use = call:cinder.api.middleware.auth:pipeline_factory
|
||||||
|
noauth = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler noauth apiv2
|
||||||
|
keystone = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext apiv2
|
||||||
|
keystone_nolimit = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext apiv2
|
||||||
|
|
||||||
|
[composite:openstack_volume_api_v3]
|
||||||
|
use = call:cinder.api.middleware.auth:pipeline_factory
|
||||||
|
noauth = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler noauth apiv3
|
||||||
|
keystone = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
|
||||||
|
keystone_nolimit = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
|
||||||
|
|
||||||
|
[filter:request_id]
|
||||||
|
paste.filter_factory = oslo_middleware.request_id:RequestId.factory
|
||||||
|
|
||||||
|
[filter:http_proxy_to_wsgi]
|
||||||
|
paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory
|
||||||
|
|
||||||
|
[filter:cors]
|
||||||
|
paste.filter_factory = oslo_middleware.cors:filter_factory
|
||||||
|
oslo_config_project = cinder
|
||||||
|
|
||||||
|
[filter:faultwrap]
|
||||||
|
paste.filter_factory = cinder.api.middleware.fault:FaultWrapper.factory
|
||||||
|
|
||||||
|
[filter:osprofiler]
|
||||||
|
paste.filter_factory = osprofiler.web:WsgiMiddleware.factory
|
||||||
|
|
||||||
|
[filter:noauth]
|
||||||
|
paste.filter_factory = cinder.api.middleware.auth:NoAuthMiddleware.factory
|
||||||
|
|
||||||
|
[filter:sizelimit]
|
||||||
|
paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory
|
||||||
|
|
||||||
|
[app:apiv1]
|
||||||
|
paste.app_factory = cinder.api.v1.router:APIRouter.factory
|
||||||
|
|
||||||
|
[app:apiv2]
|
||||||
|
paste.app_factory = cinder.api.v2.router:APIRouter.factory
|
||||||
|
|
||||||
|
[app:apiv3]
|
||||||
|
paste.app_factory = cinder.api.v3.router:APIRouter.factory
|
||||||
|
|
||||||
|
[pipeline:apiversions]
|
||||||
|
pipeline = cors http_proxy_to_wsgi faultwrap osvolumeversionapp
|
||||||
|
|
||||||
|
[app:osvolumeversionapp]
|
||||||
|
paste.app_factory = cinder.api.versions:Versions.factory
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Shared #
|
||||||
|
##########
|
||||||
|
|
||||||
|
[filter:keystonecontext]
|
||||||
|
paste.filter_factory = cinder.api.middleware.auth:CinderKeystoneContext.factory
|
||||||
|
|
||||||
|
[filter:authtoken]
|
||||||
|
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
|
23
contrib/block-box/etc-cinder/cinder.conf
Normal file
23
contrib/block-box/etc-cinder/cinder.conf
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
rootwrap_config = /etc/cinder/rootwrap.conf
|
||||||
|
api_paste_confg = /etc/cinder/api-paste.ini
|
||||||
|
volume_name_template = volume-%s
|
||||||
|
volume_group = cinder-volumes
|
||||||
|
verbose = True
|
||||||
|
auth_strategy = noauth
|
||||||
|
state_path = /var/lib/cinder
|
||||||
|
lock_path = /var/lock/cinder
|
||||||
|
volumes_dir = /var/lib/cinder/volumes
|
||||||
|
rabbit_host = rabbitmq
|
||||||
|
enabled_backends = lvm
|
||||||
|
|
||||||
|
[database]
|
||||||
|
connection = mysql+pymysql://cinder:password@mariadb/cinder?charset=utf8
|
||||||
|
|
||||||
|
[lvm]
|
||||||
|
lvm_type = thin
|
||||||
|
volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver
|
||||||
|
volume_group = cinder-volumes
|
||||||
|
iscsi_protocol = iscsi
|
||||||
|
iscsi_helper = tgtadm
|
||||||
|
volume_backend_name=lvm
|
35
contrib/block-box/etc-cinder/cinder.conf.keystone
Normal file
35
contrib/block-box/etc-cinder/cinder.conf.keystone
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
rootwrap_config = /etc/cinder/rootwrap.conf
|
||||||
|
api_paste_confg = /etc/cinder/api-paste.ini
|
||||||
|
iscsi_helper = tgtadm
|
||||||
|
volume_name_template = volume-%s
|
||||||
|
volume_group = cinder-volumes
|
||||||
|
verbose = True
|
||||||
|
auth_strategy = keystone
|
||||||
|
state_path = /var/lib/cinder
|
||||||
|
lock_path = /var/lock/cinder
|
||||||
|
volumes_dir = /var/lib/cinder/volumes
|
||||||
|
rabbit_host = rabbitmq
|
||||||
|
enabled_backends = solidfire
|
||||||
|
|
||||||
|
[database]
|
||||||
|
connection = mysql+pymysql://cinder:password@mariadb/cinder?charset=utf8
|
||||||
|
|
||||||
|
[keystone_authtoken]
|
||||||
|
auth_uri = http://keystone:5000
|
||||||
|
auth_url = http://keystone:35357
|
||||||
|
memcached_servers = keystone:11211
|
||||||
|
auth_type = password
|
||||||
|
project_domain_name = default
|
||||||
|
user_domain_name = default
|
||||||
|
project_name = service
|
||||||
|
username = cinder
|
||||||
|
password = password
|
||||||
|
|
||||||
|
[solidfire]
|
||||||
|
volume_driver=cinder.volume.drivers.solidfire.SolidFireDriver
|
||||||
|
san_ip=10.117.36.101
|
||||||
|
san_login=admin
|
||||||
|
san_password=admin
|
||||||
|
volume_backend_name=solidfire
|
||||||
|
sf_allow_template_caching=false
|
24
contrib/block-box/etc-cinder/cinder.conf.sf
Normal file
24
contrib/block-box/etc-cinder/cinder.conf.sf
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
rootwrap_config = /etc/cinder/rootwrap.conf
|
||||||
|
api_paste_confg = /etc/cinder/api-paste.ini
|
||||||
|
iscsi_helper = tgtadm
|
||||||
|
volume_name_template = volume-%s
|
||||||
|
volume_group = cinder-volumes
|
||||||
|
verbose = True
|
||||||
|
auth_strategy = noauth
|
||||||
|
state_path = /var/lib/cinder
|
||||||
|
lock_path = /var/lock/cinder
|
||||||
|
volumes_dir = /var/lib/cinder/volumes
|
||||||
|
rabbit_host = rabbitmq
|
||||||
|
enabled_backends = solidfire
|
||||||
|
|
||||||
|
[database]
|
||||||
|
connection = mysql+pymysql://cinder:password@mariadb/cinder?charset=utf8
|
||||||
|
|
||||||
|
[solidfire]
|
||||||
|
volume_driver=cinder.volume.drivers.solidfire.SolidFireDriver
|
||||||
|
san_ip=10.117.36.101
|
||||||
|
san_login=admin
|
||||||
|
san_password=admin
|
||||||
|
volume_backend_name=solidfire
|
||||||
|
sf_allow_template_caching=false
|
23
contrib/block-box/etc-cinder/cinder.conf.solidfire
Normal file
23
contrib/block-box/etc-cinder/cinder.conf.solidfire
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
rootwrap_config = /etc/cinder/rootwrap.conf
|
||||||
|
api_paste_confg = /etc/cinder/api-paste.ini
|
||||||
|
volume_name_template = volume-%s
|
||||||
|
volume_group = cinder-volumes
|
||||||
|
verbose = True
|
||||||
|
auth_strategy = noauth
|
||||||
|
state_path = /var/lib/cinder
|
||||||
|
lock_path = /var/lock/cinder
|
||||||
|
volumes_dir = /var/lib/cinder/volumes
|
||||||
|
rabbit_host = rabbitmq
|
||||||
|
enabled_backends = solidfire
|
||||||
|
|
||||||
|
[database]
|
||||||
|
connection = mysql+pymysql://cinder:password@mariadb/cinder?charset=utf8
|
||||||
|
|
||||||
|
[solidfire]
|
||||||
|
volume_driver=cinder.volume.drivers.solidfire.SolidFireDriver
|
||||||
|
san_ip=10.117.36.51
|
||||||
|
san_login=admin
|
||||||
|
san_password=admin
|
||||||
|
volume_backend_name=solidfire
|
||||||
|
sf_allow_template_caching=false
|
76
contrib/block-box/etc-cinder/logging.conf
Normal file
76
contrib/block-box/etc-cinder/logging.conf
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
[loggers]
|
||||||
|
keys = root, cinder
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = stderr, stdout, watchedfile, syslog, null
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = legacycinder, default
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARNING
|
||||||
|
handlers = null
|
||||||
|
|
||||||
|
[logger_cinder]
|
||||||
|
level = INFO
|
||||||
|
handlers = stderr
|
||||||
|
qualname = cinder
|
||||||
|
|
||||||
|
[logger_amqplib]
|
||||||
|
level = WARNING
|
||||||
|
handlers = stderr
|
||||||
|
qualname = amqplib
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARNING
|
||||||
|
handlers = stderr
|
||||||
|
qualname = sqlalchemy
|
||||||
|
# "level = INFO" logs SQL queries.
|
||||||
|
# "level = DEBUG" logs SQL queries and results.
|
||||||
|
# "level = WARNING" logs neither. (Recommended for production systems.)
|
||||||
|
|
||||||
|
[logger_boto]
|
||||||
|
level = WARNING
|
||||||
|
handlers = stderr
|
||||||
|
qualname = boto
|
||||||
|
|
||||||
|
[logger_suds]
|
||||||
|
level = INFO
|
||||||
|
handlers = stderr
|
||||||
|
qualname = suds
|
||||||
|
|
||||||
|
[logger_eventletwsgi]
|
||||||
|
level = WARNING
|
||||||
|
handlers = stderr
|
||||||
|
qualname = eventlet.wsgi.server
|
||||||
|
|
||||||
|
[handler_stderr]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
formatter = legacycinder
|
||||||
|
|
||||||
|
[handler_stdout]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stdout,)
|
||||||
|
formatter = legacycinder
|
||||||
|
|
||||||
|
[handler_watchedfile]
|
||||||
|
class = handlers.WatchedFileHandler
|
||||||
|
args = ('cinder.log',)
|
||||||
|
formatter = legacycinder
|
||||||
|
|
||||||
|
[handler_syslog]
|
||||||
|
class = handlers.SysLogHandler
|
||||||
|
args = ('/dev/log', handlers.SysLogHandler.LOG_USER)
|
||||||
|
formatter = legacycinder
|
||||||
|
|
||||||
|
[handler_null]
|
||||||
|
class = cinder.log.NullHandler
|
||||||
|
formatter = default
|
||||||
|
args = ()
|
||||||
|
|
||||||
|
[formatter_legacycinder]
|
||||||
|
class = cinder.log.LegacyCinderFormatter
|
||||||
|
|
||||||
|
[formatter_default]
|
||||||
|
format = %(message)s
|
141
contrib/block-box/etc-cinder/policy.json
Normal file
141
contrib/block-box/etc-cinder/policy.json
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
{
|
||||||
|
"admin_or_owner": "is_admin:True or (role:admin and is_admin_project:True) or project_id:%(project_id)s",
|
||||||
|
"default": "rule:admin_or_owner",
|
||||||
|
|
||||||
|
"admin_api": "is_admin:True or (role:admin and is_admin_project:True)",
|
||||||
|
|
||||||
|
"volume:create": "",
|
||||||
|
"volume:create_from_image": "",
|
||||||
|
"volume:delete": "rule:admin_or_owner",
|
||||||
|
"volume:force_delete": "rule:admin_api",
|
||||||
|
"volume:get": "rule:admin_or_owner",
|
||||||
|
"volume:get_all": "rule:admin_or_owner",
|
||||||
|
"volume:get_volume_metadata": "rule:admin_or_owner",
|
||||||
|
"volume:create_volume_metadata": "rule:admin_or_owner",
|
||||||
|
"volume:delete_volume_metadata": "rule:admin_or_owner",
|
||||||
|
"volume:update_volume_metadata": "rule:admin_or_owner",
|
||||||
|
"volume:get_volume_admin_metadata": "rule:admin_api",
|
||||||
|
"volume:update_volume_admin_metadata": "rule:admin_api",
|
||||||
|
"volume:get_snapshot": "rule:admin_or_owner",
|
||||||
|
"volume:get_all_snapshots": "rule:admin_or_owner",
|
||||||
|
"volume:create_snapshot": "rule:admin_or_owner",
|
||||||
|
"volume:delete_snapshot": "rule:admin_or_owner",
|
||||||
|
"volume:update_snapshot": "rule:admin_or_owner",
|
||||||
|
"volume:get_snapshot_metadata": "rule:admin_or_owner",
|
||||||
|
"volume:delete_snapshot_metadata": "rule:admin_or_owner",
|
||||||
|
"volume:update_snapshot_metadata": "rule:admin_or_owner",
|
||||||
|
"volume:extend": "rule:admin_or_owner",
|
||||||
|
"volume:update_readonly_flag": "rule:admin_or_owner",
|
||||||
|
"volume:retype": "rule:admin_or_owner",
|
||||||
|
"volume:update": "rule:admin_or_owner",
|
||||||
|
|
||||||
|
"volume_extension:types_manage": "rule:admin_api",
|
||||||
|
"volume_extension:types_extra_specs": "rule:admin_api",
|
||||||
|
"volume_extension:access_types_qos_specs_id": "rule:admin_api",
|
||||||
|
"volume_extension:access_types_extra_specs": "rule:admin_api",
|
||||||
|
"volume_extension:volume_type_access": "rule:admin_or_owner",
|
||||||
|
"volume_extension:volume_type_access:addProjectAccess": "rule:admin_api",
|
||||||
|
"volume_extension:volume_type_access:removeProjectAccess": "rule:admin_api",
|
||||||
|
"volume_extension:volume_type_encryption": "rule:admin_api",
|
||||||
|
"volume_extension:volume_encryption_metadata": "rule:admin_or_owner",
|
||||||
|
"volume_extension:extended_snapshot_attributes": "rule:admin_or_owner",
|
||||||
|
"volume_extension:volume_image_metadata": "rule:admin_or_owner",
|
||||||
|
|
||||||
|
"volume_extension:quotas:show": "",
|
||||||
|
"volume_extension:quotas:update": "rule:admin_api",
|
||||||
|
"volume_extension:quotas:delete": "rule:admin_api",
|
||||||
|
"volume_extension:quota_classes": "rule:admin_api",
|
||||||
|
"volume_extension:quota_classes:validate_setup_for_nested_quota_use": "rule:admin_api",
|
||||||
|
|
||||||
|
"volume_extension:volume_admin_actions:reset_status": "rule:admin_api",
|
||||||
|
"volume_extension:snapshot_admin_actions:reset_status": "rule:admin_api",
|
||||||
|
"volume_extension:backup_admin_actions:reset_status": "rule:admin_api",
|
||||||
|
"volume_extension:volume_admin_actions:force_delete": "rule:admin_api",
|
||||||
|
"volume_extension:volume_admin_actions:force_detach": "rule:admin_api",
|
||||||
|
"volume_extension:snapshot_admin_actions:force_delete": "rule:admin_api",
|
||||||
|
"volume_extension:backup_admin_actions:force_delete": "rule:admin_api",
|
||||||
|
"volume_extension:volume_admin_actions:migrate_volume": "rule:admin_api",
|
||||||
|
"volume_extension:volume_admin_actions:migrate_volume_completion": "rule:admin_api",
|
||||||
|
|
||||||
|
"volume_extension:volume_actions:upload_public": "rule:admin_api",
|
||||||
|
"volume_extension:volume_actions:upload_image": "rule:admin_or_owner",
|
||||||
|
|
||||||
|
"volume_extension:volume_host_attribute": "rule:admin_api",
|
||||||
|
"volume_extension:volume_tenant_attribute": "rule:admin_or_owner",
|
||||||
|
"volume_extension:volume_mig_status_attribute": "rule:admin_api",
|
||||||
|
"volume_extension:hosts": "rule:admin_api",
|
||||||
|
"volume_extension:services:index": "rule:admin_api",
|
||||||
|
"volume_extension:services:update" : "rule:admin_api",
|
||||||
|
|
||||||
|
"volume_extension:volume_manage": "rule:admin_api",
|
||||||
|
"volume_extension:volume_unmanage": "rule:admin_api",
|
||||||
|
"volume_extension:list_manageable": "rule:admin_api",
|
||||||
|
|
||||||
|
"volume_extension:capabilities": "rule:admin_api",
|
||||||
|
|
||||||
|
"volume:create_transfer": "rule:admin_or_owner",
|
||||||
|
"volume:accept_transfer": "",
|
||||||
|
"volume:delete_transfer": "rule:admin_or_owner",
|
||||||
|
"volume:get_transfer": "rule:admin_or_owner",
|
||||||
|
"volume:get_all_transfers": "rule:admin_or_owner",
|
||||||
|
|
||||||
|
"volume:failover_host": "rule:admin_api",
|
||||||
|
"volume:freeze_host": "rule:admin_api",
|
||||||
|
"volume:thaw_host": "rule:admin_api",
|
||||||
|
|
||||||
|
"backup:create" : "",
|
||||||
|
"backup:delete": "rule:admin_or_owner",
|
||||||
|
"backup:get": "rule:admin_or_owner",
|
||||||
|
"backup:get_all": "rule:admin_or_owner",
|
||||||
|
"backup:restore": "rule:admin_or_owner",
|
||||||
|
"backup:backup-import": "rule:admin_api",
|
||||||
|
"backup:backup-export": "rule:admin_api",
|
||||||
|
"backup:update": "rule:admin_or_owner",
|
||||||
|
"backup:backup_project_attribute": "rule:admin_api",
|
||||||
|
|
||||||
|
"snapshot_extension:snapshot_actions:update_snapshot_status": "",
|
||||||
|
"snapshot_extension:snapshot_manage": "rule:admin_api",
|
||||||
|
"snapshot_extension:snapshot_unmanage": "rule:admin_api",
|
||||||
|
"snapshot_extension:list_manageable": "rule:admin_api",
|
||||||
|
|
||||||
|
"consistencygroup:create" : "group:nobody",
|
||||||
|
"consistencygroup:delete": "group:nobody",
|
||||||
|
"consistencygroup:update": "group:nobody",
|
||||||
|
"consistencygroup:get": "group:nobody",
|
||||||
|
"consistencygroup:get_all": "group:nobody",
|
||||||
|
|
||||||
|
"consistencygroup:create_cgsnapshot" : "group:nobody",
|
||||||
|
"consistencygroup:delete_cgsnapshot": "group:nobody",
|
||||||
|
"consistencygroup:get_cgsnapshot": "group:nobody",
|
||||||
|
"consistencygroup:get_all_cgsnapshots": "group:nobody",
|
||||||
|
|
||||||
|
"group:group_types_manage": "rule:admin_api",
|
||||||
|
"group:group_types_specs": "rule:admin_api",
|
||||||
|
"group:access_group_types_specs": "rule:admin_api",
|
||||||
|
"group:group_type_access": "rule:admin_or_owner",
|
||||||
|
|
||||||
|
"group:create" : "",
|
||||||
|
"group:delete": "rule:admin_or_owner",
|
||||||
|
"group:update": "rule:admin_or_owner",
|
||||||
|
"group:get": "rule:admin_or_owner",
|
||||||
|
"group:get_all": "rule:admin_or_owner",
|
||||||
|
|
||||||
|
"group:create_group_snapshot": "",
|
||||||
|
"group:delete_group_snapshot": "rule:admin_or_owner",
|
||||||
|
"group:update_group_snapshot": "rule:admin_or_owner",
|
||||||
|
"group:get_group_snapshot": "rule:admin_or_owner",
|
||||||
|
"group:get_all_group_snapshots": "rule:admin_or_owner",
|
||||||
|
"group:reset_group_snapshot_status":"rule:admin_api",
|
||||||
|
"group:reset_status":"rule:admin_api",
|
||||||
|
|
||||||
|
"scheduler_extension:scheduler_stats:get_pools" : "rule:admin_api",
|
||||||
|
"message:delete": "rule:admin_or_owner",
|
||||||
|
"message:get": "rule:admin_or_owner",
|
||||||
|
"message:get_all": "rule:admin_or_owner",
|
||||||
|
|
||||||
|
"clusters:get": "rule:admin_api",
|
||||||
|
"clusters:get_all": "rule:admin_api",
|
||||||
|
"clusters:update": "rule:admin_api",
|
||||||
|
|
||||||
|
"workers:cleanup": "rule:admin_api"
|
||||||
|
}
|
27
contrib/block-box/etc-cinder/rootwrap.conf
Normal file
27
contrib/block-box/etc-cinder/rootwrap.conf
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Configuration for cinder-rootwrap
|
||||||
|
# This file should be owned by (and only-writeable by) the root user
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
# List of directories to load filter definitions from (separated by ',').
|
||||||
|
# These directories MUST all be only writeable by root !
|
||||||
|
filters_path=/etc/cinder/rootwrap.d,/usr/share/cinder/rootwrap
|
||||||
|
|
||||||
|
# List of directories to search executables in, in case filters do not
|
||||||
|
# explicitely specify a full path (separated by ',')
|
||||||
|
# If not specified, defaults to system PATH environment variable.
|
||||||
|
# These directories MUST all be only writeable by root !
|
||||||
|
exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin,/usr/local/bin,/usr/local/sbin
|
||||||
|
|
||||||
|
# Enable logging to syslog
|
||||||
|
# Default value is False
|
||||||
|
use_syslog=False
|
||||||
|
|
||||||
|
# Which syslog facility to use.
|
||||||
|
# Valid values include auth, authpriv, syslog, local0, local1...
|
||||||
|
# Default value is 'syslog'
|
||||||
|
syslog_log_facility=syslog
|
||||||
|
|
||||||
|
# Which messages to log.
|
||||||
|
# INFO means log all usage
|
||||||
|
# ERROR means only log unsuccessful attempts
|
||||||
|
syslog_log_level=ERROR
|
7
contrib/block-box/init-scripts/cinder-api.sh
Normal file
7
contrib/block-box/init-scripts/cinder-api.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
INIT_DB=${INIT_DB:-true}
|
||||||
|
|
||||||
|
if [ "$INIT_DB" = "true" ]; then
|
||||||
|
/bin/sh -c "cinder-manage db sync" cinder
|
||||||
|
fi
|
||||||
|
cinder-api -d
|
27
contrib/block-box/test-requirements.txt
Normal file
27
contrib/block-box/test-requirements.txt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# The order of packages is significant, because pip processes them in the order
|
||||||
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
|
# Install bounded pep8/pyflakes first, then let flake8 install
|
||||||
|
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||||
|
|
||||||
|
coverage>=4.0 # Apache-2.0
|
||||||
|
ddt>=1.0.1 # MIT
|
||||||
|
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||||
|
mock>=2.0 # BSD
|
||||||
|
os-api-ref>=1.0.0 # Apache-2.0
|
||||||
|
oslotest>=1.10.0 # Apache-2.0
|
||||||
|
sphinx>=1.5.1 # BSD
|
||||||
|
PyMySQL>=0.7.6 # MIT License
|
||||||
|
psycopg2>=2.5 # LGPL/ZPL
|
||||||
|
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||||
|
testtools>=1.4.0 # MIT
|
||||||
|
testresources>=0.2.4 # Apache-2.0/BSD
|
||||||
|
testscenarios>=0.4 # Apache-2.0/BSD
|
||||||
|
oslosphinx>=4.7.0 # Apache-2.0
|
||||||
|
oslo.versionedobjects[fixtures]>=1.17.0 # Apache-2.0
|
||||||
|
os-testr>=0.8.0 # Apache-2.0
|
||||||
|
tempest>=14.0.0 # Apache-2.0
|
||||||
|
bandit>=1.1.0 # Apache-2.0
|
||||||
|
reno>=1.8.0 # Apache-2.0
|
||||||
|
doc8 # Apache-2.0
|
Loading…
Reference in New Issue
Block a user