624 lines
25 KiB
Python
624 lines
25 KiB
Python
# Copyright (c) 2012 NetApp, Inc.
|
|
# Copyright (c) 2012 OpenStack Foundation
|
|
# All Rights Reserved.
|
|
#
|
|
# 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.
|
|
"""
|
|
Storage service catalog utility functions and classes for NetApp systems.
|
|
"""
|
|
|
|
import copy
|
|
from threading import Timer
|
|
|
|
from cinder import exception
|
|
from cinder.openstack.common import log as logging
|
|
from cinder.openstack.common import timeutils
|
|
from cinder import utils
|
|
from cinder.volume import driver
|
|
from cinder.volume.drivers.netapp import api
|
|
from cinder.volume.drivers.netapp import utils as na_utils
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class NetAppVolume(object):
|
|
"""Represents a NetApp volume.
|
|
|
|
Present attributes
|
|
id - name, vserver, junction_path, type
|
|
aggr - name, raid_type, ha_policy, disk_type
|
|
sis - dedup, compression
|
|
state - status, vserver_root, cluster_volume,
|
|
inconsistent, invalid, junction_active
|
|
qos - qos_policy_group
|
|
space - space-guarantee-enabled, space-guarantee,
|
|
thin_provisioned, size_avl_bytes, size_total_bytes
|
|
mirror - mirrored i.e. dp mirror
|
|
export - path
|
|
"""
|
|
def __init__(self, name, vserver=None):
|
|
self.id = {}
|
|
self.aggr = {}
|
|
self.sis = {}
|
|
self.state = {}
|
|
self.qos = {}
|
|
self.space = {}
|
|
self.mirror = {}
|
|
self.export = {}
|
|
self.id['name'] = name
|
|
self.id['vserver'] = vserver
|
|
|
|
def __eq__(self, other):
|
|
"""Checks for equality."""
|
|
if (self.id['name'] == other.id['name'] and
|
|
self.id['vserver'] == other.id['vserver']):
|
|
return True
|
|
|
|
def __hash__(self):
|
|
"""Computes hash for the object."""
|
|
return hash(self.id['name'])
|
|
|
|
def __cmp__(self, other):
|
|
"""Implements comparison logic for volumes."""
|
|
self_size_avl = self.space.get('size_avl_bytes')
|
|
other_size_avl = other.space.get('size_avl_bytes')
|
|
if self_size_avl is None and other_size_avl is not None:
|
|
return -1
|
|
elif self_size_avl is not None and other_size_avl is None:
|
|
return 1
|
|
elif self_size_avl is None and other_size_avl is None:
|
|
return 0
|
|
elif int(self_size_avl) < int(other_size_avl):
|
|
return -1
|
|
elif int(self_size_avl) > int(other_size_avl):
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
def __str__(self):
|
|
"""Returns human readable form for object."""
|
|
vol_str = "NetApp Volume id: %s, aggr: %s,"\
|
|
" space: %s, sis: %s, state: %s, qos: %s"\
|
|
% (self.id, self.aggr, self.space, self.sis, self.state, self.qos)
|
|
return vol_str
|
|
|
|
|
|
def get_cluster_vols_with_ssc(na_server, vserver, volume=None):
|
|
"""Gets ssc vols for cluster vserver."""
|
|
volumes = query_cluster_vols_for_ssc(na_server, vserver, volume)
|
|
sis_vols = get_sis_vol_dict(na_server, vserver, volume)
|
|
mirrored_vols = get_snapmirror_vol_dict(na_server, vserver, volume)
|
|
aggrs = {}
|
|
for vol in volumes:
|
|
aggr_name = vol.aggr['name']
|
|
if aggr_name:
|
|
if aggr_name in aggrs:
|
|
aggr_attrs = aggrs[aggr_name]
|
|
else:
|
|
aggr_attrs = query_aggr_options(na_server, aggr_name)
|
|
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 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
|
|
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
|
|
|
|
|
|
def query_cluster_vols_for_ssc(na_server, vserver, volume=None):
|
|
"""Queries cluster volumes for ssc."""
|
|
query = {'volume-attributes': None}
|
|
volume_id = {'volume-id-attributes': {'owning-vserver-name': vserver}}
|
|
if volume:
|
|
volume_id['volume-id-attributes']['name'] = volume
|
|
query['volume-attributes'] = volume_id
|
|
des_attr = {'volume-attributes':
|
|
['volume-id-attributes',
|
|
'volume-space-attributes',
|
|
'volume-state-attributes',
|
|
'volume-qos-attributes']}
|
|
result = na_utils.invoke_api(na_server, api_name='volume-get-iter',
|
|
api_family='cm', query=query,
|
|
des_result=des_attr,
|
|
additional_elems=None,
|
|
is_iter=True)
|
|
vols = set()
|
|
for res in result:
|
|
records = res.get_child_content('num-records')
|
|
if records > 0:
|
|
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
|
|
|
|
|
|
def create_vol_list(vol_attrs):
|
|
"""Creates vol list with features from attr list."""
|
|
vols = set()
|
|
for v in vol_attrs:
|
|
try:
|
|
# name and vserver are mandatory
|
|
# Absence will skip by giving KeyError.
|
|
name = v['volume-id-attributes']['name']
|
|
vserver = v['volume-id-attributes']['owning-vserver-name']
|
|
vol = NetAppVolume(name, vserver)
|
|
vol.id['type'] =\
|
|
v['volume-id-attributes'].get_child_content('type')
|
|
if vol.id['type'] == "tmp":
|
|
continue
|
|
vol.id['junction_path'] =\
|
|
v['volume-id-attributes'].get_child_content('junction-path')
|
|
# state attributes mandatory.
|
|
vol.state['vserver_root'] =\
|
|
na_utils.to_bool(
|
|
v['volume-state-attributes'].get_child_content(
|
|
'is-vserver-root'))
|
|
if vol.state['vserver_root']:
|
|
continue
|
|
vol.state['status'] =\
|
|
v['volume-state-attributes'].get_child_content('state')
|
|
vol.state['inconsistent'] =\
|
|
na_utils.to_bool(
|
|
v['volume-state-attributes'].get_child_content(
|
|
'is-inconsistent'))
|
|
vol.state['invalid'] =\
|
|
na_utils.to_bool(
|
|
v['volume-state-attributes'].get_child_content(
|
|
'is-invalid'))
|
|
vol.state['junction_active'] =\
|
|
na_utils.to_bool(
|
|
v['volume-state-attributes'].get_child_content(
|
|
'is-junction-active'))
|
|
vol.state['cluster_volume'] =\
|
|
na_utils.to_bool(
|
|
v['volume-state-attributes'].get_child_content(
|
|
'is-cluster-volume'))
|
|
if (vol.state['status'] != 'online' or
|
|
vol.state['inconsistent'] or vol.state['invalid']):
|
|
# offline, invalid and inconsistent volumes are not usable
|
|
continue
|
|
# aggr attributes mandatory.
|
|
vol.aggr['name'] =\
|
|
v['volume-id-attributes']['containing-aggregate-name']
|
|
# space attributes mandatory.
|
|
vol.space['size_avl_bytes'] =\
|
|
v['volume-space-attributes']['size-available']
|
|
vol.space['size_total_bytes'] =\
|
|
v['volume-space-attributes']['size-total']
|
|
vol.space['space-guarantee-enabled'] =\
|
|
na_utils.to_bool(
|
|
v['volume-space-attributes'].get_child_content(
|
|
'is-space-guarantee-enabled'))
|
|
vol.space['space-guarantee'] =\
|
|
v['volume-space-attributes'].get_child_content(
|
|
'space-guarantee')
|
|
# qos attributes optional.
|
|
if v.get_child_by_name('volume-qos-attributes'):
|
|
vol.qos['qos_policy_group'] =\
|
|
v['volume-qos-attributes'].get_child_content(
|
|
'policy-group-name')
|
|
else:
|
|
vol.qos['qos_policy_group'] = None
|
|
vols.add(vol)
|
|
except KeyError as e:
|
|
LOG.debug(_('Unexpected error while creating'
|
|
' ssc vol list. Message - %s') % (e.message))
|
|
continue
|
|
return vols
|
|
|
|
|
|
def query_aggr_options(na_server, aggr_name):
|
|
"""Queries cluster aggr for attributes.
|
|
|
|
Currently queries for raid and ha-policy.
|
|
"""
|
|
|
|
add_elems = {'aggregate': aggr_name}
|
|
attrs = {}
|
|
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
|
|
|
|
|
|
def get_sis_vol_dict(na_server, vserver, volume=None):
|
|
"""Queries sis for volumes.
|
|
|
|
If volume is present sis is queried for it.
|
|
Records dedup and compression enabled.
|
|
"""
|
|
|
|
sis_vols = {}
|
|
query_attr = {'vserver': vserver}
|
|
if volume:
|
|
vol_path = '/vol/%s' % (volume)
|
|
query_attr['path'] = vol_path
|
|
query = {'sis-status-info': query_attr}
|
|
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
|
|
|
|
|
|
def get_snapmirror_vol_dict(na_server, vserver, volume=None):
|
|
"""Queries snapmirror volumes."""
|
|
mirrored_vols = {}
|
|
query_attr = {'source-vserver': vserver}
|
|
if volume:
|
|
query_attr['source-volume'] = volume
|
|
query = {'snapmirror-info': query_attr}
|
|
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 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']}}
|
|
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'
|
|
|
|
|
|
def get_cluster_ssc(na_server, vserver):
|
|
"""Provides cluster volumes with ssc."""
|
|
netapp_volumes = get_cluster_vols_with_ssc(na_server, vserver)
|
|
mirror_vols = set()
|
|
dedup_vols = set()
|
|
compress_vols = set()
|
|
thin_prov_vols = set()
|
|
ssc_map = {'mirrored': mirror_vols, 'dedup': dedup_vols,
|
|
'compression': compress_vols,
|
|
'thin': thin_prov_vols, 'all': netapp_volumes}
|
|
for vol in netapp_volumes:
|
|
if vol.sis.get('dedup'):
|
|
dedup_vols.add(vol)
|
|
if vol.sis.get('compression'):
|
|
compress_vols.add(vol)
|
|
if vol.mirror.get('mirrored'):
|
|
mirror_vols.add(vol)
|
|
if vol.space.get('thin_provisioned'):
|
|
thin_prov_vols.add(vol)
|
|
return ssc_map
|
|
|
|
|
|
def refresh_cluster_stale_ssc(*args, **kwargs):
|
|
"""Refreshes stale ssc volumes with latest."""
|
|
backend = args[0]
|
|
na_server = args[1]
|
|
vserver = args[2]
|
|
identity = str(id(backend))
|
|
lock_pr = '%s_%s' % ('refresh_ssc', identity)
|
|
try:
|
|
job_set = na_utils.set_safe_attr(
|
|
backend, 'refresh_stale_running', True)
|
|
if not job_set:
|
|
return
|
|
|
|
@utils.synchronized(lock_pr)
|
|
def refresh_stale_ssc():
|
|
stale_vols = backend._update_stale_vols(reset=True)
|
|
LOG.info(_('Running stale ssc refresh job for %(server)s'
|
|
' and vserver %(vs)s')
|
|
% {'server': na_server, 'vs': vserver})
|
|
# refreshing single volumes can create inconsistency
|
|
# hence doing manipulations on copy
|
|
ssc_vols_copy = copy.deepcopy(backend.ssc_vols)
|
|
refresh_vols = set()
|
|
expired_vols = set()
|
|
for vol in stale_vols:
|
|
name = vol.id['name']
|
|
res = get_cluster_vols_with_ssc(na_server, vserver, name)
|
|
if res:
|
|
refresh_vols.add(res.pop())
|
|
else:
|
|
expired_vols.add(vol)
|
|
for vol in refresh_vols:
|
|
for k in ssc_vols_copy:
|
|
vol_set = ssc_vols_copy[k]
|
|
vol_set.discard(vol)
|
|
if k == "mirrored" and vol.mirror.get('mirrored'):
|
|
vol_set.add(vol)
|
|
if k == "dedup" and vol.sis.get('dedup'):
|
|
vol_set.add(vol)
|
|
if k == "compression" and vol.sis.get('compression'):
|
|
vol_set.add(vol)
|
|
if k == "thin" and vol.space.get('thin_provisioned'):
|
|
vol_set.add(vol)
|
|
if k == "all":
|
|
vol_set.add(vol)
|
|
for vol in expired_vols:
|
|
for k in ssc_vols_copy:
|
|
vol_set = ssc_vols_copy[k]
|
|
vol_set.discard(vol)
|
|
backend.refresh_ssc_vols(ssc_vols_copy)
|
|
LOG.info(_('Successfully completed stale refresh job for'
|
|
' %(server)s and vserver %(vs)s')
|
|
% {'server': na_server, 'vs': vserver})
|
|
|
|
refresh_stale_ssc()
|
|
finally:
|
|
na_utils.set_safe_attr(backend, 'refresh_stale_running', False)
|
|
|
|
|
|
def get_cluster_latest_ssc(*args, **kwargs):
|
|
"""Updates volumes including ssc."""
|
|
backend = args[0]
|
|
na_server = args[1]
|
|
vserver = args[2]
|
|
identity = str(id(backend))
|
|
lock_pr = '%s_%s' % ('refresh_ssc', identity)
|
|
|
|
# As this depends on stale job running state
|
|
# set flag as soon as job starts to avoid
|
|
# job accumulation.
|
|
try:
|
|
job_set = na_utils.set_safe_attr(backend, 'ssc_job_running', True)
|
|
if not job_set:
|
|
return
|
|
|
|
@utils.synchronized(lock_pr)
|
|
def get_latest_ssc():
|
|
LOG.info(_('Running cluster latest ssc job for %(server)s'
|
|
' and vserver %(vs)s')
|
|
% {'server': na_server, 'vs': vserver})
|
|
ssc_vols = get_cluster_ssc(na_server, vserver)
|
|
backend.refresh_ssc_vols(ssc_vols)
|
|
backend.ssc_run_time = timeutils.utcnow()
|
|
LOG.info(_('Successfully completed ssc job for %(server)s'
|
|
' and vserver %(vs)s')
|
|
% {'server': na_server, 'vs': vserver})
|
|
|
|
get_latest_ssc()
|
|
finally:
|
|
na_utils.set_safe_attr(backend, 'ssc_job_running', False)
|
|
|
|
|
|
def refresh_cluster_ssc(backend, na_server, vserver, synchronous=False):
|
|
"""Refresh cluster ssc for backend."""
|
|
if not isinstance(backend, driver.VolumeDriver):
|
|
raise exception.InvalidInput(reason=_("Backend not a VolumeDriver."))
|
|
if not isinstance(na_server, api.NaServer):
|
|
raise exception.InvalidInput(reason=_("Backend server not NaServer."))
|
|
delta_secs = getattr(backend, 'ssc_run_delta_secs', 1800)
|
|
if getattr(backend, 'ssc_job_running', None):
|
|
LOG.warn(_('ssc job in progress. Returning... '))
|
|
return
|
|
elif (getattr(backend, 'ssc_run_time', None) is None or
|
|
(backend.ssc_run_time and
|
|
timeutils.is_newer_than(backend.ssc_run_time, delta_secs))):
|
|
if synchronous:
|
|
get_cluster_latest_ssc(backend, na_server, vserver)
|
|
else:
|
|
t = Timer(0, get_cluster_latest_ssc,
|
|
args=[backend, na_server, vserver])
|
|
t.start()
|
|
elif getattr(backend, 'refresh_stale_running', None):
|
|
LOG.warn(_('refresh stale ssc job in progress. Returning... '))
|
|
return
|
|
else:
|
|
if backend.stale_vols:
|
|
if synchronous:
|
|
refresh_cluster_stale_ssc(backend, na_server, vserver)
|
|
else:
|
|
t = Timer(0, refresh_cluster_stale_ssc,
|
|
args=[backend, na_server, vserver])
|
|
t.start()
|
|
|
|
|
|
def get_volumes_for_specs(ssc_vols, specs):
|
|
"""Shortlists volumes for extra specs provided."""
|
|
if specs is None or not isinstance(specs, dict):
|
|
return ssc_vols['all']
|
|
result = copy.deepcopy(ssc_vols['all'])
|
|
raid_type = specs.get('netapp:raid_type')
|
|
disk_type = specs.get('netapp:disk_type')
|
|
bool_specs_list = ['netapp_mirrored', 'netapp_unmirrored',
|
|
'netapp_dedup', 'netapp_nodedup',
|
|
'netapp_compression', 'netapp_nocompression',
|
|
'netapp_thin_provisioned', 'netapp_thick_provisioned']
|
|
b_specs = {}
|
|
for spec in bool_specs_list:
|
|
b_specs[spec] = na_utils.to_bool(specs.get(spec))\
|
|
if specs.get(spec) else None
|
|
|
|
def _spec_ineffect(b_specs, spec, opp_spec):
|
|
"""If the spec with opposite spec is ineffective."""
|
|
if ((b_specs[spec] is None and b_specs[opp_spec] is None)
|
|
or (b_specs[spec] == b_specs[opp_spec])):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
if _spec_ineffect(b_specs, 'netapp_mirrored', 'netapp_unmirrored'):
|
|
pass
|
|
else:
|
|
if b_specs['netapp_mirrored'] or b_specs['netapp_unmirrored'] is False:
|
|
result = result & ssc_vols['mirrored']
|
|
else:
|
|
result = result - ssc_vols['mirrored']
|
|
if _spec_ineffect(b_specs, 'netapp_dedup', 'netapp_nodedup'):
|
|
pass
|
|
else:
|
|
if b_specs['netapp_dedup'] or b_specs['netapp_nodedup'] is False:
|
|
result = result & ssc_vols['dedup']
|
|
else:
|
|
result = result - ssc_vols['dedup']
|
|
if _spec_ineffect(b_specs, 'netapp_compression', 'netapp_nocompression'):
|
|
pass
|
|
else:
|
|
if (b_specs['netapp_compression'] or
|
|
b_specs['netapp_nocompression'] is False):
|
|
result = result & ssc_vols['compression']
|
|
else:
|
|
result = result - ssc_vols['compression']
|
|
if _spec_ineffect(b_specs, 'netapp_thin_provisioned',
|
|
'netapp_thick_provisioned'):
|
|
pass
|
|
else:
|
|
if (b_specs['netapp_thin_provisioned'] or
|
|
b_specs['netapp_thick_provisioned'] is False):
|
|
result = result & ssc_vols['thin']
|
|
else:
|
|
result = result - ssc_vols['thin']
|
|
if raid_type or disk_type:
|
|
tmp = copy.deepcopy(result)
|
|
for vol in tmp:
|
|
if raid_type:
|
|
vol_raid = vol.aggr['raid_type']
|
|
vol_raid = vol_raid.lower() if vol_raid else None
|
|
if raid_type.lower() != vol_raid:
|
|
result.discard(vol)
|
|
if disk_type:
|
|
vol_dtype = vol.aggr['disk_type']
|
|
vol_dtype = vol_dtype.lower() if vol_dtype else None
|
|
if disk_type.lower() != vol_dtype:
|
|
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': ['netapp:disk_type'],
|
|
'snapmirror-get-iter': ['netapp_mirrored',
|
|
'netapp_unmirrored'],
|
|
'sis-get-iter': ['netapp_dedup', 'netapp_nodedup',
|
|
'netapp_compression',
|
|
'netapp_nocompression'],
|
|
'aggr-options-list-info': ['netapp:raid_type'],
|
|
'volume-get-iter': []}
|
|
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 netapp apis. The following"
|
|
" extra_specs will fail or be ignored: %s"),
|
|
unsupp_ssc_features)
|