Merge branch 'master' of git://github.com/rackspace/python-novaclient
Conflicts: novaclient/v1_1/shell.py
This commit is contained in:
commit
ff48a3e1ec
@ -47,7 +47,7 @@ copyright = u'Rackspace, based on work by Jacob Kaplan-Moss'
|
||||
# The short X.Y version.
|
||||
version = '2.6'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.6.6'
|
||||
release = '2.6.7'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -37,8 +37,8 @@ class HTTPClient(httplib2.Http):
|
||||
|
||||
USER_AGENT = 'python-novaclient'
|
||||
|
||||
def __init__(self, user, apikey, projectid, auth_url, timeout=None,
|
||||
token=None, region_name=None):
|
||||
def __init__(self, user, apikey, projectid, auth_url, insecure=False,
|
||||
timeout=None, token=None, region_name=None):
|
||||
super(HTTPClient, self).__init__(timeout=timeout)
|
||||
self.user = user
|
||||
self.apikey = apikey
|
||||
@ -53,6 +53,7 @@ class HTTPClient(httplib2.Http):
|
||||
|
||||
# httplib2 overrides
|
||||
self.force_exception_to_status_code = True
|
||||
self.disable_ssl_certificate_validation = insecure
|
||||
|
||||
def http_log(self, args, kwargs, resp, body):
|
||||
if 'NOVACLIENT_DEBUG' in os.environ and os.environ['NOVACLIENT_DEBUG']:
|
||||
@ -139,7 +140,7 @@ class HTTPClient(httplib2.Http):
|
||||
def delete(self, url, **kwargs):
|
||||
return self._cs_request(url, 'DELETE', **kwargs)
|
||||
|
||||
def _extract_service_catalog(self, url, resp, body):
|
||||
def _extract_service_catalog(self, url, resp, body, extract_token=True):
|
||||
"""See what the auth service told us and process the response.
|
||||
We may get redirected to another site, fail or actually get
|
||||
back a service catalog with a token and our endpoints."""
|
||||
@ -149,8 +150,9 @@ class HTTPClient(httplib2.Http):
|
||||
self.auth_url = url
|
||||
self.service_catalog = \
|
||||
service_catalog.ServiceCatalog(body)
|
||||
self.auth_token = self.service_catalog.get_token()
|
||||
|
||||
if extract_token:
|
||||
self.auth_token = self.service_catalog.get_token()
|
||||
self.management_url = self.service_catalog.url_for(
|
||||
attr='region',
|
||||
filter_value=self.region_name)
|
||||
@ -184,7 +186,8 @@ class HTTPClient(httplib2.Http):
|
||||
_logger.debug("Using Endpoint URL: %s" % url)
|
||||
resp, body = self.request(url, "GET",
|
||||
headers={'X-Auth_Token': self.auth_token})
|
||||
return self._extract_service_catalog(url, resp, body)
|
||||
return self._extract_service_catalog(url, resp, body,
|
||||
extract_token=False)
|
||||
|
||||
def authenticate(self):
|
||||
magic_tuple = urlparse.urlsplit(self.auth_url)
|
||||
@ -198,9 +201,9 @@ class HTTPClient(httplib2.Http):
|
||||
self.version = part
|
||||
break
|
||||
|
||||
# TODO(sandy): Assume admin endpoint is service endpoint+1 for now.
|
||||
# Ideally this is going to have to be provided by the admin.
|
||||
new_netloc = netloc.replace(':%d' % port, ':%d' % (port + 1))
|
||||
# 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))
|
||||
|
||||
@ -213,7 +216,11 @@ class HTTPClient(httplib2.Http):
|
||||
# existing token? If so, our actual endpoints may
|
||||
# be different than that of the admin token.
|
||||
if self.proxy_token:
|
||||
return self._fetch_endpoints_from_auth(admin_url)
|
||||
self._fetch_endpoints_from_auth(admin_url)
|
||||
# Since keystone no longer returns the user token
|
||||
# with the endpoints any more, we need to replace
|
||||
# our service account token with the user token.
|
||||
self.auth_token = self.proxy_token
|
||||
|
||||
else:
|
||||
try:
|
||||
|
@ -1,2 +1 @@
|
||||
from novaclient.keystone.client import Client
|
||||
|
||||
|
@ -78,7 +78,7 @@ class TenantManager(base.ManagerWithFind):
|
||||
"""
|
||||
update a tenant with a new name and description
|
||||
"""
|
||||
body = {"tenant": {'id': tenant_id }}
|
||||
body = {"tenant": {'id': tenant_id}}
|
||||
if enabled is not None:
|
||||
body['tenant']['enabled'] = enabled
|
||||
if description:
|
||||
|
@ -36,7 +36,7 @@ class UserManager(base.ManagerWithFind):
|
||||
"""
|
||||
# FIXME(ja): why do we have to send id in params and url?
|
||||
params = {"user": {"id": base.getid(user),
|
||||
"email": email }}
|
||||
"email": email}}
|
||||
|
||||
self._update("/users/%s" % base.getid(user), params)
|
||||
|
||||
@ -45,7 +45,7 @@ class UserManager(base.ManagerWithFind):
|
||||
Update enabled-ness
|
||||
"""
|
||||
params = {"user": {"id": base.getid(user),
|
||||
"enabled": enabled }}
|
||||
"enabled": enabled}}
|
||||
|
||||
self._update("/users/%s/enabled" % base.getid(user), params)
|
||||
|
||||
@ -54,7 +54,7 @@ class UserManager(base.ManagerWithFind):
|
||||
Update password
|
||||
"""
|
||||
params = {"user": {"id": base.getid(user),
|
||||
"password": password }}
|
||||
"password": password}}
|
||||
|
||||
self._update("/users/%s/password" % base.getid(user), params)
|
||||
|
||||
@ -63,7 +63,7 @@ class UserManager(base.ManagerWithFind):
|
||||
Update default tenant.
|
||||
"""
|
||||
params = {"user": {"id": base.getid(user),
|
||||
"tenantId": base.getid(tenant) }}
|
||||
"tenantId": base.getid(tenant)}}
|
||||
|
||||
# FIXME(ja): seems like a bad url - default tenant is an attribute
|
||||
# not a subresource!???
|
||||
@ -73,7 +73,8 @@ class UserManager(base.ManagerWithFind):
|
||||
"""
|
||||
Create a user.
|
||||
"""
|
||||
# FIXME(ja): email should be optional but keystone currently requires it
|
||||
# FIXME(ja): email should be optional but keystone currently
|
||||
# requires it
|
||||
params = {"user": {"id": user_id,
|
||||
"password": password,
|
||||
"tenantId": tenant_id,
|
||||
|
@ -33,6 +33,18 @@ class ServiceCatalog(object):
|
||||
"""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."""
|
||||
if 'endpoints' in self.catalog:
|
||||
# We have a bastardized service catalog. Treat it special. :/
|
||||
for endpoint in self.catalog['endpoints']:
|
||||
if not filter_value or endpoint[attr] == filter_value:
|
||||
return endpoint[endpoint_type]
|
||||
raise novaclient.exceptions.EndpointNotFound()
|
||||
|
||||
# We don't always get a service catalog back ...
|
||||
if not 'serviceCatalog' in self.catalog['access']:
|
||||
return None
|
||||
|
||||
# Full catalog ...
|
||||
catalog = self.catalog['access']['serviceCatalog']
|
||||
|
||||
for service in catalog:
|
||||
|
@ -82,6 +82,11 @@ class OpenStackComputeShell(object):
|
||||
default=env('NOVA_VERSION'),
|
||||
help='Accepts 1.0 or 1.1, defaults to env[NOVA_VERSION].')
|
||||
|
||||
parser.add_argument('--insecure',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
return parser
|
||||
|
||||
def get_subcommand_parser(self, version):
|
||||
@ -148,9 +153,9 @@ class OpenStackComputeShell(object):
|
||||
self.do_help(args)
|
||||
return 0
|
||||
|
||||
user, apikey, projectid, url, region_name = \
|
||||
user, apikey, projectid, url, region_name, insecure = \
|
||||
args.username, args.apikey, args.projectid, args.url, \
|
||||
args.region_name
|
||||
args.region_name, args.insecure
|
||||
|
||||
#FIXME(usrleon): Here should be restrict for project id same as
|
||||
# for username or apikey but for compatibility it is not.
|
||||
@ -174,9 +179,8 @@ class OpenStackComputeShell(object):
|
||||
"via --url or via"
|
||||
"env[NOVA_URL")
|
||||
|
||||
self.cs = self.get_api_class(options.version) \
|
||||
(user, apikey, projectid, url,
|
||||
region_name=region_name)
|
||||
self.cs = self.get_api_class(options.version)(user, apikey, projectid,
|
||||
url, insecure, region_name=region_name)
|
||||
|
||||
try:
|
||||
self.cs.authenticate()
|
||||
|
@ -26,7 +26,7 @@ class Client(object):
|
||||
"""
|
||||
|
||||
def __init__(self, username, api_key, project_id, auth_url=None,
|
||||
timeout=None, token=None, region_name=None):
|
||||
insecure=False, timeout=None, token=None, region_name=None):
|
||||
|
||||
self.accounts = accounts.AccountManager(self)
|
||||
self.backup_schedules = backup_schedules.BackupScheduleManager(self)
|
||||
@ -42,6 +42,7 @@ class Client(object):
|
||||
api_key,
|
||||
project_id,
|
||||
_auth_url,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
token=token,
|
||||
region_name=region_name)
|
||||
|
@ -30,8 +30,8 @@ class Client(object):
|
||||
"""
|
||||
|
||||
# FIXME(jesse): project_id isn't required to autenticate
|
||||
def __init__(self, username, api_key, project_id, auth_url, timeout=None,
|
||||
token=None, region_name=None):
|
||||
def __init__(self, username, api_key, project_id, auth_url,
|
||||
insecure=False, timeout=None, token=None, region_name=None):
|
||||
self.flavors = flavors.FlavorManager(self)
|
||||
self.floating_ips = floating_ips.FloatingIPManager(self)
|
||||
self.images = images.ImageManager(self)
|
||||
@ -51,6 +51,7 @@ class Client(object):
|
||||
api_key,
|
||||
project_id,
|
||||
auth_url,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
token=token,
|
||||
region_name=region_name)
|
||||
|
@ -58,9 +58,9 @@ class ImageManager(base.ManagerWithFind):
|
||||
self._delete("/images/%s" % base.getid(image))
|
||||
|
||||
def set_meta(self, image, metadata):
|
||||
"""
|
||||
"""
|
||||
Set an images metadata
|
||||
|
||||
|
||||
:param image: The :class:`Image` to add metadata to
|
||||
:param metadata: A dict of metadata to add to the image
|
||||
"""
|
||||
@ -69,9 +69,9 @@ class ImageManager(base.ManagerWithFind):
|
||||
"metadata")
|
||||
|
||||
def delete_meta(self, image, keys):
|
||||
"""
|
||||
"""
|
||||
Delete metadata from an image
|
||||
|
||||
|
||||
:param image: The :class:`Image` to add metadata to
|
||||
:param keys: A list of metadata keys to delete from the image
|
||||
"""
|
||||
|
@ -184,7 +184,11 @@ def do_boot(cs, args):
|
||||
security_groups=security_groups,
|
||||
key_name=key_name,
|
||||
block_device_mapping=block_device_mapping)
|
||||
|
||||
# Keep any information (like adminPass) returned by create
|
||||
info = server._info
|
||||
server = cs.servers.get(info['id'])
|
||||
info.update(server._info)
|
||||
|
||||
flavor = info.get('flavor', {})
|
||||
flavor_id = flavor.get('id', '')
|
||||
@ -1123,7 +1127,8 @@ def do_secgroup_delete_group_rule(cs, args):
|
||||
|
||||
|
||||
@utils.arg('name', metavar='<name>', help='Name of key.')
|
||||
@utils.arg('--pub_key', metavar='<pub_key>', help='Path to a public ssh key.', default=None)
|
||||
@utils.arg('--pub_key', metavar='<pub_key>', help='Path to a public ssh key.',
|
||||
default=None)
|
||||
def do_keypair_add(cs, args):
|
||||
"""Create a new key pair for use with instances"""
|
||||
name = args.name
|
||||
@ -1134,7 +1139,8 @@ def do_keypair_add(cs, args):
|
||||
with open(pub_key) as f:
|
||||
pub_key = f.read()
|
||||
except IOError, e:
|
||||
raise exceptions.CommandError("Can't open or read '%s': %s" % (pub_key, e))
|
||||
raise exceptions.CommandError("Can't open or read '%s': %s" % \
|
||||
(pub_key, e))
|
||||
|
||||
keypair = cs.keypairs.create(name, pub_key)
|
||||
|
||||
@ -1155,4 +1161,3 @@ def do_keypair_list(cs, args):
|
||||
keypairs = cs.keypairs.list()
|
||||
columns = ['Name', 'Fingerprint']
|
||||
utils.print_list(keypairs, columns)
|
||||
|
||||
|
@ -128,5 +128,5 @@ class VolumeManager(base.ManagerWithFind):
|
||||
:param server_id: The ID of the server
|
||||
:param attachment_id: The ID of the attachment
|
||||
"""
|
||||
return self._delete("/servers/%s/os-volume_attachments/%s" % (server_id,
|
||||
attachment_id,))
|
||||
return self._delete("/servers/%s/os-volume_attachments/%s" %
|
||||
(server_id, attachment_id,))
|
||||
|
Loading…
x
Reference in New Issue
Block a user