from cinder.volume import configuration as conf
from cinder.volume.drivers.netapp import api
from cinder.volume.drivers.netapp import nfs as netapp_nfs
+from cinder.volume.drivers.netapp import ssc_utils
from oslo.config import cfg
mox.StubOutWithMock(drv, '_get_provider_location')
mox.StubOutWithMock(drv, '_volume_not_present')
+ mox.StubOutWithMock(drv, '_post_prov_deprov_in_ssc')
if snapshot_exists:
mox.StubOutWithMock(drv, '_execute')
mox.StubOutWithMock(drv, '_get_volume_path')
-
+ drv._get_provider_location(IgnoreArg())
drv._get_provider_location(IgnoreArg())
drv._volume_not_present(IgnoreArg(), IgnoreArg())\
.AndReturn(not snapshot_exists)
drv._get_volume_path(IgnoreArg(), IgnoreArg())
drv._execute('rm', None, run_as_root=True)
+ drv._post_prov_deprov_in_ssc(IgnoreArg())
+
mox.ReplayAll()
return mox
# set required flags
for flag in required_flags:
setattr(drv.configuration, flag, 'val')
+ setattr(drv, 'ssc_enabled', False)
+
+ mox.StubOutWithMock(netapp_nfs.NetAppDirectNfsDriver, '_check_flags')
+ netapp_nfs.NetAppDirectNfsDriver._check_flags()
mox.ReplayAll()
drv.check_for_setup_error()
mox.StubOutWithMock(drv, '_get_if_info_by_ip')
mox.StubOutWithMock(drv, '_get_vol_by_junc_vserver')
mox.StubOutWithMock(drv, '_clone_file')
+ mox.StubOutWithMock(drv, '_post_prov_deprov_in_ssc')
drv._get_host_ip(IgnoreArg()).AndReturn('127.0.0.1')
drv._get_export_path(IgnoreArg()).AndReturn('/nfs')
drv._get_vol_by_junc_vserver('openstack', '/nfs').AndReturn('nfsvol')
drv._clone_file('nfsvol', 'volume_name', 'clone_name',
'openstack')
+ drv._post_prov_deprov_in_ssc(IgnoreArg())
return mox
def _prepare_info_by_ip_response(self):
volume_name = 'volume_name'
clone_name = 'clone_name'
volume_id = volume_name + str(hash(volume_name))
+ share = 'ip:/share'
- drv._clone_volume(volume_name, clone_name, volume_id)
+ drv._clone_volume(volume_name, clone_name, volume_id, share)
mox.VerifyAll()
self._driver = netapp_nfs.NetAppDirect7modeNfsDriver(
configuration=create_configuration())
+ def _prepare_delete_snapshot_mock(self, snapshot_exists):
+ drv = self._driver
+ mox = self.mox
+
+ mox.StubOutWithMock(drv, '_get_provider_location')
+ mox.StubOutWithMock(drv, '_volume_not_present')
+
+ if snapshot_exists:
+ mox.StubOutWithMock(drv, '_execute')
+ mox.StubOutWithMock(drv, '_get_volume_path')
+
+ drv._get_provider_location(IgnoreArg())
+ drv._volume_not_present(IgnoreArg(), IgnoreArg())\
+ .AndReturn(not snapshot_exists)
+
+ if snapshot_exists:
+ drv._get_volume_path(IgnoreArg(), IgnoreArg())
+ drv._execute('rm', None, run_as_root=True)
+
+ mox.ReplayAll()
+
+ return mox
+
def test_check_for_setup_error_version(self):
drv = self._driver
drv._client = api.NaServer("127.0.0.1")
aggr_attrs = aggrs[aggr_name]
else:
aggr_attrs = query_aggr_options(na_server, aggr_name)
- eff_disk_type = query_aggr_storage_disk(na_server, aggr_name)
- aggr_attrs['disk_type'] = eff_disk_type
+ if aggr_attrs:
+ eff_disk_type = query_aggr_storage_disk(na_server,
+ aggr_name)
+ aggr_attrs['disk_type'] = eff_disk_type
aggrs[aggr_name] = aggr_attrs
vol.aggr['raid_type'] = aggr_attrs.get('raid_type')
vol.aggr['ha_policy'] = aggr_attrs.get('ha_policy')
vol.aggr['disk_type'] = aggr_attrs.get('disk_type')
- if vol.id['name'] in sis_vols:
- vol.sis['dedup'] = sis_vols[vol.id['name']]['dedup']
- vol.sis['compression'] = sis_vols[vol.id['name']]['compression']
- else:
- vol.sis['dedup'] = False
- vol.sis['compression'] = False
+ if sis_vols:
+ if vol.id['name'] in sis_vols:
+ vol.sis['dedup'] = sis_vols[vol.id['name']]['dedup']
+ vol.sis['compression'] =\
+ sis_vols[vol.id['name']]['compression']
+ else:
+ vol.sis['dedup'] = False
+ vol.sis['compression'] = False
if (vol.space['space-guarantee-enabled'] and
(vol.space['space-guarantee'] == 'file' or
vol.space['space-guarantee'] == 'volume')):
vol.space['thin_provisioned'] = False
else:
vol.space['thin_provisioned'] = True
- vol.mirror['mirrored'] = False
- if vol.id['name'] in mirrored_vols:
- for mirr_attrs in mirrored_vols[vol.id['name']]:
- if (mirr_attrs['rel_type'] == 'data_protection' and
- mirr_attrs['mirr_state'] == 'snapmirrored'):
- vol.mirror['mirrored'] = True
- break
+ if mirrored_vols:
+ vol.mirror['mirrored'] = False
+ if vol.id['name'] in mirrored_vols:
+ for mirr_attrs in mirrored_vols[vol.id['name']]:
+ if (mirr_attrs['rel_type'] == 'data_protection' and
+ mirr_attrs['mirr_state'] == 'snapmirrored'):
+ vol.mirror['mirrored'] = True
+ break
return volumes
for res in result:
records = res.get_child_content('num-records')
if records > 0:
- attr_list = res['attributes-list']
- vol_attrs = attr_list.get_children()
- vols_found = create_vol_list(vol_attrs)
- vols.update(vols_found)
+ attr_list = res.get_child_by_name('attributes-list')
+ if attr_list:
+ vol_attrs = attr_list.get_children()
+ vols_found = create_vol_list(vol_attrs)
+ vols.update(vols_found)
return vols
"""
add_elems = {'aggregate': aggr_name}
- result = na_utils.invoke_api(na_server,
- api_name='aggr-options-list-info',
- api_family='cm', query=None,
- des_result=None,
- additional_elems=add_elems,
- is_iter=False)
attrs = {}
- for res in result:
- options = res.get_child_by_name('options')
- if options:
- op_list = options.get_children()
- for op in op_list:
- if op.get_child_content('name') == 'ha_policy':
- attrs['ha_policy'] = op.get_child_content('value')
- if op.get_child_content('name') == 'raidtype':
- attrs['raid_type'] = op.get_child_content('value')
+ try:
+ result = na_utils.invoke_api(na_server,
+ api_name='aggr-options-list-info',
+ api_family='cm', query=None,
+ des_result=None,
+ additional_elems=add_elems,
+ is_iter=False)
+ for res in result:
+ options = res.get_child_by_name('options')
+ if options:
+ op_list = options.get_children()
+ for op in op_list:
+ if op.get_child_content('name') == 'ha_policy':
+ attrs['ha_policy'] = op.get_child_content('value')
+ if op.get_child_content('name') == 'raidtype':
+ attrs['raid_type'] = op.get_child_content('value')
+ except Exception as e:
+ LOG.debug(_("Exception querying aggr options. %s"), e)
return attrs
vol_path = '/vol/%s' % (volume)
query_attr['path'] = vol_path
query = {'sis-status-info': query_attr}
- result = na_utils.invoke_api(na_server,
- api_name='sis-get-iter',
- api_family='cm',
- query=query,
- is_iter=True)
- for res in result:
- attr_list = res.get_child_by_name('attributes-list')
- if attr_list:
- sis_status = attr_list.get_children()
- for sis in sis_status:
- path = sis.get_child_content('path')
- if not path:
- continue
- (___, __, vol) = path.rpartition('/')
- if not vol:
- continue
- v_sis = {}
- v_sis['compression'] = na_utils.to_bool(
- sis.get_child_content('is-compression-enabled'))
- v_sis['dedup'] = na_utils.to_bool(
- sis.get_child_content('state'))
- sis_vols[vol] = v_sis
+ try:
+ result = na_utils.invoke_api(na_server,
+ api_name='sis-get-iter',
+ api_family='cm',
+ query=query,
+ is_iter=True)
+ for res in result:
+ attr_list = res.get_child_by_name('attributes-list')
+ if attr_list:
+ sis_status = attr_list.get_children()
+ for sis in sis_status:
+ path = sis.get_child_content('path')
+ if not path:
+ continue
+ (___, __, vol) = path.rpartition('/')
+ if not vol:
+ continue
+ v_sis = {}
+ v_sis['compression'] = na_utils.to_bool(
+ sis.get_child_content('is-compression-enabled'))
+ v_sis['dedup'] = na_utils.to_bool(
+ sis.get_child_content('state'))
+ sis_vols[vol] = v_sis
+ except Exception as e:
+ LOG.debug(_("Exception querying sis information. %s"), e)
return sis_vols
if volume:
query_attr['source-volume'] = volume
query = {'snapmirror-info': query_attr}
- result = na_utils.invoke_api(na_server, api_name='snapmirror-get-iter',
- api_family='cm', query=query, is_iter=True)
- for res in result:
- attr_list = res.get_child_by_name('attributes-list')
- if attr_list:
- snap_info = attr_list.get_children()
- for snap in snap_info:
- src_volume = snap.get_child_content('source-volume')
- v_snap = {}
- v_snap['dest_loc'] =\
- snap.get_child_content('destination-location')
- v_snap['rel_type'] =\
- snap.get_child_content('relationship-type')
- v_snap['mirr_state'] =\
- snap.get_child_content('mirror-state')
- if mirrored_vols.get(src_volume):
- mirrored_vols.get(src_volume).append(v_snap)
- else:
- mirrored_vols[src_volume] = [v_snap]
+ try:
+ result = na_utils.invoke_api(na_server,
+ api_name='snapmirror-get-iter',
+ api_family='cm', query=query,
+ is_iter=True)
+ for res in result:
+ attr_list = res.get_child_by_name('attributes-list')
+ if attr_list:
+ snap_info = attr_list.get_children()
+ for snap in snap_info:
+ src_volume = snap.get_child_content('source-volume')
+ v_snap = {}
+ v_snap['dest_loc'] =\
+ snap.get_child_content('destination-location')
+ v_snap['rel_type'] =\
+ snap.get_child_content('relationship-type')
+ v_snap['mirr_state'] =\
+ snap.get_child_content('mirror-state')
+ if mirrored_vols.get(src_volume):
+ mirrored_vols.get(src_volume).append(v_snap)
+ else:
+ mirrored_vols[src_volume] = [v_snap]
+ except Exception as e:
+ LOG.debug(_("Exception querying mirror information. %s"), e)
return mirrored_vols
def query_aggr_storage_disk(na_server, aggr):
- """Queries for storage disks assosiated to an aggregate."""
+ """Queries for storage disks associated to an aggregate."""
query = {'storage-disk-info': {'disk-raid-info':
{'disk-aggregate-info':
{'aggregate-name': aggr}}}}
des_attr = {'storage-disk-info':
{'disk-raid-info': ['effective-disk-type']}}
- result = na_utils.invoke_api(na_server,
- api_name='storage-disk-get-iter',
- api_family='cm', query=query,
- des_result=des_attr,
- additional_elems=None,
- is_iter=True)
- for res in result:
- attr_list = res.get_child_by_name('attributes-list')
- if attr_list:
- storage_disks = attr_list.get_children()
- for disk in storage_disks:
- raid_info = disk.get_child_by_name('disk-raid-info')
- if raid_info:
- eff_disk_type =\
- raid_info.get_child_content('effective-disk-type')
- if eff_disk_type:
- return eff_disk_type
- else:
- continue
+ try:
+ result = na_utils.invoke_api(na_server,
+ api_name='storage-disk-get-iter',
+ api_family='cm', query=query,
+ des_result=des_attr,
+ additional_elems=None,
+ is_iter=True)
+ for res in result:
+ attr_list = res.get_child_by_name('attributes-list')
+ if attr_list:
+ storage_disks = attr_list.get_children()
+ for disk in storage_disks:
+ raid_info = disk.get_child_by_name('disk-raid-info')
+ if raid_info:
+ eff_disk_type =\
+ raid_info.get_child_content('effective-disk-type')
+ if eff_disk_type:
+ return eff_disk_type
+ else:
+ continue
+ except Exception as e:
+ LOG.debug(_("Exception querying storage disk. %s"), e)
return 'unknown'
'compression': compress_vols,
'thin': thin_prov_vols, 'all': netapp_volumes}
for vol in netapp_volumes:
- if vol.sis['dedup']:
+ if vol.sis.get('dedup'):
dedup_vols.add(vol)
- if vol.sis['compression']:
+ if vol.sis.get('compression'):
compress_vols.add(vol)
- if vol.mirror['mirrored']:
+ if vol.mirror.get('mirrored'):
mirror_vols.add(vol)
- if vol.space['thin_provisioned']:
+ if vol.space.get('thin_provisioned'):
thin_prov_vols.add(vol)
return ssc_map
for k in ssc_vols_copy:
vol_set = ssc_vols_copy[k]
vol_set.discard(vol)
- if k == "mirrored" and vol.mirror['mirrored']:
+ if k == "mirrored" and vol.mirror.get('mirrored'):
vol_set.add(vol)
- if k == "dedup" and vol.sis['dedup']:
+ if k == "dedup" and vol.sis.get('dedup'):
vol_set.add(vol)
- if k == "compression" and vol.sis['compression']:
+ if k == "compression" and vol.sis.get('compression'):
vol_set.add(vol)
- if k == "thin" and vol.space['thin_provisioned']:
+ if k == "thin" and vol.space.get('thin_provisioned'):
vol_set.add(vol)
if k == "all":
vol_set.add(vol)
if qos_policy_group.lower() != vol_qos:
result.discard(vol)
return result
+
+
+def check_ssc_api_permissions(na_server):
+ """Checks backend ssc api permissions for the user."""
+ api_map = {'storage-disk-get-iter': ['disk type'],
+ 'snapmirror-get-iter': ['data protection mirror'],
+ 'sis-get-iter': ['deduplication', 'compression'],
+ 'aggr-options-list-info': ['raid type'],
+ 'volume-get-iter': ['volume information']}
+ failed_apis = na_utils.check_apis_on_cluster(na_server, api_map.keys())
+ if failed_apis:
+ if 'volume-get-iter' in failed_apis:
+ msg = _("Fatal error: User not permitted"
+ " to query NetApp volumes.")
+ raise exception.VolumeBackendAPIException(data=msg)
+ else:
+ unsupp_ssc_features = []
+ for fail in failed_apis:
+ unsupp_ssc_features.extend(api_map[fail])
+ LOG.warn(_("The user does not have access or sufficient"
+ " privileges to use all ssc apis. The ssc"
+ " features %s may not work as expected."),
+ unsupp_ssc_features)
from cinder import utils
from cinder.volume.drivers.netapp.api import NaApiError
from cinder.volume.drivers.netapp.api import NaElement
+from cinder.volume.drivers.netapp.api import NaErrors
from cinder.volume.drivers.netapp.api import NaServer
from cinder.volume import volume_types
vs_info = attr_list.get_child_by_name('vserver-info')
vs_name = vs_info.get_child_content('vserver-name')
return vs_name
- raise NaApiError(code='Not found', message='No records found')
+ return None
do_ems = True
if hasattr(requester, 'last_ems'):
ems = _create_ems(stats, netapp_backend, server_type)
try:
if server_type == "cluster":
- node = _get_cluster_node(na_server)
+ api_version = na_server.get_api_version()
+ if api_version:
+ major, minor = api_version
+ else:
+ raise NaApiError(code='Not found',
+ message='No api version found')
+ if major == 1 and minor > 15:
+ node = getattr(requester, 'vserver', None)
+ else:
+ node = _get_cluster_node(na_server)
+ if node is None:
+ raise NaApiError(code='Not found',
+ message='No vserver found')
na_server.set_vserver(node)
else:
na_server.set_vfiler(None)
na_server.invoke_successfully(ems, True)
- requester.last_ems = timeutils.utcnow()
LOG.debug(_("ems executed successfully."))
except NaApiError as e:
- LOG.debug(_("Failed to invoke ems. Message : %s") % e)
+ LOG.warn(_("Failed to invoke ems. Message : %s") % e)
+ finally:
+ requester.last_ems = timeutils.utcnow()
def validate_instantiation(**kwargs):
volume_type = volume_types.get_volume_type(ctxt, type_id)
specs = volume_type.get('extra_specs')
return specs
+
+
+def check_apis_on_cluster(na_server, api_list=[]):
+ """Checks api availability and permissions on cluster.
+
+ Checks api availability and permissions for executing user.
+ Returns a list of failed apis.
+ """
+ failed_apis = []
+ if api_list:
+ api_version = na_server.get_api_version()
+ if api_version:
+ major, minor = api_version
+ if major == 1 and minor < 20:
+ for api_name in api_list:
+ na_el = NaElement(api_name)
+ try:
+ na_server.invoke_successfully(na_el)
+ except Exception as e:
+ if isinstance(e, NaApiError):
+ if (e.code == NaErrors['API_NOT_FOUND'].code or
+ e.code ==
+ NaErrors['INSUFFICIENT_PRIVS'].code):
+ failed_apis.append(api_name)
+ elif major == 1 and minor >= 20:
+ failed_apis = copy.copy(api_list)
+ result = invoke_api(
+ na_server,
+ api_name='system-user-capability-get-iter',
+ api_family='cm',
+ additional_elems=None,
+ is_iter=True)
+ for res in result:
+ attr_list = res.get_child_by_name('attributes-list')
+ if attr_list:
+ capabilities = attr_list.get_children()
+ for capability in capabilities:
+ op_list = capability.get_child_by_name(
+ 'operation-list')
+ if op_list:
+ ops = op_list.get_children()
+ for op in ops:
+ apis = op.get_child_content('api-name')
+ if apis:
+ api_list = apis.split(',')
+ for api_name in api_list:
+ if (api_name and
+ api_name.strip()
+ in failed_apis):
+ failed_apis.remove(api_name)
+ else:
+ continue
+ else:
+ msg = _("Unsupported Clustered Data ONTAP version.")
+ raise exception.VolumeBackendAPIException(data=msg)
+ else:
+ msg = _("Api version could not be determined.")
+ raise exception.VolumeBackendAPIException(data=msg)
+ return failed_apis