NetApp: Improve REST API cover and fix internals

Improve coverage of REST API feature merged in stable 2023.1.
Fix Netapp internals tests

partially-implements: bp netapp-ontap-rest-api-client

Co-authored-by: Caique Mello <caiquemellosbo@gmail.com>
Co-authored-by: Helena Dantas <helenamylena@gmail.com>
Co-authored-by: Lucas Oliveira <lucasmoliveira059@gmail.com>
Co-authored-by: Matheus Andrade <matheus.andrade@netapp.com>
Co-authored-by: Renan Vitor <renanpiranguinho@gmail.com>
Change-Id: Ia5fe1834cc6643d5df0a25f9f4a074911d8ad550
This commit is contained in:
Nahim Alves de Souza 2023-02-24 18:31:14 +00:00 committed by renanpiranguinho
parent afd0d723bb
commit b4cc96d5fd
7 changed files with 1635 additions and 210 deletions

View File

@ -2180,6 +2180,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
raise exception.NetAppException(msg)
else:
api_args['encrypt'] = 'true'
else:
api_args['encrypt'] = 'false'
return api_args

View File

@ -15,6 +15,7 @@
import copy
from datetime import datetime
from http import client as http_client
import math
import re
import time
@ -276,11 +277,12 @@ class NetAppRestClient(object):
# NOTE(nahimsouza): SVM scoped account is not authorized to access
# the /cluster/nodes endpoint, that's why we use /private/cli
response = self.send_request('/private/cli/version', 'get',
query=query)
# Response is formatted as:
# 'NetApp Release 9.12.1: Wed Feb 01 01:10:18 UTC 2023'
version_full = response['records'][0]['version']
version_full = response['records'][0]['version']['full']
version_parsed = re.findall(r'\d+\.\d+\.\d+', version_full)[0]
version_splited = version_parsed.split('.')
return {
@ -707,8 +709,16 @@ class NetAppRestClient(object):
'svm.name': vserver,
}
if max_throughput:
body['fixed.max_throughput_iops'] = (
int(''.join(filter(str.isdigit, max_throughput))))
value = max_throughput.lower()
if 'iops' in max_throughput:
value = value.replace('iops', '')
value = int(value)
body['fixed.max_throughput_iops'] = value
else:
value = value.replace('b/s', '')
value = int(value)
body['fixed.max_throughput_mbps'] = math.ceil(value /
units.Mi)
return self.send_request('/storage/qos/policies', 'post',
body=body)
@ -775,7 +785,16 @@ class NetAppRestClient(object):
"""Set NFS the export policy for the specified volume."""
query = {"name": volume_name}
body = {'nas.export_policy.name': policy_name}
self.send_request('/storage/volumes/', 'patch', query=query, body=body)
try:
self.send_request('/storage/volumes/', 'patch', query=query,
body=body)
except netapp_api.api.NaApiError as e:
# NOTE(nahimsouza): Since this error is ignored in ZAPI, we are
# replicating the behavior here.
if e.code == netapp_api.EREST_CANNOT_MODITY_OFFLINE_VOLUME:
LOG.debug('Cannot modify offline volume: %s', volume_name)
return
@na_utils.trace
def create_nfs_export_policy(self, policy_name):
@ -862,15 +881,15 @@ class NetAppRestClient(object):
volume = {
'aggregate': aggregate,
'aggr-list': aggregate_list,
'junction-path': volume_infos.get('nas', {}).get('path', ''),
'name': volume_infos.get('name', ''),
'owning-vserver-name': volume_infos.get('svm', {}).get('name', ''),
'type': volume_infos.get('type', ''),
'style': volume_infos.get('style', ''),
'size': volume_infos.get('space', {}).get('size', ''),
'junction-path': volume_infos.get('nas', {}).get('path'),
'name': volume_infos.get('name'),
'owning-vserver-name': volume_infos.get('svm', {}).get('name'),
'type': volume_infos.get('type'),
'style': volume_infos.get('style'),
'size': volume_infos.get('space', {}).get('size'),
'qos-policy-group-name': (
volume_infos.get('qos', {}).get('policy', {}).get('name', '')),
'style-extended': volume_infos.get('style', '')
volume_infos.get('qos', {}).get('policy', {}).get('name')),
'style-extended': volume_infos.get('style')
}
return volume
@ -887,12 +906,11 @@ class NetAppRestClient(object):
return self._has_records(result)
@na_utils.trace
def create_cifs_share(self, share_name):
def create_cifs_share(self, share_name, path):
"""Create a CIFS share."""
share_path = f'/{share_name}'
body = {
'name': share_name,
'path': share_path,
'path': path,
'svm.name': self.vserver,
}
self.send_request('/protocols/cifs/shares', 'post', body=body)
@ -1025,8 +1043,15 @@ class NetAppRestClient(object):
body['qos.policy.name'] = qos_policy_group
if adaptive_qos_policy_group is not None:
body['qos.policy.name'] = adaptive_qos_policy_group
if encrypt is True:
body['encryption.enabled'] = 'true'
if not self.features.FLEXVOL_ENCRYPTION:
msg = 'Flexvol encryption is not supported on this backend.'
raise exception.NetAppException(msg)
else:
body['encryption.enabled'] = 'true'
else:
body['encryption.enabled'] = 'false'
return body
@ -1166,7 +1191,14 @@ class NetAppRestClient(object):
@na_utils.trace
def set_volume_snapdir_access(self, volume_name, hide_snapdir):
"""Set volume snapshot directory visibility."""
volume = self._get_volume_by_args(vol_name=volume_name)
try:
volume = self._get_volume_by_args(vol_name=volume_name)
except exception.NetAppException:
msg = _('Could not find volume %s to set snapdir access')
LOG.error(msg, volume_name)
raise exception.SnapshotResourceNotFound(name=volume_name)
uuid = volume['uuid']
body = {
@ -1194,6 +1226,7 @@ class NetAppRestClient(object):
try:
volume = self._get_volume_by_args(vol_name=share_name)
svm_uuid = volume['svm']['uuid']
except exception.NetAppException:
LOG.debug('Could not find fpolicy. Share not found: %s.',
share_name)
@ -1392,7 +1425,7 @@ class NetAppRestClient(object):
except exception.NetAppException:
msg = _("FPolicy event %s not found.")
LOG.debug(msg, event_name)
return
try:
self.send_request(
f'/protocols/fpolicy/{svm_uuid}/events/{event_name}', 'delete')
@ -1415,7 +1448,7 @@ class NetAppRestClient(object):
except exception.NetAppException:
msg = _("FPolicy policy %s not found.")
LOG.debug(msg, policy_name)
return
try:
self.send_request(
f'/protocols/fpolicy/{svm_uuid}/policies/{policy_name}',
@ -1593,9 +1626,7 @@ class NetAppRestClient(object):
LOG.debug('Volume %s unmounted.', volume_name)
return
except netapp_api.api.NaApiError as e:
# TODO(felipe_rodrigues): test the clone split mount error
# code for REST.
if (e.code == netapp_api.api.EAPIERROR
if (e.code == netapp_api.EREST_UNMOUNT_FAILED_LOCK
and 'job ID' in e.message):
msg = ('Could not unmount volume %(volume)s due to '
'ongoing volume operation: %(exception)s')
@ -1637,7 +1668,8 @@ class NetAppRestClient(object):
query = {
'name': qos_policy_group_name,
'fields': 'name,object_count,fixed.max_throughput_iops,svm.name',
'fields': 'name,object_count,fixed.max_throughput_iops,'
'fixed.max_throughput_mbps,svm.name',
}
try:
res = self.send_request('/storage/qos/policies', 'get',
@ -1659,10 +1691,21 @@ class NetAppRestClient(object):
policy_info = {
'policy-group': qos_policy_group_info.get('name'),
'vserver': qos_policy_group_info.get('svm', {}).get('name'),
'max-throughput': qos_policy_group_info.get('fixed', {}).get(
'max_throughput_iops'),
'num-workloads': int(qos_policy_group_info.get('object_count')),
}
iops = qos_policy_group_info.get('fixed', {}).get(
'max_throughput_iops')
mbps = qos_policy_group_info.get('fixed', {}).get(
'max_throughput_mbps')
if iops:
policy_info['max-throughput'] = f'{iops}iops'
elif mbps:
policy_info['max-throughput'] = f'{mbps * 1024 * 1024}b/s'
else:
policy_info['max-throughput'] = None
return policy_info
@na_utils.trace
@ -1734,13 +1777,6 @@ class NetAppRestClient(object):
# Attempt to delete any QoS policies named "deleted_manila-*".
self.remove_unused_qos_policy_groups()
@na_utils.trace
def _sanitize_qos_spec_value(self, value):
value = value.lower()
value = value.replace('iops', '').replace('b/s', '')
value = int(value)
return value
@na_utils.trace
def qos_policy_group_modify(self, qos_policy_group_name, max_throughput):
"""Modifies a QoS policy group."""
@ -1748,10 +1784,19 @@ class NetAppRestClient(object):
query = {
'name': qos_policy_group_name,
}
body = {
'fixed.max_throughput_iops':
self._sanitize_qos_spec_value(max_throughput)
}
body = {}
value = max_throughput.lower()
if 'iops' in value:
value = value.replace('iops', '')
value = int(value)
body['fixed.max_throughput_iops'] = value
body['fixed.max_throughput_mbps'] = 0
elif 'b/s' in value:
value = value.replace('b/s', '')
value = int(value)
body['fixed.max_throughput_mbps'] = math.ceil(value /
units.Mi)
body['fixed.max_throughput_iops'] = 0
res = self.send_request('/storage/qos/policies', 'get', query=query)
if not res.get('records'):
msg = ('QoS %s not found.') % qos_policy_group_name
@ -1839,8 +1884,13 @@ class NetAppRestClient(object):
@na_utils.trace
def get_snapshot(self, volume_name, snapshot_name):
"""Gets a single snapshot."""
try:
volume = self._get_volume_by_args(vol_name=volume_name)
except exception.NetAppException:
msg = _('Could not find volume %s to get snapshot')
LOG.error(msg, volume_name)
raise exception.SnapshotResourceNotFound(name=snapshot_name)
volume = self._get_volume_by_args(vol_name=volume_name)
uuid = volume['uuid']
query = {
'name': snapshot_name,
@ -1902,7 +1952,12 @@ class NetAppRestClient(object):
def delete_snapshot(self, volume_name, snapshot_name, ignore_owners=False):
"""Deletes a volume snapshot."""
volume = self._get_volume_by_args(vol_name=volume_name)
try:
volume = self._get_volume_by_args(vol_name=volume_name)
except exception.NetAppException:
msg = _('Could not find volume %s to delete snapshot')
LOG.warning(msg, volume_name)
return
uuid = volume['uuid']
query = {
@ -2118,15 +2173,15 @@ class NetAppRestClient(object):
aggregate = res.get('aggregates')
if not aggregate:
msg = _('Could not find aggregate for volume %s.')
raise exception.NetAppException(msg % volume_name)
aggregate_size = len(res.get('aggregates'))
if aggregate_size > 1:
aggregate = [aggr.get('name') for aggr in res.get('aggregates')]
if not aggregate:
msg = _('Could not find aggregate for volume %s.')
raise exception.NetAppException(msg % volume_name)
return aggregate
@na_utils.trace
@ -2193,7 +2248,7 @@ class NetAppRestClient(object):
fields = ['state', 'source.svm.name', 'source.path',
'destination.svm.name', 'destination.path',
'transfer.end_time', 'uuid', 'policy.type',
'transfer_schedule.name']
'transfer_schedule.name', 'transfer.state']
query = {}
query['fields'] = ','.join(fields)
@ -2222,7 +2277,11 @@ class NetAppRestClient(object):
snapmirrors = []
for record in response.get('records', []):
snapmirrors.append({
'relationship-status': record.get('state'),
'relationship-status': (
'idle'
if record.get('state') == 'snapmirrored'
else record.get('state')),
'transferring-state': record.get('transfer', {}).get('state'),
'mirror-state': record.get('state'),
'schedule': record['transfer_schedule']['name'],
'source-vserver': record['source']['svm']['name'],
@ -2255,7 +2314,8 @@ class NetAppRestClient(object):
def get_snapmirrors(self, source_path=None, dest_path=None,
source_vserver=None, dest_vserver=None,
source_volume=None, dest_volume=None,
desired_attributes=None):
desired_attributes=None, enable_tunneling=None,
list_destinations_only=None):
"""Gets one or more SnapMirror relationships.
Either the source or destination info may be omitted.
@ -2269,7 +2329,9 @@ class NetAppRestClient(object):
source_vserver=source_vserver,
source_volume=source_volume,
dest_vserver=dest_vserver,
dest_volume=dest_volume)
dest_volume=dest_volume,
enable_tunneling=enable_tunneling,
list_destinations_only=list_destinations_only)
return snapmirrors
@ -2752,7 +2814,7 @@ class NetAppRestClient(object):
clear_checkpoint=False):
"""Stops ongoing transfers for a SnapMirror relationship."""
snapmirror = self._get_snapmirrors(
snapmirror = self.get_snapmirrors(
source_path=source_path,
dest_path=dest_path,
source_vserver=source_vserver,
@ -2817,10 +2879,11 @@ class NetAppRestClient(object):
def get_snapmirror_destinations(self, source_path=None, dest_path=None,
source_vserver=None, source_volume=None,
dest_vserver=None, dest_volume=None,
desired_attributes=None):
desired_attributes=None,
enable_tunneling=None):
"""Gets one or more SnapMirror at source endpoint."""
snapmirrors = self._get_snapmirrors(
snapmirrors = self.get_snapmirrors(
source_path=source_path,
dest_path=dest_path,
source_vserver=source_vserver,
@ -2964,9 +3027,12 @@ class NetAppRestClient(object):
wait_result=True, schedule=None):
"""Change the snapmirror state between two volumes."""
snapmirror = self.get_snapmirrors(source_path, destination_path,
source_vserver, destination_vserver,
source_volume, destination_volume)
snapmirror = self.get_snapmirrors(source_path=source_path,
dest_path=destination_path,
source_vserver=source_vserver,
source_volume=source_volume,
dest_vserver=destination_vserver,
dest_volume=destination_volume)
if not snapmirror:
msg = _('Failed to get information about relationship between '
@ -3069,11 +3135,19 @@ class NetAppRestClient(object):
'clone.is_flexclone': 'true',
'svm.name': self.connection.get_vserver(),
}
if qos_policy_group is not None:
body['qos.policy.name'] = qos_policy_group
self.send_request('/storage/volumes', 'post', body=body)
# NOTE(nahimsouza): QoS policy can not be set during the cloning
# process, so we need to make a separate request.
if qos_policy_group is not None:
volume = self._get_volume_by_args(vol_name=volume_name)
uuid = volume['uuid']
body = {
'qos.policy.name': qos_policy_group,
}
self.send_request(f'/storage/volumes/{uuid}', 'patch', body=body)
if split:
self.split_volume_clone(volume_name)
@ -3096,7 +3170,7 @@ class NetAppRestClient(object):
source_volume=None, dest_volume=None):
"""Disables future transfers to a SnapMirror destination."""
snapmirror = self._get_snapmirrors(
snapmirror = self.get_snapmirrors(
source_path=source_path,
dest_path=dest_path,
source_vserver=source_vserver,
@ -3126,25 +3200,38 @@ class NetAppRestClient(object):
source_volume=None, dest_volume=None):
"""Breaks a data protection SnapMirror relationship."""
snapmirror = self._get_snapmirrors(
source_path=source_path,
dest_path=dest_path,
source_vserver=source_vserver,
source_volume=source_volume,
dest_vserver=dest_vserver,
dest_volume=dest_volume)
interval = 2
retries = (10 / interval)
if snapmirror:
uuid = snapmirror[0]['uuid']
body = {'state': 'broken_off'}
try:
@utils.retry(netapp_api.NaRetryableError, interval=interval,
retries=retries, backoff_rate=1)
def _waiter():
snapmirror = self.get_snapmirrors(
source_path=source_path,
dest_path=dest_path,
source_vserver=source_vserver,
source_volume=source_volume,
dest_vserver=dest_vserver,
dest_volume=dest_volume)
snapmirror_state = snapmirror[0].get('transferring-state')
if snapmirror_state == 'success':
uuid = snapmirror[0]['uuid']
body = {'state': 'broken_off'}
self.send_request(f'/snapmirror/relationships/{uuid}', 'patch',
body=body)
except netapp_api.api.NaApiError as e:
transfer_in_progress = 'Another transfer is in progress'
if not (e.code == netapp_api.EREST_BREAK_SNAPMIRROR_FAILED
and transfer_in_progress in e.message):
raise
return
else:
message = 'Waiting for transfer state to be SUCCESS.'
code = ''
raise netapp_api.NaRetryableError(message=message, code=code)
try:
return _waiter()
except netapp_api.NaRetryableError:
msg = _("Transfer state did not reach the expected state. Retries "
"exhausted. Aborting.")
raise na_utils.NetAppDriverException(msg)
@na_utils.trace
def resume_snapmirror_vol(self, source_vserver, source_volume,
@ -3169,9 +3256,12 @@ class NetAppRestClient(object):
source_vserver=None, dest_vserver=None,
source_volume=None, dest_volume=None):
"""Resume a SnapMirror relationship if it is quiesced."""
response = self._get_snapmirrors(source_path, dest_path,
source_vserver, source_volume,
dest_vserver, dest_volume)
response = self.get_snapmirrors(source_path=source_path,
dest_path=dest_path,
source_vserver=source_vserver,
dest_vserver=dest_vserver,
source_volume=source_volume,
dest_volume=dest_volume)
if not response:
# NOTE(nahimsouza): As ZAPI returns this error code, it was kept
@ -3247,9 +3337,12 @@ class NetAppRestClient(object):
source_vserver=None, dest_vserver=None,
source_volume=None, dest_volume=None):
"""Update a snapmirror relationship asynchronously."""
snapmirrors = self._get_snapmirrors(source_path, dest_path,
source_vserver, source_volume,
dest_vserver, dest_volume)
snapmirrors = self.get_snapmirrors(source_path=source_path,
dest_path=dest_path,
source_vserver=source_vserver,
dest_vserver=dest_vserver,
source_volume=source_volume,
dest_volume=dest_volume)
if not snapmirrors:
msg = _('Failed to get snapmirror relationship information')
@ -3578,13 +3671,13 @@ class NetAppRestClient(object):
# response is empty. Also, REST API does not have an equivalent to
# 'udp-max-xfer-size', so the default is always returned.
nfs_info = {
'tcp-max-xfer-size': DEFAULT_TCP_MAX_XFER_SIZE,
'udp-max-xfer-size': DEFAULT_UDP_MAX_XFER_SIZE,
'tcp-max-xfer-size': str(DEFAULT_TCP_MAX_XFER_SIZE),
'udp-max-xfer-size': str(DEFAULT_UDP_MAX_XFER_SIZE),
}
records = response.get('records', [])
if records:
nfs_info['tcp-max-xfer-size'] = (
records[0]['transport']['tcp_max_transfer_size'])
str(records[0]['transport']['tcp_max_transfer_size']))
return nfs_info
@ -4495,26 +4588,27 @@ class NetAppRestClient(object):
@na_utils.trace
def _configure_nfs(self, nfs_config, svm_id):
"""Sets the nfs configuraton"""
if ('udp-max-xfer-size' in nfs_config and
(nfs_config['udp-max-xfer-size']
!= str(DEFAULT_UDP_MAX_XFER_SIZE))):
msg = _('Failed to configure NFS. REST API does not support '
'setting udp-max-xfer-size default value %(default)s '
'is not equal to actual value %(actual)s')
msg_args = {
'default': DEFAULT_UDP_MAX_XFER_SIZE,
'actual': nfs_config['udp-max-xfer-size'],
}
raise exception.NetAppException(msg % msg_args)
nfs_config_value = int(nfs_config['tcp-max-xfer-size'])
body = {
'transport.tcp_max_transfer_size': nfs_config['tcp-max-xfer-size']
'transport.tcp_max_transfer_size': nfs_config_value
}
self.send_request(f'/protocols/nfs/services/{svm_id}', 'patch',
body=body)
@na_utils.trace
def _get_svm_uuid(self):
# Get SVM UUID.
query = {
'name': self.vserver,
'fields': 'uuid'
}
res = self.send_request('/svm/svms', 'get', query=query)
if not res.get('records'):
msg = _('Vserver %s not found.') % self.vserver
raise exception.NetAppException(msg)
svm_id = res.get('records')[0]['uuid']
return svm_id
@na_utils.trace
def create_network_interface(self, ip, netmask, node, port,
vserver_name, lif_name):
@ -4665,8 +4759,8 @@ class NetAppRestClient(object):
query['svm.name'] = vserver
nfs_info = {
'tcp-max-xfer-size': DEFAULT_TCP_MAX_XFER_SIZE,
'udp-max-xfer-size': DEFAULT_UDP_MAX_XFER_SIZE,
'tcp-max-xfer-size': str(DEFAULT_TCP_MAX_XFER_SIZE),
'udp-max-xfer-size': str(DEFAULT_UDP_MAX_XFER_SIZE)
}
response = self.send_request('/protocols/nfs/services/',
@ -4675,7 +4769,7 @@ class NetAppRestClient(object):
if records:
nfs_info['tcp-max-xfer-size'] = (
records[0]['transport']['tcp_max_transfer_size'])
str(records[0]['transport']['tcp_max_transfer_size']))
return nfs_info
@ -4878,8 +4972,8 @@ class NetAppRestClient(object):
if e.code == netapp_api.EREST_ENTRY_NOT_FOUND:
LOG.debug('VLAN %(vlan)s on port %(port)s node %(node)s '
'was not found')
if (e.code == netapp_api.EREST_INTERFACE_BOUND or
e.code == netapp_api.EREST_PORT_IN_USE):
elif (e.code == netapp_api.EREST_INTERFACE_BOUND or
e.code == netapp_api.EREST_PORT_IN_USE):
LOG.debug('VLAN %(vlan)s on port %(port)s node %(node)s '
'still used by LIF and cannot be deleted.',
{'vlan': vlan, 'port': port, 'node': node})

View File

@ -53,6 +53,8 @@ EREST_INTERFACE_BOUND = '1376858'
EREST_PORT_IN_USE = '1966189'
EREST_NFS_V4_0_ENABLED_MIGRATION_FAILURE = '13172940'
EREST_VSERVER_MIGRATION_TO_NON_AFF_CLUSTER = '13172984'
EREST_UNMOUNT_FAILED_LOCK = '917536'
EREST_CANNOT_MODITY_OFFLINE_VOLUME = '917533'
class NaRetryableError(api.NaApiError):

View File

@ -2634,9 +2634,11 @@ class NetAppCmodeFileStorageLibrary(object):
# NOTE(dviroel): Don't try to resume or resync a SnapMirror that has
# one of the in progress transfer states, because the storage will
# answer with an error.
in_progress_status = ['preparing', 'transferring', 'finalizing']
in_progress_status = ['preparing', 'transferring', 'finalizing',
'synchronizing']
if (snapmirror.get('mirror-state') != 'snapmirrored' and
snapmirror.get('relationship-status') in in_progress_status):
(snapmirror.get('relationship-status') in in_progress_status or
snapmirror.get('transferring-state') in in_progress_status)):
return constants.REPLICA_STATE_OUT_OF_SYNC
if snapmirror.get('mirror-state') != 'snapmirrored':

View File

@ -85,7 +85,9 @@ DELETED_EXPORT_POLICIES = {
}
QOS_POLICY_GROUP_NAME = 'fake_qos_policy_group_name'
QOS_MAX_THROUGHPUT = '5000B/s'
QOS_MAX_THROUGHPUT_IOPS = '5000iops'
QOS_MAX_THROUGHPUT_NO_UNIT = 5000
QOS_MAX_THROUGHPUT_IOPS_NO_UNIT = 5000
ADAPTIVE_QOS_POLICY_GROUP_NAME = 'fake_adaptive_qos_policy_group_name'
VSERVER_TYPE_DEFAULT = 'default'
VSERVER_TYPE_DP_DEST = 'dp_destination'
@ -1028,15 +1030,36 @@ NFS_LIFS = [
]
NFS_LIFS_REST = [
{'uuid': FAKE_UUID,
'address': IP_ADDRESS,
'home-node': NODE_NAME,
'home-port': VLAN_PORT,
'interface-name': LIF_NAME,
'netmask': NETMASK,
'role': 'data',
'vserver': VSERVER_NAME,
},
{
'uuid': 'fake_uuid_1',
'address': IP_ADDRESS,
'home-node': NODE_NAME,
'home-port': VLAN_PORT,
'interface-name': LIF_NAME,
'netmask': NETMASK,
'role': 'data',
'vserver': VSERVER_NAME,
},
{
'uuid': 'fake_uuid_2',
'address': IP_ADDRESS,
'home-node': NODE_NAME,
'home-port': VLAN_PORT,
'interface-name': LIF_NAME,
'netmask': NETMASK,
'role': 'data',
'vserver': VSERVER_NAME,
},
{
'uuid': 'fake_uuid_3',
'address': IP_ADDRESS,
'home-node': NODE_NAME,
'home-port': VLAN_PORT,
'interface-name': LIF_NAME,
'netmask': NETMASK,
'role': 'data',
'vserver': VSERVER_NAME,
},
]
NET_INTERFACE_GET_ONE_RESPONSE = etree.XML("""
@ -3690,7 +3713,8 @@ GENERIC_NETWORK_INTERFACES_GET_REPONSE = {
}
}
}
]
],
"num_records": 1,
}
GENERIC_EXPORT_POLICY_RESPONSE_AND_VOLUMES = {
@ -3940,6 +3964,9 @@ SNAPMIRROR_GET_ITER_RESPONSE_REST = {
"transfer_schedule": {
"name": "hourly",
},
"transfer": {
"state": "success"
}
}
],
"num_records": 1,
@ -3950,12 +3977,13 @@ REST_GET_SNAPMIRRORS_RESPONSE = [{
'destination-vserver': SM_DEST_VSERVER,
'last-transfer-end-timestamp': 0,
'mirror-state': 'snapmirrored',
'relationship-status': 'snapmirrored',
'relationship-status': 'idle',
'source-volume': SM_SOURCE_VOLUME,
'source-vserver': SM_SOURCE_VSERVER,
'uuid': FAKE_UUID,
'policy-type': 'async',
'schedule': 'hourly'
'schedule': 'hourly',
'transferring-state': 'success'
}]
FAKE_CIFS_RECORDS = {
@ -4622,7 +4650,8 @@ FAKE_AGGREGATES_RESPONSE = {
{
"name": SHARE_AGGREGATE_NAME
}
]
],
"name": VSERVER_NAME,
}
]
}
@ -4673,3 +4702,65 @@ REST_MGMT_INTERFACES = {
],
"num_records": 2,
}
FAKE_CIFS_LOCAL_USER = {
'records': [
{
'sid': 'S-1-5-21-256008430-3394229847-3930036330-1001'
}
]
}
FAKE_SERVER_SWITCH_NAME = 'fake_ss_name'
FAKE_SUBTYPE = 'fake_subtype'
FAKE_DNS_CONFIG = {
'dns-state': 'true',
'domains': ['fake_domain'],
'dns-ips': ['fake_ip']
}
FAKE_VOLUME_MANAGE = {
'records': [
{
'name': VOLUME_NAMES[0],
'aggregates': [
{
'name': SHARE_AGGREGATE_NAME
}
],
'nas': {
'path': VOLUME_JUNCTION_PATH
},
'style': 'flex',
'type': 'fake_type',
'svm': {
'name': VSERVER_NAME
},
'qos': {
'policy': {
'name': QOS_POLICY_GROUP_NAME
}
},
'space': {
'size': SHARE_SIZE
}
}
],
'num_records': 1
}
FAKE_PORTS = [
{'speed': ''},
{'speed': '4'},
{'speed': 'auto'},
{'speed': 'undef'},
{'speed': 'fake_speed'}
]
FAKE_ROOT_AGGREGATES_RESPONSE = {
"records": [
{
"aggregate": SHARE_AGGREGATE_NAME
}
]
}

View File

@ -3179,6 +3179,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
'volume-type': 'rw',
'junction-path': '/%s' % fake.SHARE_NAME,
'space-reserve': ('none' if thin_provisioned else 'volume'),
'encrypt': 'false'
}
self.client.send_request.assert_called_once_with('volume-create',
@ -3291,6 +3292,7 @@ class NetAppClientCmodeTestCase(test.TestCase):
expected_api_args = {
'volume-type': volume_type,
'space-reserve': 'volume',
'encrypt': 'false'
}
self.assertEqual(expected_api_args, result_api_args)