Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions cinder/tests/unit/volume/drivers/vmware/test_fcd.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ def test_delete_volume_empty_provider_location(self, delete_fcd):
self._driver.delete_volume(volume)
delete_fcd.assert_not_called()

@mock.patch.object(VMDK_DRIVER, '_get_connection_capabilities')
@mock.patch.object(volume_utils, 'extract_host')
@mock.patch.object(FCD_DRIVER, '_get_connector_config')
@mock.patch.object(FCD_DRIVER, '_provider_location_to_moref_location')
Expand All @@ -286,7 +287,7 @@ def test_initialize_connection(
self, get_adapter_type, from_provider_location,
get_storage_profile_id, vops,
provider_loc_to_moref_loc, get_connector_config,
extract_host):
extract_host, vcenter_instance_uuid):
fcd_loc = mock.Mock(
fcd_id=mock.sentinel.fcd_id, ds_ref_val=mock.sentinel.ds_ref_val)
from_provider_location.return_value = fcd_loc
Expand All @@ -298,13 +299,16 @@ def test_initialize_connection(
vops.get_backing.return_value = backing
get_connector_config.return_value = mock.sentinel.config
extract_host.return_value = mock.sentinel.host

volume = self._create_volume_obj()
vc_uuid = "195c5fb6-68b5-4b6c-bbd6-76ee92fbde5e"
vmware_uuid = ['vmware_service_instance_uuid:%s' % vc_uuid]
vcenter_instance_uuid.return_value = vmware_uuid
connector = {'platform': 'x86_64', 'os_type': 'linux',
'ip': '10.0.0.1',
'host': 'cinder-volume-backup-vmware-vc-a-0',
'multipath': False,
'initiator': 'iqn.1993-08.org.debian:01:c4f3207eed25'}
'initiator': 'iqn.1993-08.org.debian:01:c4f3207eed25',
"connection_capabilities": vmware_uuid}
profile_id = mock.sentinel.profile_id
get_storage_profile_id.return_value = profile_id
ret = self._driver.initialize_connection(volume, connector)
Expand Down
28 changes: 28 additions & 0 deletions cinder/volume/drivers/netapp/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ def get_file_sizes_by_dir(self, ctxt, host, path):
cctxt = self._get_cctxt(host=host)
return cctxt.call(ctxt, 'get_file_sizes_by_dir', path=path)

@volume_utils.trace
def get_vserver_for_ip(self, ctxt, host, lif_ip):
cctxt = self._get_cctxt(host=host)
return cctxt.call(ctxt, 'get_vserver_for_ip', lif_ip=lif_ip)

@volume_utils.trace
def clone_file(self, ctxt, host, flex_vol, src_path,
dest_path, vserver, dest_exists=False, is_snapshot=False):
cctxt = self._get_cctxt(host=host)
return cctxt.call(ctxt, 'clone_file', flex_vol=flex_vol,
src_path=src_path, dest_path=dest_path,
vserver=vserver, dest_exists=dest_exists,
is_snapshot=is_snapshot)

def file_assign_qos(self, ctxt, host, vol_name,
qos_policy_group_name, path):
cctxt = self._get_cctxt(host=host)
Expand All @@ -77,6 +91,20 @@ def get_file_sizes_by_dir(self, ctxt, path):
# Returns used bytes of a file
return self._driver.zapi_client.get_file_sizes_by_dir(path)

def get_vserver_for_ip(self, ctxt, lif_ip):
# Returns vserver name from LIF ip
return self._driver._get_vserver_for_ip(lif_ip)

def clone_file(self, ctxt, flex_vol, src_path, dest_path, vserver,
dest_exists, is_snapshot):
# Clones a file on ONTAP
self._driver.zapi_client.clone_file(flex_vol=flex_vol,
src_path=src_path,
dest_path=dest_path,
vserver=vserver,
dest_exists=dest_exists,
is_snapshot=is_snapshot)

def file_assign_qos(self, ctxt, vol_name,
qos_policy_group_name, path):
# Sets QOS policy on a file
Expand Down
241 changes: 212 additions & 29 deletions cinder/volume/drivers/vmware/fcd.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,27 @@ def delete_volume(self, volume):
else:
raise ex

def init_kvm_hw(self, volume, connector, initiator_data):
fcd_loc = vops.FcdLocation.from_provider_location(
self._provider_location_to_moref_location(
volume.provider_location
))
vmdk_path = self.volumeops.get_vmdk_path_for_fcd(fcd_loc=fcd_loc)
mount_path = self.volumeops._get_mount_path(fcd_loc.ds_ref())
_, dir_path, file_path = vops.split_datastore_path(vmdk_path)
raw_file_path = file_path.replace('.vmdk', '-flat.vmdk')
connection_info = {
'driver_volume_type': 'nfs',
'data': {
'export': mount_path,
'name': "%s%s" % (dir_path, raw_file_path),
'format': 'raw',
'version': self.ATTACHMENT_VERSION,
},
'mount_point_base': '/var/lib/cinder/mnt'
}
return connection_info

@volume_utils.trace
def initialize_connection(self, volume, connector, initiator_data=None):
"""Allow connection to connector and return connection info.
Expand All @@ -315,6 +336,17 @@ def initialize_connection(self, volume, connector, initiator_data=None):
initialize_connection calls.
:returns: A dictionary of connection information.
"""
fcd_loc = vops.FcdLocation.from_provider_location(
self._provider_location_to_moref_location(
volume.provider_location
)
)
summary = self.volumeops.summary(fcd_loc.ds_ref())
if summary.type == "NFS41":
# We connect to KVM not VMware, only if DS is NFS
if 'connection_capabilities' not in connector:
data = self.init_kvm_hw(volume, connector, initiator_data)
return data
# Check that connection_capabilities match
# This ensures the connector is bound to the same vCenter service
if 'connection_capabilities' in connector:
Expand All @@ -324,11 +356,6 @@ def initialize_connection(self, volume, connector, initiator_data=None):
raise exception.ConnectorRejected(
reason="Connector is missing %s" % ', '.join(missing))

fcd_loc = vops.FcdLocation.from_provider_location(
self._provider_location_to_moref_location(
volume.provider_location
)
)
# We don't need this parameters unless backup is created/restored
backup = False
backing_moref = ""
Expand Down Expand Up @@ -402,10 +429,12 @@ def terminate_connection(self, volume, connector, force=False, **kwargs):
try:
# we store the PL with a datastore name, but volumeops uses
# the moref format, so we need to convert it.
backing = self.volumeops.get_backing_by_uuid(volume.id)
provider_loc = self._provider_location_to_moref_location(
volume.provider_location
)
self._delete_fcd(provider_loc, delete_folder=False)
if backing:
self._delete_fcd(provider_loc, delete_folder=False)
except vexc.VimException as ex:
if "could not be found" in str(ex):
pass
Expand All @@ -418,30 +447,35 @@ def terminate_connection(self, volume, connector, force=False, **kwargs):
time.sleep(10)

(_, _, _, summary) = self._select_ds_for_volume(volume)
backing = self.volumeops.get_backing_by_uuid(volume.id)
self.volumeops.rename_backing(backing, volume.name)
self.volumeops.update_backing_disk_uuid(backing, volume.id)
profile_id = self._get_storage_profile_id(volume)
vmware_host_ip = self.configuration.vmware_host_ip

# Now move the vmdk into the original folder here?
dest_dc = self.volumeops.get_dc(backing)
src_vmdk_path = self.volumeops.get_vmdk_path(backing)
conv_prov_loc = self._provider_location_to_ds_name_location
dest_vmdk_path = f"[{summary.name}] {volume.id}/{volume.id}.vmdk"
disk_device = self.volumeops._get_disk_device(backing)
self.volumeops.detach_disk_from_backing(backing, disk_device)
self.volumeops.move_vmdk_file(dest_dc, src_vmdk_path,
dest_vmdk_path)
fcd_loc = vops.FcdLocation.from_provider_location(provider_loc)
fcd_id = fcd_loc.fcd_id
fcd_loc = self.volumeops.update_fcd_after_backup_restore(
volume, backing, profile_id, vmware_host_ip,
dest_vmdk_path, fcd_id)
provider_location = self._provider_location_to_ds_name_location(
fcd_loc.provider_location()
)
volume.update({'provider_location': provider_location})
volume.save()
backing = self.volumeops.get_backing_by_uuid(volume.id)
# If there is no backing, we did the restore using NFS client
if backing:
self.volumeops.rename_backing(backing, volume.name)
self.volumeops.update_backing_disk_uuid(backing, volume.id)
profile_id = self._get_storage_profile_id(volume)
vmware_host_ip = self.configuration.vmware_host_ip

# Now move the vmdk into the original folder here?
dest_dc = self.volumeops.get_dc(backing)
src_vmdk_path = self.volumeops.get_vmdk_path(backing)

disk_device = self.volumeops._get_disk_device(backing)
self.volumeops.detach_disk_from_backing(backing, disk_device)
self.volumeops.move_vmdk_file(dest_dc, src_vmdk_path,
dest_vmdk_path)
fcd_loc = vops.FcdLocation.from_provider_location(
provider_loc)
fcd_id = fcd_loc.fcd_id
fcd_loc = self.volumeops.update_fcd_after_backup_restore(
volume, backing, profile_id, vmware_host_ip,
dest_vmdk_path, fcd_id)
provider_location = conv_prov_loc(
fcd_loc.provider_location()
)
volume.update({'provider_location': provider_location})
volume.save()
else:
backing = self.volumeops.get_backing_by_uuid(volume.id)
fcd_loc = vops.FcdLocation.from_provider_location(
Expand Down Expand Up @@ -647,6 +681,16 @@ def extend_volume(self, volume, new_size):
"""
# convert the datastore name provider location to what the
# volumeops uses, which is the moref format.
if volume['attach_status'] == 'attached':
attachments = volume.volume_attachment
connector = None
for attach in attachments:
connector = attach.connector
if 'connection_capabilities' not in connector:
LOG.debug('Non-VMware hypervisor for volume:%s,'
'extend will be done via nova', volume['id'])
return

fcd_loc = vops.FcdLocation.from_provider_location(
self._provider_location_to_moref_location(
volume.provider_location
Expand Down Expand Up @@ -676,12 +720,143 @@ def _clone_fcd(self, provider_loc, volume, dest_ds_ref,
disk_type, profile_id=profile_id, key_id=key_id
)

def _create_volume_from_fcd_kvm(self, volume, src_vref):
"""We need to call out to netapp in case of clone

:param volume: target volume
:param src_vref: source volume
"""
fcd_loc = vops.FcdLocation.from_provider_location(
self._provider_location_to_moref_location(
src_vref.provider_location
)
)
disk_type = self._get_disk_type(src_vref)
ds_ref = fcd_loc.ds_ref()
profile_id = self._get_storage_profile_id(volume)
key_id = self._register_kmip_key_id(volume)
new_fcd_loc = self.volumeops.create_fcd(
volume.id, volume.name, src_vref.size * units.Ki, ds_ref,
disk_type, profile_id=profile_id, key_id=key_id)
vmdk_path_new = self.volumeops.get_vmdk_path_for_fcd(
fcd_loc=new_fcd_loc)
vmdk_path_src = self.volumeops.get_vmdk_path_for_fcd(
fcd_loc=fcd_loc)
netapp_api = self._remote_netapp_api
netapp_fqdn = self.volumeops.get_netapp_for_ds(fcd_loc.ds_ref())
netapp_host = self.get_netapp_cinder_host(netapp_fqdn)
src_mpath = self.volumeops._get_mount_path(fcd_loc.ds_ref())
src_ds_mpath = src_mpath.split(':')[1]
src_lif_ip = src_mpath.split(':')[0]
netapp_vol = src_ds_mpath.split('/')[1]
ds_split = vops.split_datastore_path
vserver = netapp_api.get_vserver_for_ip(self._admin_context,
host=netapp_host,
lif_ip=src_lif_ip)
_, folder_path, src_vmdk_file = ds_split(vmdk_path_src)
_, new_folder_path, dst_vmdk_file = ds_split(vmdk_path_new)
src_flat_file = src_vmdk_file.replace('.vmdk', '-flat.vmdk')
dst_flat_file = dst_vmdk_file.replace('.vmdk', '-flat.vmdk')
if len(src_ds_mpath.split('/')) == 3:
# This case if we have qtree DS
src_qtree = src_ds_mpath.split('/')[2]
src_path = "%s/%s%s" % (src_qtree, folder_path,
src_flat_file)
dest_path = "%s/%s%s" % (src_qtree, new_folder_path,
dst_flat_file)
else:
# Normal DS expected that the vol_name=DS_NAME
src_path = "%s%s" % (folder_path, src_flat_file)
dest_path = "%s%s" % (new_folder_path, dst_flat_file)
netapp_api.clone_file(self._admin_context, host=netapp_host,
flex_vol=netapp_vol, src_path=src_path,
dest_path=dest_path, vserver=vserver,
dest_exists=True, is_snapshot=True)

cur_size = src_vref.size
self._extend_if_needed(new_fcd_loc, cur_size, volume.size)
p_location = self._provider_location_to_ds_name_location(
new_fcd_loc.provider_location()
)

return {'provider_location': p_location}

def _create_snap_kvm_fcd(self, snapshot):
"""Creates snapshot via NetApp rpc

:param snapshot: The snapshot to create.
"""
volume = snapshot.volume
fcd_loc = vops.FcdLocation.from_provider_location(
self._provider_location_to_moref_location(
volume.provider_location
)
)
disk_type = self._get_disk_type(volume)
ds_ref = self._select_ds_fcd(volume)
profile_id = self._get_storage_profile_id(volume)
key_id = self._register_kmip_key_id(volume)
snapshot_name = "snapshot-%s" % snapshot['id']
# We create a new empty fcd with the same size as the volume
fcd_loc_snap = self.volumeops.create_fcd(
snapshot.id, snapshot_name, volume.size * units.Ki, ds_ref,
disk_type, profile_id=profile_id, key_id=key_id)
vmdk_path_snap = self.volumeops.get_vmdk_path_for_fcd(
fcd_loc=fcd_loc_snap)
vmdk_path_vol = self.volumeops.get_vmdk_path_for_fcd(
fcd_loc=fcd_loc)
netapp_api = self._remote_netapp_api
netapp_fqdn = self.volumeops.get_netapp_for_ds(fcd_loc.ds_ref())
netapp_host = self.get_netapp_cinder_host(netapp_fqdn)
src_mpath = self.volumeops._get_mount_path(fcd_loc.ds_ref())
src_ds_mpath = src_mpath.split(':')[1]
src_lif_ip = src_mpath.split(':')[0]
netapp_vol = src_ds_mpath.split('/')[1]
ds_split = vops.split_datastore_path
vserver = netapp_api.get_vserver_for_ip(self._admin_context,
host=netapp_host,
lif_ip=src_lif_ip)
_, folder_path, src_vmdk_file = ds_split(vmdk_path_vol)
_, snap_folder_path, dst_vmdk_file = ds_split(vmdk_path_snap)
src_flat_file = src_vmdk_file.replace('.vmdk', '-flat.vmdk')
dst_flat_file = dst_vmdk_file.replace('.vmdk', '-flat.vmdk')
if len(src_ds_mpath.split('/')) == 3:
# This case if we have qtree DS
src_qtree = src_ds_mpath.split('/')[2]
src_path = "%s/%s%s" % (src_qtree, folder_path,
src_flat_file)
dest_path = "%s/%s%s" % (src_qtree, snap_folder_path,
dst_flat_file)
else:
# Normal DS expected that the vol_name=DS_NAME
src_path = "%s%s" % (folder_path, src_flat_file)
dest_path = "%s%s" % (snap_folder_path, dst_flat_file)
netapp_api.clone_file(self._admin_context, host=netapp_host,
flex_vol=netapp_vol, src_path=src_path,
dest_path=dest_path, vserver=vserver,
dest_exists=True, is_snapshot=True)

p_location = self._provider_location_to_ds_name_location(
fcd_loc_snap.provider_location()
)

return p_location

@volume_utils.trace
def create_snapshot(self, snapshot):
"""Creates a snapshot.

:param snapshot: Information for the snapshot to be created.
"""
if snapshot.volume['attach_status'] == 'attached':
attachments = snapshot.volume.volume_attachment
connector = None
for attach in attachments:
connector = attach.connector
if 'connection_capabilities' not in connector:
p_location = self._create_snap_kvm_fcd(snapshot)
return {'provider_location': p_location}

if self._use_fcd_snapshot:
fcd_loc = vops.FcdLocation.from_provider_location(
provider_location=self._provider_location_to_moref_location(
Expand Down Expand Up @@ -810,6 +985,14 @@ def create_cloned_volume(self, volume, src_vref):
:param volume: New Volume object
:param src_vref: Source Volume object
"""

if src_vref['attach_status'] == 'attached':
attachments = src_vref.volume_attachment
connector = None
for attach in attachments:
connector = attach.connector
if 'connection_capabilities' not in connector:
return self._create_volume_from_fcd_kvm(volume, src_vref)
return self._create_volume_from_fcd(
src_vref.provider_location, src_vref.size, volume)

Expand Down