nfv/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_block_storage_api.py
Bart Wensley ce5786af07 Refactor nova service creation in the VIM
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>
2019-01-11 09:23:54 -06:00

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