278 lines
9.2 KiB
Python
278 lines
9.2 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
#
|
|
# 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.
|
|
|
|
import eventlet
|
|
from oslo.config import cfg
|
|
|
|
from heat.openstack.common import importutils
|
|
from heat.openstack.common import log as logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
from heat.common import exception
|
|
from heat.common import heat_keystoneclient as hkc
|
|
from novaclient import client as novaclient
|
|
try:
|
|
from swiftclient import client as swiftclient
|
|
except ImportError:
|
|
swiftclient = None
|
|
logger.info('swiftclient not available')
|
|
try:
|
|
from quantumclient.v2_0 import client as quantumclient
|
|
except ImportError:
|
|
quantumclient = None
|
|
logger.info('quantumclient not available')
|
|
try:
|
|
from cinderclient.v1 import client as cinderclient
|
|
from cinderclient import exceptions as cinder_exceptions
|
|
except ImportError:
|
|
cinderclient = None
|
|
logger.info('cinderclient not available')
|
|
|
|
|
|
cloud_opts = [
|
|
cfg.StrOpt('cloud_backend',
|
|
default=None,
|
|
help="Cloud module to use as a backend. Defaults to OpenStack.")
|
|
]
|
|
cfg.CONF.register_opts(cloud_opts)
|
|
|
|
|
|
class OpenStackClients(object):
|
|
'''
|
|
Convenience class to create and cache client instances.
|
|
'''
|
|
|
|
def __init__(self, context):
|
|
self.context = context
|
|
self._nova = {}
|
|
self._keystone = None
|
|
self._swift = None
|
|
self._quantum = None
|
|
self._cinder = None
|
|
|
|
def keystone(self):
|
|
if self._keystone:
|
|
return self._keystone
|
|
|
|
self._keystone = hkc.KeystoneClient(self.context)
|
|
return self._keystone
|
|
|
|
def nova(self, service_type='compute'):
|
|
if service_type in self._nova:
|
|
return self._nova[service_type]
|
|
|
|
con = self.context
|
|
args = {
|
|
'project_id': con.tenant,
|
|
'auth_url': con.auth_url,
|
|
'service_type': service_type,
|
|
}
|
|
|
|
if con.password is not None:
|
|
args['username'] = con.username
|
|
args['api_key'] = con.password
|
|
elif con.auth_token is not None:
|
|
args['username'] = con.service_user
|
|
args['api_key'] = con.service_password
|
|
args['project_id'] = con.service_tenant
|
|
args['proxy_token'] = con.auth_token
|
|
args['proxy_tenant_id'] = con.tenant_id
|
|
else:
|
|
logger.error("Nova connection failed, no password or auth_token!")
|
|
return None
|
|
|
|
client = None
|
|
try:
|
|
# Workaround for issues with python-keyring, need no_cache=True
|
|
# ref https://bugs.launchpad.net/python-novaclient/+bug/1020238
|
|
# TODO(shardy): May be able to remove when the bug above is fixed
|
|
client = novaclient.Client(1.1, no_cache=True, **args)
|
|
client.authenticate()
|
|
self._nova[service_type] = client
|
|
except TypeError:
|
|
# for compatibility with essex, which doesn't have no_cache=True
|
|
# TODO(shardy): remove when we no longer support essex
|
|
client = novaclient.Client(1.1, **args)
|
|
client.authenticate()
|
|
self._nova[service_type] = client
|
|
|
|
return client
|
|
|
|
def swift(self):
|
|
if swiftclient is None:
|
|
return None
|
|
if self._swift:
|
|
return self._swift
|
|
|
|
con = self.context
|
|
args = {
|
|
'auth_version': '2'
|
|
}
|
|
|
|
if con.password is not None:
|
|
args['user'] = con.username
|
|
args['key'] = con.password
|
|
args['authurl'] = con.auth_url
|
|
args['tenant_name'] = con.tenant
|
|
elif con.auth_token is not None:
|
|
args['user'] = None
|
|
args['key'] = None
|
|
args['authurl'] = None
|
|
args['preauthtoken'] = con.auth_token
|
|
# Lookup endpoint for object-store service type
|
|
service_type = 'object-store'
|
|
endpoints = self.keystone().service_catalog.get_endpoints(
|
|
service_type=service_type)
|
|
if len(endpoints[service_type]) == 1:
|
|
args['preauthurl'] = endpoints[service_type][0]['publicURL']
|
|
else:
|
|
logger.error("No endpoint found for %s service type" %
|
|
service_type)
|
|
return None
|
|
else:
|
|
logger.error("Swift connection failed, no password or " +
|
|
"auth_token!")
|
|
return None
|
|
|
|
self._swift = swiftclient.Connection(**args)
|
|
return self._swift
|
|
|
|
def quantum(self):
|
|
if quantumclient is None:
|
|
return None
|
|
if self._quantum:
|
|
logger.debug('using existing _quantum')
|
|
return self._quantum
|
|
|
|
con = self.context
|
|
args = {
|
|
'auth_url': con.auth_url,
|
|
'service_type': 'network',
|
|
}
|
|
|
|
if con.password is not None:
|
|
args['username'] = con.username
|
|
args['password'] = con.password
|
|
args['tenant_name'] = con.tenant
|
|
elif con.auth_token is not None:
|
|
args['username'] = con.service_user
|
|
args['password'] = con.service_password
|
|
args['tenant_name'] = con.service_tenant
|
|
args['token'] = con.auth_token
|
|
else:
|
|
logger.error("Quantum connection failed, "
|
|
"no password or auth_token!")
|
|
return None
|
|
logger.debug('quantum args %s', args)
|
|
|
|
self._quantum = quantumclient.Client(**args)
|
|
|
|
return self._quantum
|
|
|
|
def cinder(self):
|
|
if cinderclient is None:
|
|
return self.nova('volume')
|
|
if self._cinder:
|
|
return self._cinder
|
|
|
|
con = self.context
|
|
args = {
|
|
'project_id': con.tenant,
|
|
'auth_url': con.auth_url,
|
|
'service_type': 'volume',
|
|
}
|
|
|
|
if con.password is not None:
|
|
args['username'] = con.username
|
|
args['api_key'] = con.password
|
|
elif con.auth_token is not None:
|
|
args['username'] = con.service_user
|
|
args['api_key'] = con.service_password
|
|
args['project_id'] = con.service_tenant
|
|
args['proxy_token'] = con.auth_token
|
|
args['proxy_token_id'] = con.tenant_id
|
|
else:
|
|
logger.error("Cinder connection failed, "
|
|
"no password or auth_token!")
|
|
return None
|
|
logger.debug('cinder args %s', args)
|
|
|
|
self._cinder = cinderclient.Client(**args)
|
|
|
|
return self._cinder
|
|
|
|
def attach_volume_to_instance(self, server_id, volume_id, device_id):
|
|
logger.warn('Attaching InstanceId %s VolumeId %s Device %s' %
|
|
(server_id, volume_id, device_id))
|
|
|
|
va = self.nova().volumes.create_server_volume(
|
|
server_id=server_id,
|
|
volume_id=volume_id,
|
|
device=device_id)
|
|
|
|
vol = self.cinder().volumes.get(va.id)
|
|
while vol.status == 'available' or vol.status == 'attaching':
|
|
eventlet.sleep(1)
|
|
vol.get()
|
|
if vol.status == 'in-use':
|
|
return va.id
|
|
else:
|
|
raise exception.Error(vol.status)
|
|
|
|
def detach_volume_from_instance(self, server_id, volume_id):
|
|
logger.info('VolumeAttachment un-attaching %s %s' %
|
|
(server_id, volume_id))
|
|
|
|
try:
|
|
vol = self.cinder().volumes.get(volume_id)
|
|
except cinder_exceptions.NotFound:
|
|
logger.warning('Volume %s - not found' %
|
|
(volume_id))
|
|
return
|
|
try:
|
|
self.nova().volumes.delete_server_volume(server_id,
|
|
volume_id)
|
|
except novaclient.exceptions.NotFound:
|
|
logger.warning('Deleting VolumeAttachment %s %s - not found' %
|
|
(server_id, volume_id))
|
|
try:
|
|
logger.info('un-attaching %s, status %s' % (volume_id, vol.status))
|
|
while vol.status == 'in-use':
|
|
logger.info('trying to un-attach %s, but still %s' %
|
|
(volume_id, vol.status))
|
|
eventlet.sleep(1)
|
|
try:
|
|
self.nova().volumes.delete_server_volume(
|
|
server_id,
|
|
volume_id)
|
|
except Exception:
|
|
pass
|
|
vol.get()
|
|
logger.info('volume status of %s now %s' % (volume_id, vol.status))
|
|
except cinder_exceptions.NotFound:
|
|
logger.warning('Volume %s - not found' %
|
|
(volume_id))
|
|
|
|
|
|
if cfg.CONF.cloud_backend:
|
|
cloud_backend_module = importutils.import_module(cfg.CONF.cloud_backend)
|
|
Clients = cloud_backend_module.Clients
|
|
else:
|
|
Clients = OpenStackClients
|
|
|
|
logger.debug('Using backend %s' % Clients)
|