Set pep8 version to 1.1 in test_requires
* Fixes bug 1007520 * Changes in pep8 cause new failures Change-Id: Ie678f01a5008b0df6ef43a360b599890cab40776
This commit is contained in:
parent
1711f1f9bb
commit
93f9fa75fa
|
@ -197,7 +197,7 @@ class ManagerWithFind(Manager):
|
|||
for obj in self.list():
|
||||
try:
|
||||
if all(getattr(obj, attr) == value
|
||||
for (attr, value) in searches):
|
||||
for (attr, value) in searches):
|
||||
found.append(obj)
|
||||
except AttributeError:
|
||||
continue
|
||||
|
@ -265,7 +265,7 @@ class Resource(object):
|
|||
|
||||
def __repr__(self):
|
||||
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
|
||||
k != 'manager')
|
||||
k != 'manager')
|
||||
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
|
||||
return "<%s %s>" % (self.__class__.__name__, info)
|
||||
|
||||
|
|
|
@ -160,12 +160,12 @@ class HTTPClient(httplib2.Http):
|
|||
self.auth_token = self.service_catalog.get_token()
|
||||
|
||||
management_url = self.service_catalog.url_for(
|
||||
attr='region',
|
||||
filter_value=self.region_name,
|
||||
endpoint_type=self.endpoint_type,
|
||||
service_type=self.service_type,
|
||||
service_name=self.service_name,
|
||||
volume_service_name=self.volume_service_name,)
|
||||
attr='region',
|
||||
filter_value=self.region_name,
|
||||
endpoint_type=self.endpoint_type,
|
||||
service_type=self.service_type,
|
||||
service_name=self.service_name,
|
||||
volume_service_name=self.volume_service_name)
|
||||
self.management_url = management_url.rstrip('/')
|
||||
return None
|
||||
except exceptions.AmbiguousEndpoints:
|
||||
|
@ -219,8 +219,8 @@ class HTTPClient(httplib2.Http):
|
|||
# TODO(sandy): Assume admin endpoint is 35357 for now.
|
||||
# Ideally this is going to have to be provided by the service catalog.
|
||||
new_netloc = netloc.replace(':%d' % port, ':%d' % (35357,))
|
||||
admin_url = urlparse.urlunsplit(
|
||||
(scheme, new_netloc, path, query, frag))
|
||||
admin_url = urlparse.urlunsplit((scheme, new_netloc,
|
||||
path, query, frag))
|
||||
|
||||
auth_url = self.auth_url
|
||||
if self.version == "v2.0":
|
||||
|
@ -277,8 +277,8 @@ class HTTPClient(httplib2.Http):
|
|||
def _v2_auth(self, url):
|
||||
"""Authenticate against a v2.0 auth service."""
|
||||
body = {"auth": {
|
||||
"passwordCredentials": {"username": self.user,
|
||||
"password": self.password}}}
|
||||
"passwordCredentials": {"username": self.user,
|
||||
"password": self.password}}}
|
||||
|
||||
if self.projectid:
|
||||
body['auth']['tenantName'] = self.projectid
|
||||
|
@ -289,9 +289,9 @@ class HTTPClient(httplib2.Http):
|
|||
"""Authenticate against the Rackspace auth service."""
|
||||
body = {"auth": {
|
||||
"RAX-KSKEY:apiKeyCredentials": {
|
||||
"username": self.user,
|
||||
"apiKey": self.password,
|
||||
"tenantName": self.projectid}}}
|
||||
"username": self.user,
|
||||
"apiKey": self.password,
|
||||
"tenantName": self.projectid}}}
|
||||
|
||||
self._authenticate(url, body)
|
||||
|
||||
|
@ -319,7 +319,7 @@ def get_client_class(version):
|
|||
client_path = version_map[str(version)]
|
||||
except (KeyError, ValueError):
|
||||
msg = "Invalid client version '%s'. must be one of: %s" % (
|
||||
(version, ', '.join(version_map.keys())))
|
||||
(version, ', '.join(version_map.keys())))
|
||||
raise exceptions.UnsupportedVersion(msg)
|
||||
|
||||
return utils.import_class(client_path)
|
||||
|
|
|
@ -117,7 +117,8 @@ class HTTPNotImplemented(ClientException):
|
|||
#
|
||||
# Instead, we have to hardcode it:
|
||||
_code_map = dict((c.http_status, c) for c in [BadRequest, Unauthorized,
|
||||
Forbidden, NotFound, OverLimit, HTTPNotImplemented])
|
||||
Forbidden, NotFound,
|
||||
OverLimit, HTTPNotImplemented])
|
||||
|
||||
|
||||
def from_response(response, body):
|
||||
|
|
|
@ -29,8 +29,8 @@ class ServiceCatalog(object):
|
|||
return self.catalog['access']['token']['id']
|
||||
|
||||
def url_for(self, attr=None, filter_value=None,
|
||||
service_type=None, endpoint_type='publicURL',
|
||||
service_name=None, volume_service_name=None):
|
||||
service_type=None, endpoint_type='publicURL',
|
||||
service_name=None, volume_service_name=None):
|
||||
"""Fetch the public URL from the Compute service for
|
||||
a particular endpoint attribute. If none given, return
|
||||
the first. See tests for sample service catalog."""
|
||||
|
@ -72,6 +72,6 @@ class ServiceCatalog(object):
|
|||
raise cinderclient.exceptions.EndpointNotFound()
|
||||
elif len(matching_endpoints) > 1:
|
||||
raise cinderclient.exceptions.AmbiguousEndpoints(
|
||||
endpoints=matching_endpoints)
|
||||
endpoints=matching_endpoints)
|
||||
else:
|
||||
return matching_endpoints[0][endpoint_type]
|
||||
|
|
|
@ -69,7 +69,7 @@ class OpenStackCinderShell(object):
|
|||
parser = CinderClientArgumentParser(
|
||||
prog='cinder',
|
||||
description=__doc__.strip(),
|
||||
epilog='See "cinder help COMMAND" '\
|
||||
epilog='See "cinder help COMMAND" '
|
||||
'for help on a specific command.',
|
||||
add_help=False,
|
||||
formatter_class=OpenStackHelpFormatter,
|
||||
|
@ -77,87 +77,93 @@ class OpenStackCinderShell(object):
|
|||
|
||||
# Global arguments
|
||||
parser.add_argument('-h', '--help',
|
||||
action='store_true',
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
action='store_true',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument('--debug',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help="Print debugging output")
|
||||
default=False,
|
||||
action='store_true',
|
||||
help="Print debugging output")
|
||||
|
||||
parser.add_argument('--os_username',
|
||||
default=utils.env('OS_USERNAME', 'CINDER_USERNAME'),
|
||||
help='Defaults to env[OS_USERNAME].')
|
||||
default=utils.env('OS_USERNAME',
|
||||
'CINDER_USERNAME'),
|
||||
help='Defaults to env[OS_USERNAME].')
|
||||
|
||||
parser.add_argument('--os_password',
|
||||
default=utils.env('OS_PASSWORD', 'CINDER_PASSWORD'),
|
||||
help='Defaults to env[OS_PASSWORD].')
|
||||
default=utils.env('OS_PASSWORD',
|
||||
'CINDER_PASSWORD'),
|
||||
help='Defaults to env[OS_PASSWORD].')
|
||||
|
||||
parser.add_argument('--os_tenant_name',
|
||||
default=utils.env('OS_TENANT_NAME', 'CINDER_PROJECT_ID'),
|
||||
help='Defaults to env[OS_TENANT_NAME].')
|
||||
default=utils.env('OS_TENANT_NAME',
|
||||
'CINDER_PROJECT_ID'),
|
||||
help='Defaults to env[OS_TENANT_NAME].')
|
||||
|
||||
parser.add_argument('--os_auth_url',
|
||||
default=utils.env('OS_AUTH_URL', 'CINDER_URL'),
|
||||
help='Defaults to env[OS_AUTH_URL].')
|
||||
default=utils.env('OS_AUTH_URL',
|
||||
'CINDER_URL'),
|
||||
help='Defaults to env[OS_AUTH_URL].')
|
||||
|
||||
parser.add_argument('--os_region_name',
|
||||
default=utils.env('OS_REGION_NAME', 'CINDER_REGION_NAME'),
|
||||
help='Defaults to env[OS_REGION_NAME].')
|
||||
default=utils.env('OS_REGION_NAME',
|
||||
'CINDER_REGION_NAME'),
|
||||
help='Defaults to env[OS_REGION_NAME].')
|
||||
|
||||
parser.add_argument('--service_type',
|
||||
help='Defaults to compute for most actions')
|
||||
help='Defaults to compute for most actions')
|
||||
|
||||
parser.add_argument('--service_name',
|
||||
default=utils.env('CINDER_SERVICE_NAME'),
|
||||
help='Defaults to env[CINDER_SERVICE_NAME]')
|
||||
default=utils.env('CINDER_SERVICE_NAME'),
|
||||
help='Defaults to env[CINDER_SERVICE_NAME]')
|
||||
|
||||
parser.add_argument('--volume_service_name',
|
||||
default=utils.env('CINDER_VOLUME_SERVICE_NAME'),
|
||||
help='Defaults to env[CINDER_VOLUME_SERVICE_NAME]')
|
||||
default=utils.env('CINDER_VOLUME_SERVICE_NAME'),
|
||||
help='Defaults to env[CINDER_VOLUME_SERVICE_NAME]')
|
||||
|
||||
parser.add_argument('--endpoint_type',
|
||||
default=utils.env('CINDER_ENDPOINT_TYPE',
|
||||
default=DEFAULT_CINDER_ENDPOINT_TYPE),
|
||||
help='Defaults to env[CINDER_ENDPOINT_TYPE] or '
|
||||
+ DEFAULT_CINDER_ENDPOINT_TYPE + '.')
|
||||
default=utils.env('CINDER_ENDPOINT_TYPE',
|
||||
default=DEFAULT_CINDER_ENDPOINT_TYPE),
|
||||
help='Defaults to env[CINDER_ENDPOINT_TYPE] or '
|
||||
+ DEFAULT_CINDER_ENDPOINT_TYPE + '.')
|
||||
|
||||
parser.add_argument('--os_volume_api_version',
|
||||
default=utils.env('OS_VOLUME_API_VERSION',
|
||||
default=DEFAULT_OS_VOLUME_API_VERSION),
|
||||
help='Accepts 1, defaults to env[OS_VOLUME_API_VERSION].')
|
||||
default=utils.env('OS_VOLUME_API_VERSION',
|
||||
default=DEFAULT_OS_VOLUME_API_VERSION),
|
||||
help='Accepts 1,defaults '
|
||||
'to env[OS_VOLUME_API_VERSION].')
|
||||
|
||||
parser.add_argument('--insecure',
|
||||
default=utils.env('CINDERCLIENT_INSECURE', default=False),
|
||||
action='store_true',
|
||||
help=argparse.SUPPRESS)
|
||||
default=utils.env('CINDERCLIENT_INSECURE',
|
||||
default=False),
|
||||
action='store_true',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
# FIXME(dtroyer): The args below are here for diablo compatibility,
|
||||
# remove them in folsum cycle
|
||||
|
||||
# alias for --os_username, left in for backwards compatibility
|
||||
parser.add_argument('--username',
|
||||
help='Deprecated')
|
||||
help='Deprecated')
|
||||
|
||||
# alias for --os_region_name, left in for backwards compatibility
|
||||
parser.add_argument('--region_name',
|
||||
help='Deprecated')
|
||||
help='Deprecated')
|
||||
|
||||
# alias for --os_password, left in for backwards compatibility
|
||||
parser.add_argument('--apikey', '--password', dest='apikey',
|
||||
default=utils.env('CINDER_API_KEY'),
|
||||
help='Deprecated')
|
||||
default=utils.env('CINDER_API_KEY'),
|
||||
help='Deprecated')
|
||||
|
||||
# alias for --os_tenant_name, left in for backward compatibility
|
||||
parser.add_argument('--projectid', '--tenant_name', dest='projectid',
|
||||
default=utils.env('CINDER_PROJECT_ID'),
|
||||
help='Deprecated')
|
||||
default=utils.env('CINDER_PROJECT_ID'),
|
||||
help='Deprecated')
|
||||
|
||||
# alias for --os_auth_url, left in for backward compatibility
|
||||
parser.add_argument('--url', '--auth_url', dest='url',
|
||||
default=utils.env('CINDER_URL'),
|
||||
help='Deprecated')
|
||||
default=utils.env('CINDER_URL'),
|
||||
help='Deprecated')
|
||||
|
||||
return parser
|
||||
|
||||
|
@ -222,10 +228,11 @@ class OpenStackCinderShell(object):
|
|||
yield name, module
|
||||
|
||||
def _add_bash_completion_subparser(self, subparsers):
|
||||
subparser = subparsers.add_parser('bash_completion',
|
||||
subparser = subparsers.add_parser(
|
||||
'bash_completion',
|
||||
add_help=False,
|
||||
formatter_class=OpenStackHelpFormatter
|
||||
)
|
||||
formatter_class=OpenStackHelpFormatter)
|
||||
|
||||
self.subcommands['bash_completion'] = subparser
|
||||
subparser.set_defaults(func=self.do_bash_completion)
|
||||
|
||||
|
@ -238,16 +245,17 @@ class OpenStackCinderShell(object):
|
|||
help = desc.strip().split('\n')[0]
|
||||
arguments = getattr(callback, 'arguments', [])
|
||||
|
||||
subparser = subparsers.add_parser(command,
|
||||
subparser = subparsers.add_parser(
|
||||
command,
|
||||
help=help,
|
||||
description=desc,
|
||||
add_help=False,
|
||||
formatter_class=OpenStackHelpFormatter
|
||||
)
|
||||
formatter_class=OpenStackHelpFormatter)
|
||||
|
||||
subparser.add_argument('-h', '--help',
|
||||
action='help',
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
action='help',
|
||||
help=argparse.SUPPRESS,)
|
||||
|
||||
self.subcommands[command] = subparser
|
||||
for (args, kwargs) in arguments:
|
||||
subparser.add_argument(*args, **kwargs)
|
||||
|
@ -273,11 +281,11 @@ class OpenStackCinderShell(object):
|
|||
|
||||
# build available subcommands based on version
|
||||
self.extensions = self._discover_extensions(
|
||||
options.os_volume_api_version)
|
||||
options.os_volume_api_version)
|
||||
self._run_extension_hooks('__pre_parse_args__')
|
||||
|
||||
subcommand_parser = self.get_subcommand_parser(
|
||||
options.os_volume_api_version)
|
||||
options.os_volume_api_version)
|
||||
self.parser = subcommand_parser
|
||||
|
||||
if options.help and len(args) == 0:
|
||||
|
@ -296,16 +304,16 @@ class OpenStackCinderShell(object):
|
|||
return 0
|
||||
|
||||
(os_username, os_password, os_tenant_name, os_auth_url,
|
||||
os_region_name, endpoint_type, insecure,
|
||||
service_type, service_name, volume_service_name,
|
||||
username, apikey, projectid, url, region_name) = (
|
||||
args.os_username, args.os_password,
|
||||
args.os_tenant_name, args.os_auth_url,
|
||||
args.os_region_name, args.endpoint_type,
|
||||
args.insecure, args.service_type, args.service_name,
|
||||
args.volume_service_name, args.username,
|
||||
args.apikey, args.projectid,
|
||||
args.url, args.region_name)
|
||||
os_region_name, endpoint_type, insecure,
|
||||
service_type, service_name, volume_service_name,
|
||||
username, apikey, projectid, url, region_name) = (
|
||||
args.os_username, args.os_password,
|
||||
args.os_tenant_name, args.os_auth_url,
|
||||
args.os_region_name, args.endpoint_type,
|
||||
args.insecure, args.service_type, args.service_name,
|
||||
args.volume_service_name, args.username,
|
||||
args.apikey, args.projectid,
|
||||
args.url, args.region_name)
|
||||
|
||||
if not endpoint_type:
|
||||
endpoint_type = DEFAULT_CINDER_ENDPOINT_TYPE
|
||||
|
@ -320,31 +328,33 @@ class OpenStackCinderShell(object):
|
|||
if not utils.isunauthenticated(args.func):
|
||||
if not os_username:
|
||||
if not username:
|
||||
raise exc.CommandError("You must provide a username "
|
||||
"via either --os_username or env[OS_USERNAME]")
|
||||
raise exc.CommandError(
|
||||
"You must provide a username "
|
||||
"via either --os_username or env[OS_USERNAME]")
|
||||
else:
|
||||
os_username = username
|
||||
|
||||
if not os_password:
|
||||
if not apikey:
|
||||
raise exc.CommandError("You must provide a password "
|
||||
"via either --os_password or via "
|
||||
"env[OS_PASSWORD]")
|
||||
"via either --os_password or via "
|
||||
"env[OS_PASSWORD]")
|
||||
else:
|
||||
os_password = apikey
|
||||
|
||||
if not os_tenant_name:
|
||||
if not projectid:
|
||||
raise exc.CommandError("You must provide a tenant name "
|
||||
"via either --os_tenant_name or "
|
||||
"env[OS_TENANT_NAME]")
|
||||
"via either --os_tenant_name or "
|
||||
"env[OS_TENANT_NAME]")
|
||||
else:
|
||||
os_tenant_name = projectid
|
||||
|
||||
if not os_auth_url:
|
||||
if not url:
|
||||
raise exc.CommandError("You must provide an auth url "
|
||||
"via either --os_auth_url or env[OS_AUTH_URL]")
|
||||
raise exc.CommandError(
|
||||
"You must provide an auth url "
|
||||
"via either --os_auth_url or env[OS_AUTH_URL]")
|
||||
else:
|
||||
os_auth_url = url
|
||||
|
||||
|
@ -352,19 +362,23 @@ class OpenStackCinderShell(object):
|
|||
os_region_name = region_name
|
||||
|
||||
if not os_tenant_name:
|
||||
raise exc.CommandError("You must provide a tenant name "
|
||||
"via either --os_tenant_name or env[OS_TENANT_NAME]")
|
||||
raise exc.CommandError(
|
||||
"You must provide a tenant name "
|
||||
"via either --os_tenant_name or env[OS_TENANT_NAME]")
|
||||
|
||||
if not os_auth_url:
|
||||
raise exc.CommandError("You must provide an auth url "
|
||||
"via either --os_auth_url or env[OS_AUTH_URL]")
|
||||
raise exc.CommandError(
|
||||
"You must provide an auth url "
|
||||
"via either --os_auth_url or env[OS_AUTH_URL]")
|
||||
|
||||
self.cs = client.Client(options.os_volume_api_version, os_username,
|
||||
os_password, os_tenant_name, os_auth_url, insecure,
|
||||
region_name=os_region_name, endpoint_type=endpoint_type,
|
||||
extensions=self.extensions, service_type=service_type,
|
||||
service_name=service_name,
|
||||
volume_service_name=volume_service_name)
|
||||
os_password, os_tenant_name, os_auth_url,
|
||||
insecure, region_name=os_region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
extensions=self.extensions,
|
||||
service_type=service_type,
|
||||
service_name=service_name,
|
||||
volume_service_name=volume_service_name)
|
||||
|
||||
try:
|
||||
if not utils.isunauthenticated(args.func):
|
||||
|
@ -398,7 +412,7 @@ class OpenStackCinderShell(object):
|
|||
print ' '.join(commands | options)
|
||||
|
||||
@utils.arg('command', metavar='<subcommand>', nargs='?',
|
||||
help='Display help for <subcommand>')
|
||||
help='Display help for <subcommand>')
|
||||
def do_help(self, args):
|
||||
"""
|
||||
Display help about this program or one of its subcommands.
|
||||
|
|
|
@ -23,11 +23,11 @@ class Client(object):
|
|||
|
||||
# FIXME(jesse): project_id isn't required to authenticate
|
||||
def __init__(self, username, api_key, project_id, auth_url,
|
||||
insecure=False, timeout=None, proxy_tenant_id=None,
|
||||
proxy_token=None, region_name=None,
|
||||
endpoint_type='publicURL', extensions=None,
|
||||
service_type='compute', service_name=None,
|
||||
volume_service_name=None):
|
||||
insecure=False, timeout=None, proxy_tenant_id=None,
|
||||
proxy_token=None, region_name=None,
|
||||
endpoint_type='publicURL', extensions=None,
|
||||
service_type='compute', service_name=None,
|
||||
volume_service_name=None):
|
||||
# FIXME(comstud): Rename the api_key argument above when we
|
||||
# know it's not being used as keyword argument
|
||||
password = api_key
|
||||
|
@ -44,19 +44,20 @@ class Client(object):
|
|||
setattr(self, extension.name,
|
||||
extension.manager_class(self))
|
||||
|
||||
self.client = client.HTTPClient(username,
|
||||
password,
|
||||
project_id,
|
||||
auth_url,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
service_name=service_name,
|
||||
volume_service_name=volume_service_name)
|
||||
self.client = client.HTTPClient(
|
||||
username,
|
||||
password,
|
||||
project_id,
|
||||
auth_url,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
service_name=service_name,
|
||||
volume_service_name=volume_service_name)
|
||||
|
||||
def authenticate(self):
|
||||
"""
|
||||
|
|
|
@ -100,7 +100,7 @@ def do_list(cs, args):
|
|||
servers = [s.get('server_id') for s in vol.attachments]
|
||||
setattr(vol, 'attached_to', ','.join(map(str, servers)))
|
||||
utils.print_list(volumes, ['ID', 'Status', 'Display Name',
|
||||
'Size', 'Volume Type', 'Attached to'])
|
||||
'Size', 'Volume Type', 'Attached to'])
|
||||
|
||||
|
||||
@utils.arg('volume', metavar='<volume>', help='ID of the volume.')
|
||||
|
@ -112,31 +112,32 @@ def do_show(cs, args):
|
|||
|
||||
|
||||
@utils.arg('size',
|
||||
metavar='<size>',
|
||||
type=int,
|
||||
help='Size of volume in GB')
|
||||
@utils.arg('--snapshot_id',
|
||||
metavar='<size>',
|
||||
type=int,
|
||||
help='Size of volume in GB')
|
||||
@utils.arg(
|
||||
'--snapshot_id',
|
||||
metavar='<snapshot_id>',
|
||||
help='Optional snapshot id to create the volume from. (Default=None)',
|
||||
default=None)
|
||||
@utils.arg('--display_name', metavar='<display_name>',
|
||||
help='Optional volume name. (Default=None)',
|
||||
default=None)
|
||||
help='Optional volume name. (Default=None)',
|
||||
default=None)
|
||||
@utils.arg('--display_description', metavar='<display_description>',
|
||||
help='Optional volume description. (Default=None)',
|
||||
default=None)
|
||||
help='Optional volume description. (Default=None)',
|
||||
default=None)
|
||||
@utils.arg('--volume_type',
|
||||
metavar='<volume_type>',
|
||||
help='Optional volume type. (Default=None)',
|
||||
default=None)
|
||||
metavar='<volume_type>',
|
||||
help='Optional volume type. (Default=None)',
|
||||
default=None)
|
||||
@utils.service_type('volume')
|
||||
def do_create(cs, args):
|
||||
"""Add a new volume."""
|
||||
cs.volumes.create(args.size,
|
||||
args.snapshot_id,
|
||||
args.display_name,
|
||||
args.display_description,
|
||||
args.volume_type)
|
||||
args.snapshot_id,
|
||||
args.display_name,
|
||||
args.display_description,
|
||||
args.volume_type)
|
||||
|
||||
|
||||
@utils.arg('volume', metavar='<volume>', help='ID of the volume to delete.')
|
||||
|
@ -152,8 +153,8 @@ def do_snapshot_list(cs, args):
|
|||
"""List all the snapshots."""
|
||||
snapshots = cs.volume_snapshots.list()
|
||||
_translate_volume_snapshot_keys(snapshots)
|
||||
utils.print_list(snapshots, ['ID', 'Volume ID', 'Status', 'Display Name',
|
||||
'Size'])
|
||||
utils.print_list(snapshots,
|
||||
['ID', 'Volume ID', 'Status', 'Display Name', 'Size'])
|
||||
|
||||
|
||||
@utils.arg('snapshot', metavar='<snapshot>', help='ID of the snapshot.')
|
||||
|
@ -165,31 +166,32 @@ def do_snapshot_show(cs, args):
|
|||
|
||||
|
||||
@utils.arg('volume_id',
|
||||
metavar='<volume_id>',
|
||||
help='ID of the volume to snapshot')
|
||||
metavar='<volume_id>',
|
||||
help='ID of the volume to snapshot')
|
||||
@utils.arg('--force',
|
||||
metavar='<True|False>',
|
||||
help='Optional flag to indicate whether to snapshot a volume even if its '
|
||||
'attached to an instance. (Default=False)',
|
||||
default=False)
|
||||
metavar='<True|False>',
|
||||
help='Optional flag to indicate whether '
|
||||
'to snapshot a volume even if its '
|
||||
'attached to an instance. (Default=False)',
|
||||
default=False)
|
||||
@utils.arg('--display_name', metavar='<display_name>',
|
||||
help='Optional snapshot name. (Default=None)',
|
||||
default=None)
|
||||
help='Optional snapshot name. (Default=None)',
|
||||
default=None)
|
||||
@utils.arg('--display_description', metavar='<display_description>',
|
||||
help='Optional snapshot description. (Default=None)',
|
||||
default=None)
|
||||
help='Optional snapshot description. (Default=None)',
|
||||
default=None)
|
||||
@utils.service_type('volume')
|
||||
def do_snapshot_create(cs, args):
|
||||
"""Add a new snapshot."""
|
||||
cs.volume_snapshots.create(args.volume_id,
|
||||
args.force,
|
||||
args.display_name,
|
||||
args.display_description)
|
||||
args.force,
|
||||
args.display_name,
|
||||
args.display_description)
|
||||
|
||||
|
||||
@utils.arg('snapshot_id',
|
||||
metavar='<snapshot_id>',
|
||||
help='ID of the snapshot to delete.')
|
||||
metavar='<snapshot_id>',
|
||||
help='ID of the snapshot to delete.')
|
||||
@utils.service_type('volume')
|
||||
def do_snapshot_delete(cs, args):
|
||||
"""Remove a snapshot."""
|
||||
|
@ -209,8 +211,8 @@ def do_type_list(cs, args):
|
|||
|
||||
|
||||
@utils.arg('name',
|
||||
metavar='<name>',
|
||||
help="Name of the new flavor")
|
||||
metavar='<name>',
|
||||
help="Name of the new flavor")
|
||||
@utils.service_type('volume')
|
||||
def do_type_create(cs, args):
|
||||
"""Create a new volume type."""
|
||||
|
@ -219,8 +221,8 @@ def do_type_create(cs, args):
|
|||
|
||||
|
||||
@utils.arg('id',
|
||||
metavar='<id>',
|
||||
help="Unique ID of the volume type to delete")
|
||||
metavar='<id>',
|
||||
help="Unique ID of the volume type to delete")
|
||||
@utils.service_type('volume')
|
||||
def do_type_delete(cs, args):
|
||||
"""Delete a specific flavor"""
|
||||
|
|
|
@ -41,7 +41,7 @@ class SnapshotManager(base.ManagerWithFind):
|
|||
resource_class = Snapshot
|
||||
|
||||
def create(self, volume_id, force=False,
|
||||
display_name=None, display_description=None):
|
||||
display_name=None, display_description=None):
|
||||
|
||||
"""
|
||||
Create a snapshot of the given volume.
|
||||
|
@ -54,9 +54,9 @@ class SnapshotManager(base.ManagerWithFind):
|
|||
:rtype: :class:`Snapshot`
|
||||
"""
|
||||
body = {'snapshot': {'volume_id': volume_id,
|
||||
'force': force,
|
||||
'display_name': display_name,
|
||||
'display_description': display_description}}
|
||||
'force': force,
|
||||
'display_name': display_name,
|
||||
'display_description': display_description}}
|
||||
return self._create('/snapshots', body, 'snapshot')
|
||||
|
||||
def get(self, snapshot_id):
|
||||
|
|
|
@ -41,8 +41,8 @@ class VolumeManager(base.ManagerWithFind):
|
|||
resource_class = Volume
|
||||
|
||||
def create(self, size, snapshot_id=None,
|
||||
display_name=None, display_description=None,
|
||||
volume_type=None):
|
||||
display_name=None, display_description=None,
|
||||
volume_type=None):
|
||||
"""
|
||||
Create a volume.
|
||||
|
||||
|
@ -54,10 +54,10 @@ class VolumeManager(base.ManagerWithFind):
|
|||
:rtype: :class:`Volume`
|
||||
"""
|
||||
body = {'volume': {'size': size,
|
||||
'snapshot_id': snapshot_id,
|
||||
'display_name': display_name,
|
||||
'display_description': display_description,
|
||||
'volume_type': volume_type}}
|
||||
'snapshot_id': snapshot_id,
|
||||
'display_name': display_name,
|
||||
'display_description': display_description,
|
||||
'volume_type': volume_type}}
|
||||
return self._create('/volumes', body, 'volume')
|
||||
|
||||
def get(self, volume_id):
|
||||
|
@ -98,9 +98,9 @@ class VolumeManager(base.ManagerWithFind):
|
|||
:rtype: :class:`Volume`
|
||||
"""
|
||||
body = {'volumeAttachment': {'volumeId': volume_id,
|
||||
'device': device}}
|
||||
'device': device}}
|
||||
return self._create("/servers/%s/os-volume_attachments" % server_id,
|
||||
body, "volumeAttachment")
|
||||
body, "volumeAttachment")
|
||||
|
||||
def get_server_volume(self, server_id, attachment_id):
|
||||
"""
|
||||
|
@ -112,7 +112,7 @@ class VolumeManager(base.ManagerWithFind):
|
|||
:rtype: :class:`Volume`
|
||||
"""
|
||||
return self._get("/servers/%s/os-volume_attachments/%s" % (server_id,
|
||||
attachment_id,), "volumeAttachment")
|
||||
attachment_id,), "volumeAttachment")
|
||||
|
||||
def get_server_volumes(self, server_id):
|
||||
"""
|
||||
|
@ -122,7 +122,7 @@ class VolumeManager(base.ManagerWithFind):
|
|||
:rtype: list of :class:`Volume`
|
||||
"""
|
||||
return self._list("/servers/%s/os-volume_attachments" % server_id,
|
||||
"volumeAttachments")
|
||||
"volumeAttachments")
|
||||
|
||||
def delete_server_volume(self, server_id, attachment_id):
|
||||
"""
|
||||
|
@ -132,4 +132,4 @@ class VolumeManager(base.ManagerWithFind):
|
|||
:param attachment_id: The ID of the attachment
|
||||
"""
|
||||
self._delete("/servers/%s/os-volume_attachments/%s" %
|
||||
(server_id, attachment_id,))
|
||||
(server_id, attachment_id,))
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# python-cinderclient documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sun Dec 6 14:19:25 2009.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
# This file is execfile()d with current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
|
|
|
@ -15,7 +15,7 @@ def assert_has_keys(dict, required=[], optional=[]):
|
|||
except AssertionError:
|
||||
extra_keys = set(keys).difference(set(required + optional))
|
||||
raise AssertionError("found unexpected keys: %s" %
|
||||
list(extra_keys))
|
||||
list(extra_keys))
|
||||
|
||||
|
||||
class FakeClient(object):
|
||||
|
@ -27,11 +27,11 @@ class FakeClient(object):
|
|||
expected = (method, url)
|
||||
called = self.client.callstack[pos][0:2]
|
||||
|
||||
assert self.client.callstack, \
|
||||
"Expected %s %s but no calls were made." % expected
|
||||
assert(self.client.callstack,
|
||||
"Expected %s %s but no calls were made." % expected)
|
||||
|
||||
assert expected == called, 'Expected %s %s; got %s %s' % \
|
||||
(expected + called)
|
||||
assert (expected == called, 'Expected %s %s; got %s %s' %
|
||||
(expected + called))
|
||||
|
||||
if body is not None:
|
||||
assert self.client.callstack[pos][2] == body
|
||||
|
@ -42,8 +42,8 @@ class FakeClient(object):
|
|||
"""
|
||||
expected = (method, url)
|
||||
|
||||
assert self.client.callstack, \
|
||||
"Expected %s %s but no calls were made." % expected
|
||||
assert(self.client.callstack,
|
||||
"Expected %s %s but no calls were made." % expected)
|
||||
|
||||
found = False
|
||||
for entry in self.client.callstack:
|
||||
|
@ -51,8 +51,9 @@ class FakeClient(object):
|
|||
found = True
|
||||
break
|
||||
|
||||
assert found, 'Expected %s %s; got %s' % \
|
||||
(expected, self.client.callstack)
|
||||
assert(found, 'Expected %s %s; got %s' %
|
||||
(expected, self.client.callstack))
|
||||
|
||||
if body is not None:
|
||||
try:
|
||||
assert entry[2] == body
|
||||
|
|
|
@ -36,8 +36,7 @@ class ClientTest(utils.TestCase):
|
|||
headers = {"X-Auth-Token": "token",
|
||||
"X-Auth-Project-Id": "project_id",
|
||||
"User-Agent": cl.USER_AGENT,
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
'Accept': 'application/json', }
|
||||
mock_request.assert_called_with("http://example.com/hi",
|
||||
"GET", headers=headers)
|
||||
# Automatic JSON parsing
|
||||
|
|
|
@ -106,9 +106,9 @@ class ServiceCatalogTest(utils.TestCase):
|
|||
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for,
|
||||
service_type='compute')
|
||||
self.assertEquals(sc.url_for('tenantId', '1', service_type='compute'),
|
||||
"https://compute1.host/v1/1234")
|
||||
"https://compute1.host/v1/1234")
|
||||
self.assertEquals(sc.url_for('tenantId', '2', service_type='compute'),
|
||||
"https://compute1.host/v1/3456")
|
||||
"https://compute1.host/v1/3456")
|
||||
|
||||
self.assertRaises(exceptions.EndpointNotFound, sc.url_for,
|
||||
"region", "South", service_type='compute')
|
||||
|
@ -119,9 +119,9 @@ class ServiceCatalogTest(utils.TestCase):
|
|||
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for,
|
||||
service_type='volume')
|
||||
self.assertEquals(sc.url_for('tenantId', '1', service_type='volume'),
|
||||
"https://volume1.host/v1/1234")
|
||||
"https://volume1.host/v1/1234")
|
||||
self.assertEquals(sc.url_for('tenantId', '2', service_type='volume'),
|
||||
"https://volume1.host/v1/3456")
|
||||
"https://volume1.host/v1/3456")
|
||||
|
||||
self.assertRaises(exceptions.EndpointNotFound, sc.url_for,
|
||||
"region", "North", service_type='volume')
|
||||
|
|
|
@ -120,10 +120,7 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
"maxServerMeta": 5,
|
||||
"maxImageMeta": 5,
|
||||
"maxPersonality": 5,
|
||||
"maxPersonalitySize": 10240
|
||||
},
|
||||
},
|
||||
})
|
||||
"maxPersonalitySize": 10240}, }, })
|
||||
|
||||
#
|
||||
# Servers
|
||||
|
@ -141,8 +138,7 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
return (200, {"volumes": [
|
||||
{'id': 1234,
|
||||
'name': 'sample-volume',
|
||||
'attachments': [{'server_id': 1234}]
|
||||
},
|
||||
'attachments': [{'server_id': 1234}]},
|
||||
]})
|
||||
|
||||
def get_volumes_1234(self, **kw):
|
||||
|
@ -152,8 +148,8 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
def post_servers(self, body, **kw):
|
||||
assert set(body.keys()) <= set(['server', 'os:scheduler_hints'])
|
||||
fakes.assert_has_keys(body['server'],
|
||||
required=['name', 'imageRef', 'flavorRef'],
|
||||
optional=['metadata', 'personality'])
|
||||
required=['name', 'imageRef', 'flavorRef'],
|
||||
optional=['metadata', 'personality'])
|
||||
if 'personality' in body['server']:
|
||||
for pfile in body['server']['personality']:
|
||||
fakes.assert_has_keys(pfile, required=['path', 'contents'])
|
||||
|
@ -194,18 +190,19 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
return (200, {'data': 'Fake diagnostics'})
|
||||
|
||||
def get_servers_1234_actions(self, **kw):
|
||||
return (200, {'actions': [
|
||||
{
|
||||
'action': 'rebuild',
|
||||
'error': None,
|
||||
'created_at': '2011-12-30 11:45:36'
|
||||
},
|
||||
{
|
||||
'action': 'reboot',
|
||||
'error': 'Failed!',
|
||||
'created_at': '2011-12-30 11:40:29'
|
||||
},
|
||||
]})
|
||||
return (
|
||||
200, {'actions': [
|
||||
{
|
||||
'action': 'rebuild',
|
||||
'error': None,
|
||||
'created_at': '2011-12-30 11:45:36'
|
||||
},
|
||||
{
|
||||
'action': 'reboot',
|
||||
'error': 'Failed!',
|
||||
'created_at': '2011-12-30 11:40:29'
|
||||
},
|
||||
]})
|
||||
|
||||
#
|
||||
# Server Addresses
|
||||
|
@ -350,8 +347,8 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
|
||||
def get_os_floating_ips_1(self, **kw):
|
||||
return (200, {'floating_ip':
|
||||
{'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1'}
|
||||
})
|
||||
{'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1'}
|
||||
})
|
||||
|
||||
def post_os_floating_ips(self, body, **kw):
|
||||
return (202, self.get_os_floating_ips_1()[1])
|
||||
|
@ -359,12 +356,12 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
def post_os_floating_ips(self, body):
|
||||
if body.get('pool'):
|
||||
return (200, {'floating_ip':
|
||||
{'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1',
|
||||
'pool': 'cinder'}})
|
||||
{'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1',
|
||||
'pool': 'cinder'}})
|
||||
else:
|
||||
return (200, {'floating_ip':
|
||||
{'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1',
|
||||
'pool': None}})
|
||||
{'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1',
|
||||
'pool': None}})
|
||||
|
||||
def delete_os_floating_ips_1(self, **kw):
|
||||
return (204, None)
|
||||
|
@ -378,41 +375,41 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
if kw.get('ip'):
|
||||
return (205, {'dns_entries':
|
||||
[{'dns_entry':
|
||||
{'ip': kw.get('ip'),
|
||||
'name': "host1",
|
||||
'type': "A",
|
||||
'domain': 'testdomain'}},
|
||||
{'ip': kw.get('ip'),
|
||||
'name': "host1",
|
||||
'type': "A",
|
||||
'domain': 'testdomain'}},
|
||||
{'dns_entry':
|
||||
{'ip': kw.get('ip'),
|
||||
'name': "host2",
|
||||
'type': "A",
|
||||
'domain': 'testdomain'}}]})
|
||||
{'ip': kw.get('ip'),
|
||||
'name': "host2",
|
||||
'type': "A",
|
||||
'domain': 'testdomain'}}]})
|
||||
else:
|
||||
return (404, None)
|
||||
|
||||
def get_os_floating_ip_dns_testdomain_entries_testname(self, **kw):
|
||||
return (205, {'dns_entry':
|
||||
{'ip': "10.10.10.10",
|
||||
'name': 'testname',
|
||||
'type': "A",
|
||||
'domain': 'testdomain'}})
|
||||
{'ip': "10.10.10.10",
|
||||
'name': 'testname',
|
||||
'type': "A",
|
||||
'domain': 'testdomain'}})
|
||||
|
||||
def put_os_floating_ip_dns_testdomain(self, body, **kw):
|
||||
if body['domain_entry']['scope'] == 'private':
|
||||
fakes.assert_has_keys(body['domain_entry'],
|
||||
required=['availability_zone', 'scope'])
|
||||
required=['availability_zone', 'scope'])
|
||||
elif body['domain_entry']['scope'] == 'public':
|
||||
fakes.assert_has_keys(body['domain_entry'],
|
||||
required=['project', 'scope'])
|
||||
required=['project', 'scope'])
|
||||
|
||||
else:
|
||||
fakes.assert_has_keys(body['domain_entry'],
|
||||
required=['project', 'scope'])
|
||||
required=['project', 'scope'])
|
||||
return (205, None)
|
||||
|
||||
def put_os_floating_ip_dns_testdomain_entries_testname(self, body, **kw):
|
||||
fakes.assert_has_keys(body['dns_entry'],
|
||||
required=['ip', 'dns_type'])
|
||||
required=['ip', 'dns_type'])
|
||||
return (205, None)
|
||||
|
||||
def delete_os_floating_ip_dns_testdomain(self, **kw):
|
||||
|
@ -471,7 +468,7 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
fakes.assert_has_keys(body['metadata'],
|
||||
required=['test_key'])
|
||||
return (200,
|
||||
{'metadata': self.get_images_1()[1]['image']['metadata']})
|
||||
{'metadata': self.get_images_1()[1]['image']['metadata']})
|
||||
|
||||
def delete_images_1(self, **kw):
|
||||
return (204, None)
|
||||
|
@ -588,14 +585,16 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
# Security Groups
|
||||
#
|
||||
def get_os_security_groups(self, **kw):
|
||||
return (200, {"security_groups": [
|
||||
{'id': 1, 'name': 'test', 'description': 'FAKE_SECURITY_GROUP'}
|
||||
]})
|
||||
return (200, {"security_groups":
|
||||
[
|
||||
{'id': 1, 'name': 'test',
|
||||
'description': 'FAKE_SECURITY_GROUP'}
|
||||
]})
|
||||
|
||||
def get_os_security_groups_1(self, **kw):
|
||||
return (200, {"security_group":
|
||||
{'id': 1, 'name': 'test', 'description': 'FAKE_SECURITY_GROUP'}
|
||||
})
|
||||
})
|
||||
|
||||
def delete_os_security_groups_1(self, **kw):
|
||||
return (202, None)
|
||||
|
@ -605,7 +604,7 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
fakes.assert_has_keys(body['security_group'],
|
||||
required=['name', 'description'])
|
||||
r = {'security_group':
|
||||
self.get_os_security_groups()[1]['security_groups'][0]}
|
||||
self.get_os_security_groups()[1]['security_groups'][0]}
|
||||
return (202, r)
|
||||
|
||||
#
|
||||
|
@ -616,7 +615,7 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
{'id': 1, 'parent_group_id': 1, 'group_id': 2,
|
||||
'ip_protocol': 'TCP', 'from_port': '22', 'to_port': 22,
|
||||
'cidr': '10.0.0.0/8'}
|
||||
]})
|
||||
]})
|
||||
|
||||
def delete_os_security_group_rules_1(self, **kw):
|
||||
return (202, None)
|
||||
|
@ -624,11 +623,11 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||
def post_os_security_group_rules(self, body, **kw):
|
||||
assert body.keys() == ['security_group_rule']
|
||||
fakes.assert_has_keys(body['security_group_rule'],
|
||||
required=['parent_group_id'],
|
||||
optional=['group_id', 'ip_protocol', 'from_port',
|
||||
'to_port', 'cidr'])
|
||||
required=['parent_group_id'],
|
||||
optional=['group_id', 'ip_protocol', 'from_port',
|
||||
'to_port', 'cidr'])
|
||||
r = {'security_group_rule':
|
||||
self.get_os_security_group_rules()[1]['security_group_rules'][0]}
|
||||
self.get_os_security_group_rules()[1]['security_group_rules'][0]}
|
||||
return (202, r)
|
||||
|
||||
#
|
||||
|
|
|
@ -42,8 +42,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||
}
|
||||
auth_response = httplib2.Response({
|
||||
"status": 200,
|
||||
"body": json.dumps(resp),
|
||||
})
|
||||
"body": json.dumps(resp), })
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response,
|
||||
json.dumps(resp)))
|
||||
|
@ -85,8 +84,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
||||
auth_response = httplib2.Response({
|
||||
"status": 401,
|
||||
"body": json.dumps(resp),
|
||||
})
|
||||
"body": json.dumps(resp), })
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response,
|
||||
json.dumps(resp)))
|
||||
|
@ -138,8 +136,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||
"body": correct_response}
|
||||
]
|
||||
|
||||
responses = [(to_http_response(resp), resp['body']) \
|
||||
for resp in dict_responses]
|
||||
responses = [(to_http_response(resp), resp['body'])
|
||||
for resp in dict_responses]
|
||||
|
||||
def side_effect(*args, **kwargs):
|
||||
return responses.pop(0)
|
||||
|
@ -159,9 +157,9 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||
'passwordCredentials': {
|
||||
'username': cs.client.user,
|
||||
'password': cs.client.password,
|
||||
},
|
||||
'tenantName': cs.client.projectid,
|
||||
},
|
||||
},
|
||||
'tenantName': cs.client.projectid,
|
||||
},
|
||||
}
|
||||
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
|
@ -214,9 +212,10 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|||
],
|
||||
},
|
||||
}
|
||||
auth_response = httplib2.Response({
|
||||
"status": 200,
|
||||
"body": json.dumps(resp),
|
||||
auth_response = httplib2.Response(
|
||||
{
|
||||
"status": 200,
|
||||
"body": json.dumps(resp),
|
||||
})
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response,
|
||||
|
|
|
@ -207,8 +207,8 @@ def print_help():
|
|||
python-cinderclient development uses virtualenv to track and manage Python
|
||||
dependencies while in development and testing.
|
||||
|
||||
To activate the python-cinderclient virtualenv for the extent of your current
|
||||
shell session you can run:
|
||||
To activate the python-cinderclient virtualenv for the extent of your
|
||||
current shell session you can run:
|
||||
|
||||
$ source .venv/bin/activate
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
|
||||
distribute>=0.6.24
|
||||
|
||||
mock
|
||||
nose
|
||||
nosexcover
|
||||
openstack.nose_plugin
|
||||
pep8>=1.0
|
||||
pep8==1.1
|
||||
sphinx>=1.1.2
|
||||
unittest2
|
||||
|
|
Loading…
Reference in New Issue