ce5786af07
Updates the VIM to avoid creating the nova-compute service, which required an extension to the nova API. Instead: - Nova is configured to automatically create new services and set them to disabled. - When a host configured with the nova-compute service is unlocked, the VIM will wait for the nova-compute service to be created and then enable it. These changes only apply to the kubernetes configuration. This commit also adds some robustness to the VIM's keystone token handling code to fail earlier when a token cannot be retrieved. Change-Id: If8ce4eea87a51451495517077ca2ea6fbc6b689d Story: 2004583 Task: 28387 Depends-On: Idb27a927de2ac91ebbb1df343a349bb14ec2f0d5 Signed-off-by: Bart Wensley <barton.wensley@windriver.com>
588 lines
19 KiB
Python
Executable File
588 lines
19 KiB
Python
Executable File
#
|
|
# Copyright (c) 2015-2018 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
from six.moves import http_client as httplib
|
|
|
|
from nfv_common import debug
|
|
|
|
from nfv_vim import nfvi
|
|
|
|
from nfv_plugins.nfvi_plugins import config
|
|
|
|
from nfv_plugins.nfvi_plugins.openstack import cinder
|
|
from nfv_plugins.nfvi_plugins.openstack import exceptions
|
|
from nfv_plugins.nfvi_plugins.openstack import openstack
|
|
|
|
from nfv_plugins.nfvi_plugins.openstack.objects import OPENSTACK_SERVICE
|
|
|
|
DLOG = debug.debug_get_logger('nfv_plugins.nfvi_plugins.block_storage_api')
|
|
|
|
|
|
def volume_get_avail_status(status):
|
|
"""
|
|
Convert the nfvi volume status to a volume availability status
|
|
"""
|
|
avail_status = list()
|
|
|
|
if cinder.VOLUME_STATUS.AVAILABLE == status:
|
|
avail_status.append(nfvi.objects.v1.VOLUME_AVAIL_STATUS.AVAILABLE)
|
|
|
|
elif cinder.VOLUME_STATUS.IN_USE == status:
|
|
avail_status.append(nfvi.objects.v1.VOLUME_AVAIL_STATUS.IN_USE)
|
|
|
|
elif cinder.VOLUME_STATUS.ERROR == status:
|
|
avail_status.append(nfvi.objects.v1.VOLUME_AVAIL_STATUS.FAILED)
|
|
|
|
elif cinder.VOLUME_STATUS.ERROR_DELETING == status:
|
|
avail_status.append(nfvi.objects.v1.VOLUME_AVAIL_STATUS.FAILED)
|
|
|
|
elif cinder.VOLUME_STATUS.ERROR_RESTORING == status:
|
|
avail_status.append(nfvi.objects.v1.VOLUME_AVAIL_STATUS.FAILED)
|
|
|
|
elif cinder.VOLUME_STATUS.ERROR_EXTENDING == status:
|
|
avail_status.append(nfvi.objects.v1.VOLUME_AVAIL_STATUS.FAILED)
|
|
|
|
return avail_status
|
|
|
|
|
|
def volume_get_action(status):
|
|
"""
|
|
Convert the nfvi volume status to a volume action
|
|
"""
|
|
if cinder.VOLUME_STATUS.CREATING == status:
|
|
return nfvi.objects.v1.VOLUME_ACTION.BUILDING
|
|
|
|
elif cinder.VOLUME_STATUS.ATTACHING == status:
|
|
return nfvi.objects.v1.VOLUME_ACTION.ATTACHING
|
|
|
|
elif cinder.VOLUME_STATUS.DELETING == status:
|
|
return nfvi.objects.v1.VOLUME_ACTION.DELETING
|
|
|
|
elif cinder.VOLUME_STATUS.BACKING_UP == status:
|
|
return nfvi.objects.v1.VOLUME_ACTION.BACKING_UP
|
|
|
|
elif cinder.VOLUME_STATUS.RESTORING_BACKUP == status:
|
|
return nfvi.objects.v1.VOLUME_ACTION.RESTORING_BACKUP
|
|
|
|
elif cinder.VOLUME_STATUS.DOWNLOADING == status:
|
|
return nfvi.objects.v1.VOLUME_ACTION.DOWNLOADING
|
|
|
|
else:
|
|
return nfvi.objects.v1.VOLUME_ACTION.NONE
|
|
|
|
|
|
class NFVIBlockStorageAPI(nfvi.api.v1.NFVIBlockStorageAPI):
|
|
"""
|
|
NFVI Block Storage API Class Definition
|
|
"""
|
|
_name = 'Block-Storage-API'
|
|
_version = '1.0.0'
|
|
_provider = 'Wind River'
|
|
_signature = '22b3dbf6-e4ba-441b-8797-fb8a51210a43'
|
|
|
|
def __init__(self):
|
|
super(NFVIBlockStorageAPI, self).__init__()
|
|
self._token = None
|
|
self._directory = None
|
|
|
|
@property
|
|
def name(self):
|
|
return self._name
|
|
|
|
@property
|
|
def version(self):
|
|
return self._version
|
|
|
|
@property
|
|
def provider(self):
|
|
return self._provider
|
|
|
|
@property
|
|
def signature(self):
|
|
return self._signature
|
|
|
|
def get_volumes(self, future, paging, callback):
|
|
"""
|
|
Get a list of volumes
|
|
"""
|
|
response = dict()
|
|
response['completed'] = False
|
|
response['reason'] = ''
|
|
response['page-request-id'] = paging.page_request_id
|
|
|
|
try:
|
|
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
|
|
|
|
if self._directory.get_service_info(OPENSTACK_SERVICE.CINDER) \
|
|
is None:
|
|
DLOG.info("Cinder service get-volumes not available.")
|
|
response['result-data'] = list()
|
|
response['completed'] = True
|
|
paging.next_page = None
|
|
return
|
|
|
|
if self._token is None or self._token.is_expired():
|
|
future.work(openstack.get_token, self._directory)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete() or \
|
|
future.result.data is None:
|
|
return
|
|
|
|
self._token = future.result.data
|
|
|
|
DLOG.verbose("Volume paging (before): %s" % paging)
|
|
|
|
future.work(cinder.get_volumes, self._token, paging.page_limit,
|
|
paging.next_page)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete():
|
|
return
|
|
|
|
volume_data_list = future.result.data
|
|
|
|
volumes = list()
|
|
|
|
for volume_data in volume_data_list['volumes']:
|
|
name = volume_data.get('name', None)
|
|
if name is None:
|
|
name = volume_data['id']
|
|
|
|
volumes.append((volume_data['id'], name))
|
|
|
|
paging.next_page = None
|
|
|
|
volume_links = volume_data_list.get('volumes_links', None)
|
|
if volume_links is not None:
|
|
for volume_link in volume_links:
|
|
if 'next' == volume_link['rel']:
|
|
paging.next_page = volume_link['href']
|
|
break
|
|
|
|
DLOG.verbose("Volume paging (after): %s" % paging)
|
|
|
|
response['result-data'] = volumes
|
|
response['completed'] = True
|
|
|
|
except exceptions.OpenStackRestAPIException as e:
|
|
if httplib.UNAUTHORIZED == e.http_status_code:
|
|
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
|
|
if self._token is not None:
|
|
self._token.set_expired()
|
|
|
|
else:
|
|
DLOG.exception("Caught exception while trying to get volume "
|
|
"list, error=%s." % e)
|
|
|
|
except Exception as e:
|
|
DLOG.exception("Caught exception while trying to get volume list, "
|
|
"error=%s." % e)
|
|
|
|
finally:
|
|
callback.send(response)
|
|
callback.close()
|
|
|
|
def create_volume(self, future, volume_name, volume_description, size_gb,
|
|
image_uuid, callback):
|
|
"""
|
|
Create a volume
|
|
"""
|
|
response = dict()
|
|
response['completed'] = False
|
|
response['reason'] = ''
|
|
|
|
try:
|
|
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
|
|
|
|
if self._token is None or self._token.is_expired():
|
|
future.work(openstack.get_token, self._directory)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete() or \
|
|
future.result.data is None:
|
|
return
|
|
|
|
self._token = future.result.data
|
|
|
|
future.work(cinder.create_volume, self._token, volume_name,
|
|
volume_description, size_gb, image_uuid)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete():
|
|
return
|
|
|
|
volume_data = future.result.data['volume']
|
|
|
|
future.work(cinder.get_volume, self._token, volume_data['id'])
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete():
|
|
return
|
|
|
|
volume_data = future.result.data['volume']
|
|
|
|
name = volume_data.get('name', None)
|
|
description = volume_data.get('description', "")
|
|
avail_status = volume_get_avail_status(volume_data['status'])
|
|
action = volume_get_action(volume_data['status'])
|
|
|
|
volume_meta_data = volume_data.get('volume_image_metadata', None)
|
|
if volume_meta_data is not None:
|
|
image_uuid = volume_meta_data.get('image_id', None)
|
|
else:
|
|
image_uuid = None
|
|
|
|
if name is None:
|
|
name = volume_data['id']
|
|
|
|
if description is None:
|
|
description = ""
|
|
|
|
if volume_data['bootable'] in ['true', 'False', '1']:
|
|
bootable = 'yes'
|
|
else:
|
|
bootable = 'no'
|
|
|
|
if volume_data['encrypted'] in ['true', 'True', '1']:
|
|
encrypted = 'yes'
|
|
else:
|
|
encrypted = 'no'
|
|
|
|
volume_obj = nfvi.objects.v1.Volume(
|
|
volume_data['id'], name, description, avail_status, action,
|
|
int(volume_data['size']), bootable, encrypted, image_uuid)
|
|
|
|
response['result-data'] = volume_obj
|
|
response['completed'] = True
|
|
|
|
except exceptions.OpenStackRestAPIException as e:
|
|
if httplib.UNAUTHORIZED == e.http_status_code:
|
|
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
|
|
if self._token is not None:
|
|
self._token.set_expired()
|
|
|
|
else:
|
|
DLOG.exception("Caught exception while trying to create a "
|
|
"volume, error=%s." % e)
|
|
|
|
except Exception as e:
|
|
DLOG.exception("Caught exception while trying to create a "
|
|
"volume, error=%s." % e)
|
|
|
|
finally:
|
|
callback.send(response)
|
|
callback.close()
|
|
|
|
def delete_volume(self, future, volume_uuid, callback):
|
|
"""
|
|
Delete a volume
|
|
"""
|
|
response = dict()
|
|
response['completed'] = False
|
|
response['reason'] = ''
|
|
|
|
try:
|
|
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
|
|
|
|
if self._token is None or self._token.is_expired():
|
|
future.work(openstack.get_token, self._directory)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete() or \
|
|
future.result.data is None:
|
|
return
|
|
|
|
self._token = future.result.data
|
|
|
|
future.work(cinder.delete_volume, self._token, volume_uuid)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete():
|
|
return
|
|
|
|
response['completed'] = True
|
|
|
|
except exceptions.OpenStackRestAPIException as e:
|
|
if httplib.UNAUTHORIZED == e.http_status_code:
|
|
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
|
|
if self._token is not None:
|
|
self._token.set_expired()
|
|
|
|
else:
|
|
DLOG.exception("Caught exception while trying to delete a "
|
|
"volume, error=%s." % e)
|
|
|
|
except Exception as e:
|
|
DLOG.exception("Caught exception while trying to delete a "
|
|
"volume, error=%s." % e)
|
|
|
|
finally:
|
|
callback.send(response)
|
|
callback.close()
|
|
|
|
def update_volume(self, future, volume_uuid, volume_description, callback):
|
|
"""
|
|
Update a volume
|
|
"""
|
|
response = dict()
|
|
response['completed'] = False
|
|
response['reason'] = ''
|
|
|
|
try:
|
|
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
|
|
|
|
if self._token is None or self._token.is_expired():
|
|
future.work(openstack.get_token, self._directory)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete() or \
|
|
future.result.data is None:
|
|
return
|
|
|
|
self._token = future.result.data
|
|
|
|
future.work(cinder.update_volume, self._token, volume_uuid,
|
|
volume_description)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete():
|
|
return
|
|
|
|
future.work(cinder.get_volume, self._token, volume_uuid)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete():
|
|
return
|
|
|
|
volume_data = future.result.data['volume']
|
|
|
|
name = volume_data.get('name', None)
|
|
description = volume_data.get('description', "")
|
|
avail_status = volume_get_avail_status(volume_data['status'])
|
|
action = volume_get_action(volume_data['status'])
|
|
|
|
volume_meta_data = volume_data.get('volume_image_metadata', None)
|
|
if volume_meta_data is not None:
|
|
image_uuid = volume_meta_data.get('image_id', None)
|
|
else:
|
|
image_uuid = None
|
|
|
|
if name is None:
|
|
name = volume_data['id']
|
|
|
|
if description is None:
|
|
description = ""
|
|
|
|
if volume_data['bootable'] in ['true', 'False', '1']:
|
|
bootable = 'yes'
|
|
else:
|
|
bootable = 'no'
|
|
|
|
if volume_data['encrypted'] in ['true', 'True', '1']:
|
|
encrypted = 'yes'
|
|
else:
|
|
encrypted = 'no'
|
|
|
|
volume_obj = nfvi.objects.v1.Volume(
|
|
volume_data['id'], name, description, avail_status, action,
|
|
int(volume_data['size']), bootable, encrypted, image_uuid)
|
|
|
|
response['result-data'] = volume_obj
|
|
response['completed'] = True
|
|
|
|
except exceptions.OpenStackRestAPIException as e:
|
|
if httplib.UNAUTHORIZED == e.http_status_code:
|
|
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
|
|
if self._token is not None:
|
|
self._token.set_expired()
|
|
|
|
else:
|
|
DLOG.exception("Caught exception while trying to update a "
|
|
"volume, error=%s." % e)
|
|
|
|
except Exception as e:
|
|
DLOG.exception("Caught exception while trying to update a "
|
|
"volume, error=%s." % e)
|
|
|
|
finally:
|
|
callback.send(response)
|
|
callback.close()
|
|
|
|
def get_volume(self, future, volume_uuid, callback):
|
|
"""
|
|
Get a volume
|
|
"""
|
|
response = dict()
|
|
response['completed'] = False
|
|
response['reason'] = ''
|
|
|
|
try:
|
|
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
|
|
|
|
if self._token is None or self._token.is_expired():
|
|
future.work(openstack.get_token, self._directory)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete() or \
|
|
future.result.data is None:
|
|
return
|
|
|
|
self._token = future.result.data
|
|
|
|
future.work(cinder.get_volume, self._token, volume_uuid)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete():
|
|
return
|
|
|
|
volume_data = future.result.data['volume']
|
|
|
|
name = volume_data.get('name', None)
|
|
description = volume_data.get('description', "")
|
|
avail_status = volume_get_avail_status(volume_data['status'])
|
|
action = volume_get_action(volume_data['status'])
|
|
|
|
volume_meta_data = volume_data.get('volume_image_metadata', None)
|
|
if volume_meta_data is not None:
|
|
image_uuid = volume_meta_data.get('image_id', None)
|
|
else:
|
|
image_uuid = None
|
|
|
|
if name is None:
|
|
name = volume_data['id']
|
|
|
|
if description is None:
|
|
description = ""
|
|
|
|
if volume_data['bootable'] in ['true', 'False', '1']:
|
|
bootable = 'yes'
|
|
else:
|
|
bootable = 'no'
|
|
|
|
if volume_data['encrypted'] in ['true', 'True', '1']:
|
|
encrypted = 'yes'
|
|
else:
|
|
encrypted = 'no'
|
|
|
|
volume_obj = nfvi.objects.v1.Volume(
|
|
volume_data['id'], name, description, avail_status, action,
|
|
int(volume_data['size']), bootable, encrypted, image_uuid)
|
|
|
|
response['result-data'] = volume_obj
|
|
response['completed'] = True
|
|
|
|
except exceptions.OpenStackRestAPIException as e:
|
|
if httplib.UNAUTHORIZED == e.http_status_code:
|
|
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
|
|
if self._token is not None:
|
|
self._token.set_expired()
|
|
|
|
else:
|
|
DLOG.exception("Caught exception while trying to get a "
|
|
"volume, error=%s." % e)
|
|
|
|
except Exception as e:
|
|
DLOG.exception("Caught exception while trying to get a volume, "
|
|
"error=%s." % e)
|
|
|
|
finally:
|
|
callback.send(response)
|
|
callback.close()
|
|
|
|
def get_volume_snapshots(self, future, callback):
|
|
"""
|
|
Get a list of volume snapshots
|
|
"""
|
|
response = dict()
|
|
response['completed'] = False
|
|
response['reason'] = ''
|
|
|
|
try:
|
|
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
|
|
|
|
if self._directory.get_service_info(OPENSTACK_SERVICE.CINDER) \
|
|
is None:
|
|
DLOG.info("Cinder service get-volume-snapshots not available.")
|
|
response['result-data'] = list()
|
|
response['completed'] = True
|
|
return
|
|
|
|
if self._token is None or self._token.is_expired():
|
|
future.work(openstack.get_token, self._directory)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete() or \
|
|
future.result.data is None:
|
|
return
|
|
|
|
self._token = future.result.data
|
|
|
|
future.work(cinder.get_volume_snapshots, self._token)
|
|
future.result = (yield)
|
|
|
|
if not future.result.is_complete():
|
|
return
|
|
|
|
volume_snapshot_data_list = future.result.data
|
|
|
|
volume_snapshots = list()
|
|
|
|
for volume_snapshot_data in volume_snapshot_data_list['snapshots']:
|
|
name = volume_snapshot_data.get('name', None)
|
|
if name is None:
|
|
name = volume_snapshot_data['id']
|
|
|
|
description = volume_snapshot_data.get('description', "")
|
|
if description is None:
|
|
description = ""
|
|
|
|
size_gb = int(volume_snapshot_data.get('size', 0))
|
|
if size_gb is None:
|
|
size_gb = 0
|
|
|
|
volume_uuid = volume_snapshot_data.get('volume_id', "")
|
|
|
|
volume_snapshot = nfvi.objects.v1.VolumeSnapshot(
|
|
volume_snapshot_data['id'], name, description, size_gb,
|
|
volume_uuid)
|
|
|
|
volume_snapshots.append(volume_snapshot)
|
|
|
|
response['result-data'] = volume_snapshots
|
|
response['completed'] = True
|
|
|
|
except exceptions.OpenStackRestAPIException as e:
|
|
if httplib.UNAUTHORIZED == e.http_status_code:
|
|
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
|
|
if self._token is not None:
|
|
self._token.set_expired()
|
|
|
|
else:
|
|
DLOG.exception("Caught exception while trying to get volume "
|
|
"list, error=%s." % e)
|
|
|
|
except Exception as e:
|
|
DLOG.exception("Caught exception while trying to get volume list, "
|
|
"error=%s." % e)
|
|
|
|
finally:
|
|
callback.send(response)
|
|
callback.close()
|
|
|
|
def initialize(self, config_file):
|
|
"""
|
|
Initialize the plugin
|
|
"""
|
|
config.load(config_file)
|
|
self._directory = openstack.get_directory(
|
|
config, openstack.SERVICE_CATEGORY.OPENSTACK)
|
|
|
|
def finalize(self):
|
|
"""
|
|
Finalize the plugin
|
|
"""
|
|
return
|