updating keystone developer documentation
updating docstrings to remove errors in automodule generation updating setup.py to generate source documentation blueprint keystone-documentation bug 843056 Change-Id: Ie8dfedc89c1a6d9ffa5106d29dd19837b02746ce
This commit is contained in:
parent
a57d56e9cf
commit
20c2adb9ac
30
doc/README.rst
Normal file
30
doc/README.rst
Normal file
@ -0,0 +1,30 @@
|
||||
==========================
|
||||
Building the Documentation
|
||||
==========================
|
||||
|
||||
Using setup.py
|
||||
==============
|
||||
|
||||
From the project root, just type::
|
||||
|
||||
% setup.py build_sphinx
|
||||
|
||||
|
||||
|
||||
Manually
|
||||
========
|
||||
|
||||
1. Generate the code.rst file so that Sphinx will pull in our docstrings::
|
||||
|
||||
% ./generate_autodoc_index.py
|
||||
|
||||
2. Run `sphinx_build`::
|
||||
|
||||
% sphinx-build -b html source build/html
|
||||
|
||||
|
||||
The docs have been built
|
||||
========================
|
||||
|
||||
Check out the `build` directory to find them. Yay!
|
||||
|
57
doc/generate_autodoc_index.py
Executable file
57
doc/generate_autodoc_index.py
Executable file
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python
|
||||
"""Generates files for sphinx documentation using a simple Autodoc based
|
||||
template.
|
||||
To use:
|
||||
cd keystone/doc
|
||||
./generate_autodoc_index.py
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
RSTDIR="source/sourcecode"
|
||||
SOURCE="../keystone"
|
||||
|
||||
def find_autodoc_modules():
|
||||
"""returns a list of modules in the SOURCE directory"""
|
||||
modlist = []
|
||||
for root, dirs, files in os.walk(SOURCE):
|
||||
for filename in files:
|
||||
if filename.endswith(".py"):
|
||||
# root = ../keystone/test/unit
|
||||
# filename = base.py
|
||||
# remove the first two pieces of the root
|
||||
elements = root.split(os.path.sep)[1:]
|
||||
# and get the base module name
|
||||
base, extension = os.path.splitext(filename)
|
||||
if not (base == "__init__"):
|
||||
elements.append(base)
|
||||
modlist.append(".".join(elements))
|
||||
return modlist
|
||||
|
||||
if not(os.path.exists(RSTDIR)):
|
||||
os.mkdir(RSTDIR)
|
||||
|
||||
INDEXOUT = open("%s/autoindex.rst" % RSTDIR, "w")
|
||||
INDEXOUT.write("Source Code Index\n")
|
||||
INDEXOUT.write("=================\n")
|
||||
INDEXOUT.write(".. toctree::\n")
|
||||
INDEXOUT.write(" :maxdepth: 1\n")
|
||||
INDEXOUT.write("\n")
|
||||
|
||||
for module in find_autodoc_modules():
|
||||
generated_file = "%s/%s.rst" % (RSTDIR, module)
|
||||
print "Generating %s" % generated_file
|
||||
|
||||
INDEXOUT.write(" %s\n" % module)
|
||||
FILEOUT = open(generated_file, "w")
|
||||
FILEOUT.write("The :mod:`%s` Module\n" % module)
|
||||
FILEOUT.write("=============================="
|
||||
"=============================="
|
||||
"==============================\n")
|
||||
FILEOUT.write(".. automodule:: %s\n" % module)
|
||||
FILEOUT.write(" :members:\n")
|
||||
FILEOUT.write(" :undoc-members:\n")
|
||||
FILEOUT.write(" :show-inheritance:\n")
|
||||
FILEOUT.close()
|
||||
|
||||
INDEXOUT.close()
|
@ -17,3 +17,81 @@
|
||||
Keystone Architecture
|
||||
=====================
|
||||
|
||||
Keystone has two major components: Authentication and a Service Catalog.
|
||||
|
||||
Authentication
|
||||
--------------
|
||||
|
||||
In providing a token-based authentication service for OpenStack, keystone
|
||||
has several major concepts:
|
||||
|
||||
Tenant
|
||||
A grouping used in OpenStack to contain relevant OpenStack services. A
|
||||
tenant maps to a Nova "project-id", and in object storage, a tenant can
|
||||
have multiple containers. Depending on the installation, a tenant can
|
||||
represent a customer, account, organization, or project.
|
||||
|
||||
User
|
||||
Represents an individual within OpenStack for the purposes of
|
||||
authenticating them to OpenStack services. Users have credentials, and may
|
||||
be assigned to one or more tenants. When authenticated, a token is
|
||||
provided that is specific to a single tenant.
|
||||
|
||||
Credentials
|
||||
Password or other information that uniquely identifies a User to Keystone
|
||||
for the purposes of providing a token.
|
||||
|
||||
Token
|
||||
A token is an arbitrary bit of text that is used to share authentication
|
||||
with other OpenStack services so that Keystone can provide a central
|
||||
location for authenticating users for access to OpenStack services. A
|
||||
token may be "scoped" or "unscoped". A scoped token represents a user
|
||||
authenticated to a Tenant, where an unscoped token represents just the
|
||||
user.
|
||||
|
||||
Tokens are valid for a limited amount of time and may be revoked at any
|
||||
time.
|
||||
|
||||
Role
|
||||
A role is a set of permissions to access and use specific operations for
|
||||
a given user when applied to a tenant. Roles are logical groupings of
|
||||
those permissions to enable common permissions to be easily grouped and
|
||||
bound to users associated with a given tenant.
|
||||
|
||||
Service Catalog
|
||||
---------------
|
||||
|
||||
Keystone also provides a list of REST API endpoints as a definitive list for
|
||||
an OpenStack installation. Key concepts include:
|
||||
|
||||
Service
|
||||
An OpenStack service such as nova, swift, glance, or keystone. A service
|
||||
may have one of more endpoints through which users can interact with
|
||||
OpenStack services and resources.
|
||||
|
||||
Endpoint
|
||||
A network accessible address (typically a URL) that represents the API
|
||||
interface to an OpenStack service. Endpoints may also be grouped into
|
||||
templates which represent a group of consumable OpenStack services
|
||||
available across regions.
|
||||
|
||||
Template
|
||||
A collection of endpoints representing a set of consumable OpenStack
|
||||
service endpoints.
|
||||
|
||||
Components of Keystone
|
||||
----------------------
|
||||
|
||||
Keystone includes a command-line interface which interacts with the Keystone
|
||||
API for administrating keystone and related services.
|
||||
|
||||
* keystone - runs both keystone-admin and keystone-service
|
||||
* keystone-admin - the administrative API for manipulating keystone
|
||||
* keystone-service - the user oriented API for authentication
|
||||
* keystone-manage - the command line interface to manipulate keystone
|
||||
|
||||
Keystone also includes WSGI middelware to provide authentication support
|
||||
for Nova and Swift.
|
||||
|
||||
Keystone uses a built-in SQLite datastore - and may use an external LDAP
|
||||
service to authenticate users instead of using stored credentials.
|
||||
|
@ -44,6 +44,7 @@ sys.path.append([os.path.abspath('../keystone'),
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.ifconfig',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.pngmath',
|
||||
|
@ -63,4 +63,4 @@ Curl examples
|
||||
:maxdepth: 1
|
||||
|
||||
adminAPI_curl_examples
|
||||
serviceAPI_curl_examples
|
||||
serviceAPI_curl_examples
|
||||
|
@ -27,19 +27,35 @@ Using Keystone
|
||||
==============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:maxdepth: 1
|
||||
|
||||
installing
|
||||
gettingstarted
|
||||
installing
|
||||
gettingstarted
|
||||
|
||||
Developer Docs
|
||||
==============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:maxdepth: 1
|
||||
|
||||
architecture
|
||||
community
|
||||
architecture
|
||||
community
|
||||
|
||||
Man Pages
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
man/keystonemanage.rst
|
||||
|
||||
Source Code
|
||||
===========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
sourcecode/autoindex
|
||||
|
||||
Outstanding Documentation Tasks
|
||||
===============================
|
||||
|
@ -134,3 +134,186 @@ Mac OSX
|
||||
|
||||
$> python setup.py develop
|
||||
|
||||
Configuring Keystone
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Once Keystone is installed, it needs to be configured, and then any services
|
||||
that will be using Keystone need to be provided with service tokens. The
|
||||
service tokens are used to allow those services to validate users against
|
||||
Keystone's API interface.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
keystone.conf
|
||||
man/keystonemanage.rst
|
||||
|
||||
Once keystone is installed and running a number of elements need to be
|
||||
configured to provide data to authenticate against.
|
||||
|
||||
Creating Tenants
|
||||
################
|
||||
|
||||
* keystone-manage tenant add [tenant_name]
|
||||
|
||||
e.g.
|
||||
|
||||
keystone-manage tenant add admin
|
||||
keystone-manage tenant add demo
|
||||
|
||||
Creating Users
|
||||
##############
|
||||
|
||||
* keystone-manage user add [username] [password]
|
||||
|
||||
e.g.
|
||||
|
||||
keystone-manage tenant add admin secrete
|
||||
keystone-manage tenant add demo johny5oh
|
||||
|
||||
Creating Roles
|
||||
##############
|
||||
|
||||
* keystone-manage role add [username]
|
||||
* keystone-manage role grant [role] [username] ([tenant])
|
||||
|
||||
e.g.
|
||||
|
||||
keystone-manage role add Admin
|
||||
keystone-manage role add Member
|
||||
keystone-manage role add KeystoneAdmin
|
||||
keystone-manage role add KeystoneServiceAdmin
|
||||
|
||||
keystone-manage role grant Admin admin admin
|
||||
keystone-manage role grant Member demo demo
|
||||
keystone-manage role grant Admin admin demo
|
||||
|
||||
keystone-manage role grant Admin admin
|
||||
keystone-manage role grant KeystoneAdmin admin
|
||||
keystone-manage role grant KeystoneServiceAdmin admin
|
||||
|
||||
Creating Services
|
||||
#################
|
||||
|
||||
Define the services that will be using Keystone for authentication
|
||||
|
||||
* keystone-manage service add [servicename] [type] [description]
|
||||
|
||||
e.g.
|
||||
|
||||
keystone-manage service add nova compute "Nova Compute Service"
|
||||
keystone-manage service add glance image "Glance Image Service"
|
||||
keystone-manage service add keystone identity "Keystone Identity Service"
|
||||
|
||||
Creating Endpoints
|
||||
##################
|
||||
|
||||
|
||||
e.g.
|
||||
|
||||
keystone-manage endpointTemplates add RegionOne nova http://%HOST_IP%:8774/v1.1/%tenant_id% http://%HOST_IP%:8774/v1.1/%tenant_id% http://%HOST_IP%:8774/v1.1/%tenant_id% 1 1
|
||||
keystone-manage endpointTemplates add RegionOne glance http://%HOST_IP%:9292/v1.1/%tenant_id% http://%HOST_IP%:9292/v1.1/%tenant_id% http://%HOST_IP%:9292/v1.1/%tenant_id% 1 1
|
||||
keystone-manage endpointTemplates add RegionOne keystone http://%HOST_IP%:5000/v2.0 http://%HOST_IP%:35357/v2.0 http://%HOST_IP%:5000/v2.0 1 1
|
||||
keystone-manage endpointTemplates add RegionOne swift http://%HOST_IP%:8080/v1/AUTH_%tenant_id% http://%HOST_IP%:8080/ http://%HOST_IP%:8080/v1/AUTH_%tenant_id% 1 1
|
||||
|
||||
|
||||
|
||||
Defining an Administrative Service Token
|
||||
########################################
|
||||
|
||||
This token is arbitrary text which needs to be identical between Keystone
|
||||
and the services using Keystone to authenticate users, such as Nova, Swift,
|
||||
Glance, and Dashboard.
|
||||
|
||||
* keystone-manage token add [token] [tenant] [user] [expire datetime]
|
||||
|
||||
e.g.
|
||||
keystone-manage token add 999888777666 admin admin 2015-02-05T00:00
|
||||
|
||||
|
||||
Configuring Nova to use Keystone
|
||||
################################
|
||||
|
||||
To configure Nova to use Keystone for authentication, the Nova API service
|
||||
can be run against the api-paste file provided by Keystone. This is most
|
||||
easily accomplished by setting the --api_paste_config flag in nova.conf to
|
||||
point to examples/paste/nova-api-paste.ini from Keystone. This paste file
|
||||
included references to the WSGI authentication middleware provided with the
|
||||
keystone installation.
|
||||
|
||||
When configuring Nova, it is important to create a admin service token for
|
||||
the service (from the Configuration step above) and include that as the key
|
||||
'admin_token' in the nova-api-paste.ini. See the documented nova-api-paste.ini
|
||||
file for references.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
nova-api-paste
|
||||
|
||||
|
||||
Configuring Swift to use Keystone
|
||||
#################################
|
||||
|
||||
Similar to Nova, swift can be configured to use Keystone for authentication
|
||||
rather than it's built in 'tempauth'.
|
||||
|
||||
1. Add a service endpoint for Swift to Keystone
|
||||
|
||||
2. Configure the paste file for swift-proxy (/etc/swift/swift-proxy.conf)
|
||||
|
||||
3. Reconfigure Swift's proxy server to use Keystone instead of TempAuth.
|
||||
Here's an example `/etc/swift/proxy-server.conf`:
|
||||
|
||||
[DEFAULT]
|
||||
bind_port = 8888
|
||||
user = <user>
|
||||
|
||||
[pipeline:main]
|
||||
pipeline = catch_errors cache keystone proxy-server
|
||||
|
||||
[app:proxy-server]
|
||||
use = egg:swift#proxy
|
||||
account_autocreate = true
|
||||
|
||||
[filter:keystone]
|
||||
use = egg:keystone#tokenauth
|
||||
auth_protocol = http
|
||||
auth_host = 127.0.0.1
|
||||
auth_port = 35357
|
||||
admin_token = 999888777666
|
||||
delay_auth_decision = 0
|
||||
service_protocol = http
|
||||
service_host = 127.0.0.1
|
||||
service_port = 8100
|
||||
service_pass = dTpw
|
||||
|
||||
[filter:cache]
|
||||
use = egg:swift#memcache
|
||||
set log_name = cache
|
||||
|
||||
[filter:catch_errors]
|
||||
use = egg:swift#catch_errors
|
||||
|
||||
4. Restart swift
|
||||
|
||||
5. Verify that keystone is providing authentication to Swift
|
||||
|
||||
Use `swift` to check everything works (note: you currently have to create a
|
||||
container or upload something as your first action to have the account
|
||||
created; there's a Swift bug to be fixed soon):
|
||||
|
||||
$ swift -A http://127.0.0.1:5000/v1.0 -U joeuser -K secrete post container
|
||||
$ swift -A http://127.0.0.1:5000/v1.0 -U joeuser -K secrete stat -v
|
||||
StorageURL: http://127.0.0.1:8888/v1/AUTH_1234
|
||||
Auth Token: 74ce1b05-e839-43b7-bd76-85ef178726c3
|
||||
Account: AUTH_1234
|
||||
Containers: 1
|
||||
Objects: 0
|
||||
Bytes: 0
|
||||
Accept-Ranges: bytes
|
||||
X-Trans-Id: tx25c1a6969d8f4372b63912f411de3c3b
|
||||
|
||||
**Note: Keystone currently allows any valid token to do anything with any
|
||||
account.**
|
||||
|
||||
|
107
doc/source/keystone.conf.rst
Normal file
107
doc/source/keystone.conf.rst
Normal file
@ -0,0 +1,107 @@
|
||||
..
|
||||
Copyright 2011 OpenStack, LLC
|
||||
All Rights Reserved.
|
||||
|
||||
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.
|
||||
|
||||
keystone.conf example
|
||||
=====================
|
||||
::
|
||||
|
||||
[DEFAULT]
|
||||
# Show more verbose log output (sets INFO log level output)
|
||||
verbose = False
|
||||
|
||||
# Show debugging output in logs (sets DEBUG log level output)
|
||||
debug = False
|
||||
|
||||
# Which backend store should Keystone use by default.
|
||||
# Default: 'sqlite'
|
||||
# Available choices are 'sqlite' [future will include LDAP, PAM, etc]
|
||||
default_store = sqlite
|
||||
|
||||
# Log to this file. Make sure you do not set the same log
|
||||
# file for both the API and registry servers!
|
||||
log_file = %DEST%/keystone/keystone.log
|
||||
|
||||
# List of backends to be configured
|
||||
backends = keystone.backends.sqlalchemy
|
||||
#For LDAP support, add: ,keystone.backends.ldap
|
||||
|
||||
# Dictionary Maps every service to a header.Missing services would get header
|
||||
# X_(SERVICE_NAME) Key => Service Name, Value => Header Name
|
||||
service-header-mappings = {
|
||||
'nova' : 'X-Server-Management-Url',
|
||||
'swift' : 'X-Storage-Url',
|
||||
'cdn' : 'X-CDN-Management-Url'}
|
||||
|
||||
# Address to bind the API server
|
||||
# TODO Properties defined within app not available via pipeline.
|
||||
service_host = 0.0.0.0
|
||||
|
||||
# Port the bind the API server to
|
||||
service_port = 5000
|
||||
|
||||
# Address to bind the Admin API server
|
||||
admin_host = 0.0.0.0
|
||||
|
||||
# Port the bind the Admin API server to
|
||||
admin_port = 35357
|
||||
|
||||
#Role that allows to perform admin operations.
|
||||
keystone-admin-role = KeystoneAdmin
|
||||
|
||||
#Role that allows to perform service admin operations.
|
||||
keystone-service-admin-role = KeystoneServiceAdmin
|
||||
|
||||
[keystone.backends.sqlalchemy]
|
||||
# SQLAlchemy connection string for the reference implementation registry
|
||||
# server. Any valid SQLAlchemy connection string is fine.
|
||||
# See: http://bit.ly/ideIpI
|
||||
#sql_connection = sqlite:///keystone.db
|
||||
sql_connection = %SQL_CONN%
|
||||
backend_entities = ['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant',
|
||||
'User', 'Credentials', 'EndpointTemplates', 'Token',
|
||||
'Service']
|
||||
|
||||
# Period in seconds after which SQLAlchemy should reestablish its connection
|
||||
# to the database.
|
||||
sql_idle_timeout = 30
|
||||
|
||||
[pipeline:admin]
|
||||
pipeline =
|
||||
urlrewritefilter
|
||||
admin_api
|
||||
|
||||
[pipeline:keystone-legacy-auth]
|
||||
pipeline =
|
||||
urlrewritefilter
|
||||
legacy_auth
|
||||
RAX-KEY-extension
|
||||
service_api
|
||||
|
||||
[app:service_api]
|
||||
paste.app_factory = keystone.server:service_app_factory
|
||||
|
||||
[app:admin_api]
|
||||
paste.app_factory = keystone.server:admin_app_factory
|
||||
|
||||
[filter:urlrewritefilter]
|
||||
paste.filter_factory = keystone.middleware.url:filter_factory
|
||||
|
||||
[filter:legacy_auth]
|
||||
paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory
|
||||
|
||||
[filter:RAX-KEY-extension]
|
||||
paste.filter_factory = keystone.contrib.extensions.service.raxkey.frontend:filter_factory
|
||||
|
@ -21,22 +21,179 @@ SYNOPSIS
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
keystone-manage is a utility for managing and configuring a Keystone installation.
|
||||
One important use of keystone-manage is to setup the database. To do this run::
|
||||
keystone-manage is the command line tool that interacts with the keystone
|
||||
service to configure Keystone
|
||||
|
||||
keystone-manage db_sync
|
||||
USAGE
|
||||
=====
|
||||
|
||||
``keystone-manage [options] type action [additional args]``
|
||||
|
||||
user
|
||||
^^^^
|
||||
|
||||
* **user add** [username] [password]
|
||||
|
||||
adds a user to Keystone's data store
|
||||
|
||||
* **user list**
|
||||
|
||||
lists all users
|
||||
|
||||
* **user disable** [username]
|
||||
|
||||
disables the user *username*
|
||||
|
||||
tenant
|
||||
^^^^^^
|
||||
|
||||
* **tenant add** [tenant_name]
|
||||
|
||||
adds a tenant to Keystone's data store
|
||||
|
||||
* **tenant list**
|
||||
|
||||
lists all users
|
||||
|
||||
* **tenant disable** [tenant_name]
|
||||
|
||||
role
|
||||
^^^^
|
||||
|
||||
* **role add** [role_name]
|
||||
|
||||
adds a role
|
||||
|
||||
* **role list** ([tenant_name])
|
||||
|
||||
lists all roles, or all roles for tenant, if tenant_name is provided
|
||||
|
||||
* **role grant** [role_name] [username] ([tenant])
|
||||
|
||||
grants a role to a specific user. Granted globally if tenant_name is not
|
||||
provided or granted for a specific tenant if tenant_name is provided.
|
||||
|
||||
service
|
||||
^^^^^^^
|
||||
|
||||
* **service add** [name] [type] [description]
|
||||
|
||||
adds a service
|
||||
|
||||
* **service list**
|
||||
|
||||
lists all services with id, name, and type
|
||||
|
||||
endpointTemplate
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
* **endpointTemplate add** [region] [service] [public_url] [admin_url] [internal_url] [enabled] [is_global]
|
||||
|
||||
Add a service endpoint for keystone.
|
||||
|
||||
example::
|
||||
|
||||
keystone-manage endpointTemplates add RegionOne \
|
||||
keystone \
|
||||
http://keystone_host:5000/v2.0 \
|
||||
http://keystone_host:35357/v2.0 \
|
||||
http://keystone_host:5000/v2.0 \
|
||||
1 \
|
||||
1
|
||||
|
||||
|
||||
* **endpointTemplate list** ([tenant_name])
|
||||
|
||||
lists endpoint templates with service, region, and public_url. Restricted to
|
||||
tenant endpoints if tenant_name is provided.
|
||||
|
||||
token
|
||||
^^^^^
|
||||
|
||||
* **token add** [token] [username] [tenant] [expiration]
|
||||
|
||||
adds a token for a given user and tenant with an expiration
|
||||
|
||||
* **token list**
|
||||
|
||||
lists all tokens
|
||||
|
||||
* **token delete** [token]
|
||||
|
||||
deletes the identified token
|
||||
|
||||
endpoint
|
||||
^^^^^^^^
|
||||
|
||||
* **endpoint add** [tenant_name] [endpoint_template]
|
||||
|
||||
adds a tenant-specific endpoint
|
||||
|
||||
credentials
|
||||
^^^^^^^^^^^
|
||||
|
||||
* **credentials add** [username] [type] [key] [password] ([tenant_name])
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
|
||||
**General options**
|
||||
Common Options:
|
||||
|
||||
**-v, --verbose**
|
||||
Print more verbose output
|
||||
The following configuration options are common to all keystone
|
||||
programs.
|
||||
|
||||
**--sql_connection=CONN_STRING**
|
||||
A proper SQLAlchemy connection string as described
|
||||
`here <http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html?highlight=engine#sqlalchemy.create_engine>`_
|
||||
--version
|
||||
Show version number and exit
|
||||
|
||||
-h, --help
|
||||
Show this help message and exit
|
||||
|
||||
-v, --verbose
|
||||
Print more verbose output
|
||||
|
||||
-d, --debug
|
||||
Print debugging output to console
|
||||
|
||||
-c PATH, --config-file=PATH
|
||||
Path to the config file to use. When not specified
|
||||
(the default), we generally look at the first argument
|
||||
specified to be a config file, and if that is also
|
||||
missing, we search standard directories for a config
|
||||
file.
|
||||
|
||||
-p BIND_PORT, --port=BIND_PORT, --bind-port=BIND_PORT
|
||||
specifies port to listen on (default is 5000)
|
||||
|
||||
--host=BIND_HOST, --bind-host=BIND_HOST
|
||||
Specifies host address to listen on (default is all or
|
||||
0.0.0.0)
|
||||
|
||||
-t, --trace-calls
|
||||
Turns on call tracing for troubleshooting
|
||||
|
||||
Logging Options:
|
||||
|
||||
The following configuration options are specific to logging
|
||||
functionality for this program.
|
||||
|
||||
--log-config=PATH
|
||||
If this option is specified, the logging configuration
|
||||
file specified is used and overrides any other logging
|
||||
options specified. Please see the Python logging
|
||||
module documentation for details on logging
|
||||
configuration files.
|
||||
|
||||
--log-date-format=FORMAT
|
||||
Format string for %(asctime)s in log records. Default:
|
||||
%Y-%m-%d %H:%M:%S
|
||||
|
||||
--log-file=PATH
|
||||
(Optional) Name of log file to output to. If not set,
|
||||
logging will go to stdout.
|
||||
|
||||
--log-dir=LOG_DIR
|
||||
(Optional) The directory to keep log files in (will be
|
||||
prepended to --logfile)
|
||||
|
||||
FILES
|
||||
=====
|
||||
@ -46,9 +203,10 @@ None
|
||||
SEE ALSO
|
||||
========
|
||||
|
||||
* `Keystone <http://github.com/rackspace/keystone>`__
|
||||
* `Keystone <http://github.com/openstack/keystone>`__
|
||||
|
||||
BUGS
|
||||
====
|
||||
SOURCE
|
||||
======
|
||||
|
||||
* Keystone is sourced in GitHub so you can view current bugs at `Keystone <http://github.com/rackspace/keystone>`__
|
||||
* Keystone is sourced in GitHub `Keystone <http://github.com/openstack/keystone>`__
|
||||
* Keystone bugs are managed at Launchpad `Launchpad Keystone <https://bugs.launchpad.net/keystone>`__
|
||||
|
148
doc/source/nova-api-paste.rst
Normal file
148
doc/source/nova-api-paste.rst
Normal file
@ -0,0 +1,148 @@
|
||||
..
|
||||
Copyright 2011 OpenStack, LLC
|
||||
All Rights Reserved.
|
||||
|
||||
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.
|
||||
|
||||
nova-api-paste example
|
||||
======================
|
||||
::
|
||||
|
||||
#######
|
||||
# EC2 #
|
||||
#######
|
||||
|
||||
[composite:ec2]
|
||||
use = egg:Paste#urlmap
|
||||
/: ec2versions
|
||||
/services/Cloud: ec2cloud
|
||||
/services/Admin: ec2admin
|
||||
/latest: ec2metadata
|
||||
/2007-01-19: ec2metadata
|
||||
/2007-03-01: ec2metadata
|
||||
/2007-08-29: ec2metadata
|
||||
/2007-10-10: ec2metadata
|
||||
/2007-12-15: ec2metadata
|
||||
/2008-02-01: ec2metadata
|
||||
/2008-09-01: ec2metadata
|
||||
/2009-04-04: ec2metadata
|
||||
/1.0: ec2metadata
|
||||
|
||||
[pipeline:ec2cloud]
|
||||
pipeline = logrequest totoken authtoken keystonecontext cloudrequest authorizer ec2executor
|
||||
|
||||
[pipeline:ec2admin]
|
||||
pipeline = logrequest totoken authtoken keystonecontext adminrequest authorizer ec2executor
|
||||
|
||||
[pipeline:ec2metadata]
|
||||
pipeline = logrequest ec2md
|
||||
|
||||
[pipeline:ec2versions]
|
||||
pipeline = logrequest ec2ver
|
||||
|
||||
[filter:logrequest]
|
||||
paste.filter_factory = nova.api.ec2:RequestLogging.factory
|
||||
|
||||
[filter:ec2lockout]
|
||||
paste.filter_factory = nova.api.ec2:Lockout.factory
|
||||
|
||||
[filter:totoken]
|
||||
paste.filter_factory = keystone.middleware.ec2_token:EC2Token.factory
|
||||
|
||||
[filter:ec2noauth]
|
||||
paste.filter_factory = nova.api.ec2:NoAuth.factory
|
||||
|
||||
[filter:authenticate]
|
||||
paste.filter_factory = nova.api.ec2:Authenticate.factory
|
||||
|
||||
[filter:cloudrequest]
|
||||
controller = nova.api.ec2.cloud.CloudController
|
||||
paste.filter_factory = nova.api.ec2:Requestify.factory
|
||||
|
||||
[filter:adminrequest]
|
||||
controller = nova.api.ec2.admin.AdminController
|
||||
paste.filter_factory = nova.api.ec2:Requestify.factory
|
||||
|
||||
[filter:authorizer]
|
||||
paste.filter_factory = nova.api.ec2:Authorizer.factory
|
||||
|
||||
[app:ec2executor]
|
||||
paste.app_factory = nova.api.ec2:Executor.factory
|
||||
|
||||
[app:ec2ver]
|
||||
paste.app_factory = nova.api.ec2:Versions.factory
|
||||
|
||||
[app:ec2md]
|
||||
paste.app_factory = nova.api.ec2.metadatarequesthandler:MetadataRequestHandler.factory
|
||||
|
||||
#############
|
||||
# Openstack #
|
||||
#############
|
||||
|
||||
[composite:osapi]
|
||||
use = egg:Paste#urlmap
|
||||
/: osversions
|
||||
/v1.0: openstackapi10
|
||||
/v1.1: openstackapi11
|
||||
|
||||
[pipeline:openstackapi10]
|
||||
pipeline = faultwrap authtoken keystonecontext ratelimit osapiapp10
|
||||
|
||||
[pipeline:openstackapi11]
|
||||
pipeline = faultwrap authtoken keystonecontext ratelimit extensions osapiapp11
|
||||
|
||||
[filter:faultwrap]
|
||||
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
|
||||
|
||||
[filter:auth]
|
||||
paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory
|
||||
|
||||
[filter:noauth]
|
||||
paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory
|
||||
|
||||
[filter:ratelimit]
|
||||
paste.filter_factory = nova.api.openstack.limits:RateLimitingMiddleware.factory
|
||||
|
||||
[filter:extensions]
|
||||
paste.filter_factory = nova.api.openstack.extensions:ExtensionMiddleware.factory
|
||||
|
||||
[app:osapiapp10]
|
||||
paste.app_factory = nova.api.openstack:APIRouterV10.factory
|
||||
|
||||
[app:osapiapp11]
|
||||
paste.app_factory = nova.api.openstack:APIRouterV11.factory
|
||||
|
||||
[pipeline:osversions]
|
||||
pipeline = faultwrap osversionapp
|
||||
|
||||
[app:osversionapp]
|
||||
paste.app_factory = nova.api.openstack.versions:Versions.factory
|
||||
|
||||
##########
|
||||
# Shared #
|
||||
##########
|
||||
|
||||
[filter:keystonecontext]
|
||||
paste.filter_factory = keystone.middleware.nova_keystone_context:NovaKeystoneContext.factory
|
||||
|
||||
[filter:authtoken]
|
||||
paste.filter_factory = keystone.middleware.auth_token:filter_factory
|
||||
service_protocol = http
|
||||
service_host = 127.0.0.1
|
||||
service_port = 5000
|
||||
auth_host = 127.0.0.1
|
||||
auth_port = 35357
|
||||
auth_protocol = http
|
||||
auth_uri = http://127.0.0.1:5000/
|
||||
admin_token = 999888777666
|
||||
|
@ -206,11 +206,9 @@ class FakeLDAP(object):
|
||||
def modify_s(self, dn, attrs):
|
||||
"""Modify the object at dn using the attribute list.
|
||||
|
||||
Args:
|
||||
dn -- a dn
|
||||
attrs -- a list of tuples in the following form:
|
||||
([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
|
||||
|
||||
:param dn: an LDAP DN
|
||||
:param attrs: a list of tuples in the following form:
|
||||
([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
|
||||
"""
|
||||
if server_fail:
|
||||
raise ldap.SERVER_DOWN
|
||||
|
@ -47,7 +47,8 @@ def parse_options(parser, cli_args=None):
|
||||
:param parser: The option parser
|
||||
:param cli_args: (Optional) Set of arguments to process. If not present,
|
||||
sys.argv[1:] is used.
|
||||
:retval tuple of (options, args)
|
||||
:returns: tuple of (options, args)
|
||||
|
||||
"""
|
||||
|
||||
(options, args) = parser.parse_args(cli_args)
|
||||
@ -187,14 +188,16 @@ def find_config_file(options, args):
|
||||
* If --config-file option is used, use that
|
||||
* If args[0] is a file, use that
|
||||
* Search for keystone.conf in standard directories:
|
||||
* .
|
||||
* ~.keystone/
|
||||
* ~
|
||||
* /etc/keystone
|
||||
* /etc
|
||||
:if no config file is given get from possible_topdir/etc/keystone.conf
|
||||
|
||||
:retval Full path to config file, or None if no config file found
|
||||
* .
|
||||
* ~.keystone/
|
||||
* ~
|
||||
* /etc/keystone
|
||||
* /etc
|
||||
|
||||
If no config file is given get from possible_topdir/etc/keystone.conf
|
||||
|
||||
:returns: Full path to config file, or None if no config file found
|
||||
"""
|
||||
POSSIBLE_TOPDIR = os.path.normpath(os.path.join(\
|
||||
os.path.abspath(sys.argv[0]),
|
||||
@ -234,9 +237,11 @@ def load_paste_config(app_name, options, args):
|
||||
config file path and a configuration mapping from a paste config file.
|
||||
|
||||
We search for the paste config file in the following order:
|
||||
|
||||
* If --config-file option is used, use that
|
||||
* If args[0] is a file, use that
|
||||
* Search for keystone.conf in standard directories:
|
||||
|
||||
* .
|
||||
* ~.keystone/
|
||||
* ~
|
||||
@ -248,10 +253,9 @@ def load_paste_config(app_name, options, args):
|
||||
the config file.
|
||||
:param options: Set of typed options returned from parse_options()
|
||||
:param args: Command line arguments from argv[1:]
|
||||
:retval Tuple of (conf_file, conf)
|
||||
|
||||
:raises RuntimeError when config file cannot be located or there was a
|
||||
problem loading the configuration file.
|
||||
:returns: Tuple of (conf_file, conf)
|
||||
:raises: RuntimeError when config file cannot be located or there was a
|
||||
problem loading the configuration file.
|
||||
"""
|
||||
conf_file = find_config_file(options, args)
|
||||
if not conf_file:
|
||||
@ -299,6 +303,7 @@ def load_paste_app(app_name, options, args):
|
||||
* If --config-file option is used, use that
|
||||
* If args[0] is a file, use that
|
||||
* Search for keystone.conf in standard directories:
|
||||
|
||||
* .
|
||||
* ~.keystone/
|
||||
* ~
|
||||
@ -308,9 +313,8 @@ def load_paste_app(app_name, options, args):
|
||||
:param app_name: Name of the application to load (server, admin, proxy, ..)
|
||||
:param options: Set of typed options returned from parse_options()
|
||||
:param args: Command line arguments from argv[1:]
|
||||
|
||||
:raises RuntimeError when config file cannot be located or application
|
||||
cannot be loaded from config file
|
||||
:raises: RuntimeError when config file cannot be located or application
|
||||
cannot be loaded from config file
|
||||
"""
|
||||
conf_file, conf = load_paste_config(app_name, options, args)
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import keystone.backends.api
|
||||
from keystone.backends.api import BaseUserAPI
|
||||
|
||||
|
||||
#Base APIs
|
||||
|
@ -21,10 +21,11 @@
|
||||
BASIC AUTH MIDDLEWARE - STUB
|
||||
|
||||
This WSGI component should perform multiple jobs:
|
||||
- validate incoming basic claims
|
||||
- perform all basic auth interactions with clients
|
||||
- collect and forward identity information from the authentication process
|
||||
such as user name, groups, etc...
|
||||
|
||||
* validate incoming basic claims
|
||||
* perform all basic auth interactions with clients
|
||||
* collect and forward identity information from the authentication process
|
||||
such as user name, groups, etc...
|
||||
|
||||
This is an Auth component as per: http://wiki.openstack.org/openstack-authn
|
||||
|
||||
|
@ -20,32 +20,48 @@
|
||||
TOKEN-BASED AUTH MIDDLEWARE
|
||||
|
||||
This WSGI component performs multiple jobs:
|
||||
- it verifies that incoming client requests have valid tokens by verifying
|
||||
tokens with the auth service.
|
||||
- it will reject unauthenticated requests UNLESS it is in 'delay_auth_decision'
|
||||
mode, which means the final decision is delegated to the downstream WSGI
|
||||
component (usually the OpenStack service)
|
||||
- it will collect and forward identity information from a valid token
|
||||
such as user name etc...
|
||||
|
||||
* it verifies that incoming client requests have valid tokens by verifying
|
||||
tokens with the auth service.
|
||||
* it will reject unauthenticated requests UNLESS it is in 'delay_auth_decision'
|
||||
mode, which means the final decision is delegated to the downstream WSGI
|
||||
component (usually the OpenStack service)
|
||||
* it will collect and forward identity information from a valid token
|
||||
such as user name etc...
|
||||
|
||||
Refer to: http://wiki.openstack.org/openstack-authn
|
||||
|
||||
|
||||
HEADERS
|
||||
-------
|
||||
Headers starting with HTTP_ is a standard http header
|
||||
Headers starting with HTTP_X is an extended http header
|
||||
|
||||
> Coming in from initial call from client or customer
|
||||
HTTP_X_AUTH_TOKEN : the client token being passed in
|
||||
HTTP_X_STORAGE_TOKEN: the client token being passed in (legacy Rackspace use)
|
||||
to support cloud files
|
||||
> Used for communication between components
|
||||
www-authenticate : only used if this component is being used remotely
|
||||
HTTP_AUTHORIZATION : basic auth password used to validate the connection
|
||||
* Headers starting with HTTP\_ is a standard http header
|
||||
* Headers starting with HTTP_X is an extended http header
|
||||
|
||||
> What we add to the request for use by the OpenStack service
|
||||
HTTP_X_AUTHORIZATION: the client identity being passed in
|
||||
Coming in from initial call from client or customer
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
HTTP_X_AUTH_TOKEN
|
||||
the client token being passed in
|
||||
|
||||
HTTP_X_STORAGE_TOKEN
|
||||
the client token being passed in (legacy Rackspace use) to support
|
||||
cloud files
|
||||
|
||||
Used for communication between components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
www-authenticate
|
||||
only used if this component is being used remotely
|
||||
|
||||
HTTP_AUTHORIZATION
|
||||
basic auth password used to validate the connection
|
||||
|
||||
What we add to the request for use by the OpenStack service
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
HTTP_X_AUTHORIZATION
|
||||
the client identity being passed in
|
||||
|
||||
"""
|
||||
|
||||
|
@ -38,19 +38,34 @@ Refer to: http://wiki.openstack.org/openstack-authn
|
||||
|
||||
HEADERS
|
||||
-------
|
||||
HTTP_ is a standard http header
|
||||
HTTP_X is an extended http header
|
||||
|
||||
> Coming in from initial call
|
||||
HTTP_X_AUTH_TOKEN : the client token being passed in
|
||||
HTTP_X_STORAGE_TOKEN: the client token being passed in (legacy Rackspace use)
|
||||
to support cloud files
|
||||
> Used for communication between components
|
||||
www-authenticate : only used if this component is being used remotely
|
||||
HTTP_AUTHORIZATION : basic auth password used to validate the connection
|
||||
* HTTP\_ is a standard http header
|
||||
* HTTP_X is an extended http header
|
||||
|
||||
> What we add to the request for use by the OpenStack service
|
||||
HTTP_X_AUTHORIZATION: the client identity being passed in
|
||||
Coming in from initial call
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
HTTP_X_AUTH_TOKEN
|
||||
the client token being passed in
|
||||
|
||||
HTTP_X_STORAGE_TOKEN
|
||||
the client token being passed in (legacy Rackspace use) to support
|
||||
cloud files
|
||||
|
||||
Used for communication between components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
www-authenticate
|
||||
only used if this component is being used remotely
|
||||
|
||||
HTTP_AUTHORIZATION
|
||||
basic auth password used to validate the connection
|
||||
|
||||
What we add to the request for use by the OpenStack service
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
HTTP_X_AUTHORIZATION
|
||||
the client identity being passed in
|
||||
|
||||
|
||||
"""
|
||||
|
@ -20,19 +20,34 @@ Service that stores identities and issues and manages tokens
|
||||
|
||||
HEADERS
|
||||
-------
|
||||
HTTP_ is a standard http header
|
||||
HTTP_X is an extended http header
|
||||
|
||||
> Coming in from initial call
|
||||
HTTP_X_AUTH_TOKEN : the client token being passed in
|
||||
HTTP_X_STORAGE_TOKEN: the client token being passed in (legacy Rackspace use)
|
||||
to support cloud files
|
||||
> Used for communication between components
|
||||
www-authenticate : only used if this component is being used remotely
|
||||
HTTP_AUTHORIZATION : basic auth password used to validate the connection
|
||||
* HTTP\_ is a standard http header
|
||||
* HTTP_X is an extended http header
|
||||
|
||||
> What we add to the request for use by the OpenStack SERVICE
|
||||
HTTP_X_AUTHORIZATION: the client identity being passed in
|
||||
Coming in from initial call
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
HTTP_X_AUTH_TOKEN
|
||||
the client token being passed in
|
||||
|
||||
HTTP_X_STORAGE_TOKEN
|
||||
the client token being passed in (legacy Rackspace use) to support
|
||||
cloud files
|
||||
|
||||
Used for communication between components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
www-authenticate
|
||||
only used if this component is being used remotely
|
||||
|
||||
HTTP_AUTHORIZATION
|
||||
basic auth password used to validate the connection
|
||||
|
||||
What we add to the request for use by the OpenStack SERVICE
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
HTTP_X_AUTHORIZATION
|
||||
the client identity being passed in
|
||||
|
||||
"""
|
||||
from keystone.routers.service import ServiceApi
|
||||
|
@ -59,16 +59,18 @@ class RestfulTestCase(HttpTestCase):
|
||||
|
||||
Dynamically encodes json or xml as request body if one is provided.
|
||||
|
||||
WARNING: Existing Content-Type header will be overwritten.
|
||||
WARNING: If both as_json and as_xml are provided, as_xml is ignored.
|
||||
WARNING: If either as_json or as_xml AND a body is provided, the body
|
||||
is ignored.
|
||||
.. WARNING::
|
||||
* Existing Content-Type header will be overwritten.
|
||||
* If both as_json and as_xml are provided, as_xml is ignored.
|
||||
* If either as_json or as_xml AND a body is provided, the body
|
||||
is ignored.
|
||||
|
||||
Dynamically returns 'as_json' or 'as_xml' attribute based on the
|
||||
detected response type, and fails the current test case if
|
||||
unsuccessful.
|
||||
|
||||
response.as_json: standard python dictionary
|
||||
|
||||
response.as_xml: as_etree.ElementTree
|
||||
"""
|
||||
|
||||
|
@ -144,7 +144,7 @@ class ServiceAPITest(unittest.TestCase):
|
||||
"""
|
||||
Creates a tenant fixture.
|
||||
|
||||
:params **kwargs: Attributes of the tenant to create
|
||||
:params \*\*kwargs: Attributes of the tenant to create
|
||||
"""
|
||||
values = kwargs.copy()
|
||||
user = db_api.USER.get_by_name(values['user_name'])
|
||||
@ -159,7 +159,7 @@ class ServiceAPITest(unittest.TestCase):
|
||||
"""
|
||||
Creates a tenant fixture.
|
||||
|
||||
:params **kwargs: Attributes of the tenant to create
|
||||
:params \*\*kwargs: Attributes of the tenant to create
|
||||
"""
|
||||
values = kwargs.copy()
|
||||
tenant = db_api.TENANT.create(values)
|
||||
@ -171,7 +171,7 @@ class ServiceAPITest(unittest.TestCase):
|
||||
Creates a user fixture. If the user's tenant ID is set, and the tenant
|
||||
does not exist in the database, the tenant is created.
|
||||
|
||||
:params **kwargs: Attributes of the user to create
|
||||
:params \*\*kwargs: Attributes of the user to create
|
||||
"""
|
||||
values = kwargs.copy()
|
||||
tenant_name = values.get('tenant_name')
|
||||
@ -189,7 +189,7 @@ class ServiceAPITest(unittest.TestCase):
|
||||
"""
|
||||
Creates a token fixture.
|
||||
|
||||
:params **kwargs: Attributes of the token to create
|
||||
:params \*\*kwargs: Attributes of the token to create
|
||||
"""
|
||||
values = kwargs.copy()
|
||||
token = db_api.TOKEN.create(values)
|
||||
|
8
setup.py
8
setup.py
@ -14,8 +14,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
import keystone
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
cmdclass = {}
|
||||
|
||||
@ -27,6 +30,9 @@ try:
|
||||
|
||||
class local_BuildDoc(BuildDoc):
|
||||
def run(self):
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
subprocess.Popen(["python", "generate_autodoc_index.py"],
|
||||
cwd=os.path.join(base_dir, "doc")).communicate()
|
||||
for builder in ['html', 'man']:
|
||||
self.builder = builder
|
||||
self.finalize_options()
|
||||
|
Loading…
Reference in New Issue
Block a user