1053 lines
40 KiB
Python
1053 lines
40 KiB
Python
# Copyright (c) 2021 Zadara Storage, Inc.
|
|
# 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.
|
|
"""
|
|
Tests for Zadara VPSA Share driver
|
|
"""
|
|
|
|
import copy
|
|
import requests
|
|
|
|
from unittest import mock
|
|
from urllib import parse
|
|
|
|
from manila import context
|
|
from manila import exception as manila_exception
|
|
from manila.share import configuration
|
|
from manila.share.drivers.zadara import zadara
|
|
from manila import test
|
|
from manila.tests import fake_share
|
|
|
|
|
|
def check_access_key(func):
|
|
"""A decorator for all operations that needed an API before executing"""
|
|
def wrap(self, *args, **kwargs):
|
|
if not self._is_correct_access_key():
|
|
return RUNTIME_VARS['bad_login']
|
|
return func(self, *args, **kwargs)
|
|
|
|
return wrap
|
|
|
|
|
|
DEFAULT_RUNTIME_VARS = {
|
|
'status': 200,
|
|
'user': 'test',
|
|
'password': 'test_password',
|
|
'access_key': '0123456789ABCDEF',
|
|
'volumes': [],
|
|
'servers': [],
|
|
'controllers': [('active_ctrl', {'display-name': 'test_ctrl'})],
|
|
'counter': 1000,
|
|
|
|
"login": """
|
|
{
|
|
"response": {
|
|
"user": {
|
|
"updated-at": "2021-01-22",
|
|
"access-key": "%s",
|
|
"id": 1,
|
|
"created-at": "2021-01-22",
|
|
"email": "jsmith@example.com",
|
|
"username": "jsmith"
|
|
},
|
|
"status": 0
|
|
}
|
|
}""",
|
|
"good": """
|
|
{
|
|
"response": {
|
|
"status": 0
|
|
}
|
|
}""",
|
|
"good_snapshot": """
|
|
{
|
|
"response": {
|
|
"snapshot_name": "fakesnaplocation",
|
|
"status": 0
|
|
}
|
|
}""",
|
|
"bad_login": """
|
|
{
|
|
"response": {
|
|
"status": 5,
|
|
"status-msg": "Some message..."
|
|
}
|
|
}""",
|
|
"bad_volume": """
|
|
{
|
|
"response": {
|
|
"status": 10081,
|
|
"status-msg": "Virtual volume xxx should be found"
|
|
}
|
|
}""",
|
|
"fake_volume": """
|
|
{
|
|
"response": {
|
|
"volumes": [],
|
|
"status": 0,
|
|
"status-msg": "Virtual volume xxx doesn't exist"
|
|
}
|
|
}""",
|
|
"bad_server": """
|
|
{
|
|
"response": {
|
|
"status": 10086,
|
|
"status-msg": "Server xxx not found"
|
|
}
|
|
}""",
|
|
"server_created": """
|
|
{
|
|
"response": {
|
|
"server_name": "%s",
|
|
"status": 0
|
|
}
|
|
}""",
|
|
}
|
|
|
|
RUNTIME_VARS = None
|
|
|
|
|
|
class FakeResponse(object):
|
|
def __init__(self, method, url, params, body, headers, **kwargs):
|
|
# kwargs include: verify, timeout
|
|
self.method = method
|
|
self.url = url
|
|
self.body = body
|
|
self.params = params
|
|
self.headers = headers
|
|
self.status = RUNTIME_VARS['status']
|
|
|
|
@property
|
|
def access_key(self):
|
|
"""Returns Response Access Key"""
|
|
return self.headers["X-Access-Key"]
|
|
|
|
def read(self):
|
|
ops = {'POST': [('/api/users/login.json', self._login),
|
|
('/api/volumes.json', self._create_volume),
|
|
('/api/servers.json', self._create_server),
|
|
('/api/servers/*/volumes.json', self._attach),
|
|
('/api/volumes/*/rename.json', self._rename),
|
|
('/api/volumes/*/detach.json', self._detach),
|
|
('/api/volumes/*/expand.json', self._expand),
|
|
('/api/consistency_groups/*/snapshots.json',
|
|
self._create_snapshot),
|
|
('/api/snapshots/*/rename.json',
|
|
self._rename_snapshot),
|
|
('/api/consistency_groups/*/clone.json',
|
|
self._create_clone_from_snapshot),
|
|
('/api/consistency_groups/*/clone.json',
|
|
self._create_clone)],
|
|
'DELETE': [('/api/volumes/*', self._delete),
|
|
('/api/snapshots/*', self._delete_snapshot)],
|
|
'GET': [('/api/volumes.json?showonlyfile=YES',
|
|
self._list_volumes),
|
|
('/api/volumes.json?display_name=*',
|
|
self._get_volume_by_name),
|
|
('/api/pools/*.json', self._get_pool),
|
|
('/api/vcontrollers.json', self._list_controllers),
|
|
('/api/servers.json', self._list_servers),
|
|
('/api/consistency_groups/*/snapshots.json',
|
|
self._list_vol_snapshots),
|
|
('/api/volumes/*/servers.json',
|
|
self._list_vol_attachments)]
|
|
}
|
|
|
|
ops_list = ops[self.method]
|
|
for (templ_url, func) in ops_list:
|
|
if self._compare_url(self.url, templ_url):
|
|
result = func()
|
|
return result
|
|
|
|
@staticmethod
|
|
def _compare_url(url, template_url):
|
|
items = url.split('/')
|
|
titems = template_url.split('/')
|
|
for (i, titem) in enumerate(titems):
|
|
if '*' not in titem and titem != items[i]:
|
|
return False
|
|
if '?' in titem and titem.split('=')[0] != items[i].split('=')[0]:
|
|
return False
|
|
|
|
return True
|
|
|
|
@staticmethod
|
|
def _get_counter():
|
|
cnt = RUNTIME_VARS['counter']
|
|
RUNTIME_VARS['counter'] += 1
|
|
return cnt
|
|
|
|
def _login(self):
|
|
params = self.body
|
|
if (params['user'] == RUNTIME_VARS['user'] and
|
|
params['password'] == RUNTIME_VARS['password']):
|
|
return RUNTIME_VARS['login'] % RUNTIME_VARS['access_key']
|
|
else:
|
|
return RUNTIME_VARS['bad_login']
|
|
|
|
def _is_correct_access_key(self):
|
|
return self.access_key == RUNTIME_VARS['access_key']
|
|
|
|
@check_access_key
|
|
def _create_volume(self):
|
|
params = self.body
|
|
params['display-name'] = params['name']
|
|
params['cg-name'] = params['name']
|
|
params['snapshots'] = []
|
|
params['server_ext_names'] = ''
|
|
params['provisioned-capacity'] = 1
|
|
vpsa_vol = 'volume-%07d' % self._get_counter()
|
|
params['nfs-export-path'] = '10.2.1.56:/export/%s' % vpsa_vol
|
|
RUNTIME_VARS['volumes'].append((vpsa_vol, params))
|
|
return RUNTIME_VARS['good']
|
|
|
|
@check_access_key
|
|
def _create_server(self):
|
|
params = self.body
|
|
|
|
params['display-name'] = params['display_name']
|
|
vpsa_srv = 'srv-%07d' % self._get_counter()
|
|
RUNTIME_VARS['servers'].append((vpsa_srv, params))
|
|
return RUNTIME_VARS['server_created'] % vpsa_srv
|
|
|
|
@check_access_key
|
|
def _attach(self):
|
|
srv = self.url.split('/')[3]
|
|
|
|
params = self.body
|
|
|
|
vol = params['volume_name[]']
|
|
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
if params['name'] == vol:
|
|
attachments = params['server_ext_names'].split(',')
|
|
if srv in attachments:
|
|
# already attached - ok
|
|
return RUNTIME_VARS['good']
|
|
else:
|
|
if not attachments[0]:
|
|
params['server_ext_names'] = srv
|
|
else:
|
|
params['server_ext_names'] += ',' + srv
|
|
return RUNTIME_VARS['good']
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
@check_access_key
|
|
def _detach(self):
|
|
params = self.body
|
|
vol = self.url.split('/')[3]
|
|
srv = params['server_name[]']
|
|
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
if params['name'] == vol:
|
|
attachments = params['server_ext_names'].split(',')
|
|
if srv not in attachments:
|
|
return RUNTIME_VARS['bad_server']
|
|
else:
|
|
attachments.remove(srv)
|
|
params['server_ext_names'] = (','.join([str(elem)
|
|
for elem in attachments]))
|
|
return RUNTIME_VARS['good']
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
@check_access_key
|
|
def _expand(self):
|
|
params = self.body
|
|
vol = self.url.split('/')[3]
|
|
capacity = params['capacity']
|
|
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
if params['name'] == vol:
|
|
params['capacity'] = capacity
|
|
return RUNTIME_VARS['good']
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
@check_access_key
|
|
def _rename(self):
|
|
params = self.body
|
|
vol = self.url.split('/')[3]
|
|
|
|
for (vol_name, vol_params) in RUNTIME_VARS['volumes']:
|
|
if vol_params['name'] == vol:
|
|
vol_params['name'] = params['new_name']
|
|
vol_params['display-name'] = params['new_name']
|
|
vol_params['cg-name'] = params['new_name']
|
|
return RUNTIME_VARS['good']
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
@check_access_key
|
|
def _rename_snapshot(self):
|
|
params = self.body
|
|
vpsa_snapshot = self.url.split('/')[3]
|
|
|
|
for (vol_name, vol_params) in RUNTIME_VARS['volumes']:
|
|
for snapshot in vol_params['snapshots']:
|
|
if vpsa_snapshot == snapshot['provider-location']:
|
|
snapshot['name'] = params['newname']
|
|
snapshot['display-name'] = params['newname']
|
|
return RUNTIME_VARS['good']
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
@check_access_key
|
|
def _create_snapshot(self):
|
|
params = self.body
|
|
cg_name = self.url.split('/')[3]
|
|
snap_name = params['display_name']
|
|
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
if params['cg-name'] == cg_name:
|
|
snapshots = params['snapshots']
|
|
if snap_name in snapshots:
|
|
# already attached
|
|
return RUNTIME_VARS['bad_volume']
|
|
else:
|
|
snapshots.append(snap_name)
|
|
return RUNTIME_VARS['good_snapshot']
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
@check_access_key
|
|
def _delete_snapshot(self):
|
|
snap = self.url.split('/')[3].split('.')[0]
|
|
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
if snap in params['snapshots']:
|
|
params['snapshots'].remove(snap)
|
|
return RUNTIME_VARS['good']
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
@check_access_key
|
|
def _create_clone_from_snapshot(self):
|
|
params = self.body
|
|
params['display-name'] = params['name']
|
|
params['cg-name'] = params['name']
|
|
params['capacity'] = 1
|
|
params['snapshots'] = []
|
|
params['server_ext_names'] = ''
|
|
params['pool'] = 'pool-0001'
|
|
params['provisioned-capacity'] = 1
|
|
vpsa_vol = 'volume-%07d' % self._get_counter()
|
|
params['nfs-export-path'] = '10.2.1.56:/export/%s' % vpsa_vol
|
|
RUNTIME_VARS['volumes'].append((vpsa_vol, params))
|
|
return RUNTIME_VARS['good']
|
|
|
|
@check_access_key
|
|
def _create_clone(self):
|
|
params = self.body
|
|
params['display-name'] = params['name']
|
|
params['cg-name'] = params['name']
|
|
params['capacity'] = 1
|
|
params['snapshots'] = []
|
|
params['server_ext_names'] = ''
|
|
vpsa_vol = 'volume-%07d' % self._get_counter()
|
|
RUNTIME_VARS['volumes'].append((vpsa_vol, params))
|
|
return RUNTIME_VARS['good']
|
|
|
|
def _delete(self):
|
|
vol = self.url.split('/')[3].split('.')[0]
|
|
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
if params['name'] == vol:
|
|
if params['server_ext_names']:
|
|
# there are attachments - should be volume busy error
|
|
return RUNTIME_VARS['bad_volume']
|
|
else:
|
|
RUNTIME_VARS['volumes'].remove((vol_name, params))
|
|
return RUNTIME_VARS['good']
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
def _generate_list_resp(self, null_body, body, lst, vol):
|
|
resp = ''
|
|
for (obj, params) in lst:
|
|
if vol:
|
|
resp += body % (params['name'],
|
|
params['display-name'],
|
|
params['cg-name'],
|
|
params['capacity'],
|
|
params['pool'],
|
|
params['provisioned-capacity'],
|
|
params['nfs-export-path'])
|
|
else:
|
|
resp += body % (obj, params['display-name'])
|
|
if resp:
|
|
return resp
|
|
else:
|
|
return null_body
|
|
|
|
def _list_volumes(self):
|
|
null_body = """
|
|
{
|
|
"response": {
|
|
"volumes": [
|
|
],
|
|
"status": 0
|
|
}
|
|
}"""
|
|
body = """
|
|
{
|
|
"response": {
|
|
"volumes": %s,
|
|
"status": 0
|
|
}
|
|
}"""
|
|
|
|
volume_obj = """
|
|
{
|
|
"name": "%s",
|
|
"display_name": "%s",
|
|
"cg_name": "%s",
|
|
"status": "Available",
|
|
"virtual_capacity": %d,
|
|
"pool_name": "%s",
|
|
"allocated-capacity": 1,
|
|
"provisioned_capacity": "%s",
|
|
"raid-group-name": "r5",
|
|
"cache": "write-through",
|
|
"created-at": "2021-01-22",
|
|
"modified-at": "2021-01-22",
|
|
"nfs_export_path": "%s"
|
|
}
|
|
"""
|
|
if len(RUNTIME_VARS['volumes']) == 0:
|
|
return null_body
|
|
resp = ''
|
|
volume_list = ''
|
|
count = 0
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
volume_dict = volume_obj % (params['name'],
|
|
params['display-name'],
|
|
params['cg-name'],
|
|
params['capacity'],
|
|
params['pool'],
|
|
params['provisioned-capacity'],
|
|
params['nfs-export-path'])
|
|
if count == 0:
|
|
volume_list += volume_dict
|
|
count += 1
|
|
elif count != len(RUNTIME_VARS['volumes']):
|
|
volume_list = volume_list + ',' + volume_dict
|
|
count += 1
|
|
if volume_list:
|
|
volume_list = '[' + volume_list + ']'
|
|
resp = body % volume_list
|
|
return resp
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
def _get_volume_by_name(self):
|
|
volume_name = self.url.split('=')[1]
|
|
body = """
|
|
{
|
|
"response": {
|
|
"volumes": [
|
|
{
|
|
"name": "%s",
|
|
"display_name": "%s",
|
|
"cg_name": "%s",
|
|
"status": "Available",
|
|
"virtual_capacity": %d,
|
|
"pool_name": "%s",
|
|
"allocated-capacity": 1,
|
|
"provisioned_capacity": %d,
|
|
"raid-group-name": "r5",
|
|
"cache": "write-through",
|
|
"created-at": "2021-01-22",
|
|
"modified-at": "2021-01-22",
|
|
"nfs_export_path": "%s",
|
|
"server_ext_names": "%s"
|
|
}
|
|
],
|
|
"status": 0
|
|
}
|
|
}"""
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
if params['name'] == volume_name:
|
|
resp = body % (volume_name, params['display-name'],
|
|
params['cg-name'], params['capacity'],
|
|
params['pool'], params['provisioned-capacity'],
|
|
params['nfs-export-path'],
|
|
params['server_ext_names'])
|
|
return resp
|
|
|
|
return RUNTIME_VARS['fake_volume']
|
|
|
|
def _list_controllers(self):
|
|
null_body = """
|
|
{
|
|
"response": {
|
|
"vcontrollers": [
|
|
],
|
|
"status": 0
|
|
}
|
|
}"""
|
|
body = """
|
|
{
|
|
"response": {
|
|
"vcontrollers": [
|
|
{
|
|
"name": "%s",
|
|
"display_name": "%s",
|
|
"state": "active",
|
|
"target":
|
|
"iqn.2011-04.zadarastorage:vsa-xxx:1",
|
|
"iscsi_ip": "1.1.1.1",
|
|
"iscsi_ipv6": "",
|
|
"mgmt-ip": "1.1.1.1",
|
|
"software-ver": "0.0.09-05.1--77.7",
|
|
"heartbeat1": "ok",
|
|
"heartbeat2": "ok",
|
|
"vpsa_chap_user": "test_chap_user",
|
|
"vpsa_chap_secret": "test_chap_secret"
|
|
}
|
|
],
|
|
"status": 0
|
|
}
|
|
}"""
|
|
return self._generate_list_resp(null_body,
|
|
body,
|
|
RUNTIME_VARS['controllers'],
|
|
False)
|
|
|
|
def _get_pool(self):
|
|
response = """
|
|
{
|
|
"response": {
|
|
"pool": {
|
|
"name": "pool-0001",
|
|
"capacity": 100,
|
|
"available_capacity": 99,
|
|
"provisioned_capacity": 1
|
|
},
|
|
"status": 0
|
|
}
|
|
}"""
|
|
return response
|
|
|
|
def _list_servers(self):
|
|
null_body = """
|
|
{
|
|
"response": {
|
|
"servers": [
|
|
],
|
|
"status": 0
|
|
}
|
|
}"""
|
|
body = """
|
|
{
|
|
"response": {
|
|
"servers": %s,
|
|
"status": 0
|
|
}
|
|
}"""
|
|
|
|
server_obj = """
|
|
{
|
|
"name": "%s",
|
|
"display_name": "%s",
|
|
"iscsi_ip": "%s",
|
|
"status": "Active",
|
|
"created-at": "2021-01-22",
|
|
"modified-at": "2021-01-22"
|
|
}
|
|
"""
|
|
resp = ''
|
|
server_list = ''
|
|
count = 0
|
|
for (obj, params) in RUNTIME_VARS['servers']:
|
|
server_dict = server_obj % (obj,
|
|
params['display-name'],
|
|
params['iqn'])
|
|
if count == 0:
|
|
server_list += server_dict
|
|
count += 1
|
|
elif count != len(RUNTIME_VARS['servers']):
|
|
server_list = server_list + ',' + server_dict
|
|
count += 1
|
|
server_list = '[' + server_list + ']'
|
|
resp = body % server_list
|
|
if resp:
|
|
return resp
|
|
else:
|
|
return null_body
|
|
|
|
def _get_server_obj(self, name):
|
|
for (srv_name, params) in RUNTIME_VARS['servers']:
|
|
if srv_name == name:
|
|
return params
|
|
|
|
def _list_vol_attachments(self):
|
|
vol = self.url.split('/')[3]
|
|
null_body = """
|
|
{
|
|
"response": {
|
|
"servers": [
|
|
],
|
|
"status": 0
|
|
}
|
|
}"""
|
|
body = """
|
|
{
|
|
"response": {
|
|
"servers": %s,
|
|
"status": 0
|
|
}
|
|
}"""
|
|
|
|
server_obj = """
|
|
{
|
|
"name": "%s",
|
|
"display_name": "%s",
|
|
"iscsi_ip": "%s",
|
|
"target":
|
|
"iqn.2011-04.zadarastorage:vsa-xxx:1",
|
|
"lun": 0
|
|
}
|
|
"""
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
if params['name'] == vol:
|
|
attachments = params['server_ext_names'].split(',')
|
|
if not attachments[0]:
|
|
return null_body
|
|
resp = ''
|
|
server_list = ''
|
|
count = 0
|
|
for server in attachments:
|
|
srv_params = self._get_server_obj(server)
|
|
server_dict = (server_obj % (server,
|
|
srv_params['display_name'],
|
|
srv_params['iscsi']))
|
|
if count == 0:
|
|
server_list += server_dict
|
|
count += 1
|
|
elif count != len(attachments):
|
|
server_list = server_list + ',' + server_dict
|
|
count += 1
|
|
server_list = '[' + server_list + ']'
|
|
resp = body % server_list
|
|
return resp
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
def _list_vol_snapshots(self):
|
|
cg_name = self.url.split('/')[3]
|
|
|
|
null_body = """
|
|
{
|
|
"response": {
|
|
"snapshots": [
|
|
],
|
|
"status": 0
|
|
}
|
|
}"""
|
|
|
|
body = """
|
|
{
|
|
"response": {
|
|
"snapshots": %s,
|
|
"status": 0
|
|
}
|
|
}"""
|
|
|
|
snapshot_obj = """
|
|
{
|
|
"name": "%s",
|
|
"display_name": "%s",
|
|
"status": "normal",
|
|
"cg-name": "%s",
|
|
"pool-name": "pool-00000001"
|
|
}
|
|
"""
|
|
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
|
if params['cg-name'] == cg_name:
|
|
snapshots = params['snapshots']
|
|
if len(snapshots) == 0:
|
|
return null_body
|
|
resp = ''
|
|
snapshot_list = ''
|
|
count = 0
|
|
|
|
for snapshot in snapshots:
|
|
snapshot_dict = snapshot_obj % (snapshot, snapshot,
|
|
cg_name)
|
|
if count == 0:
|
|
snapshot_list += snapshot_dict
|
|
count += 1
|
|
elif count != len(snapshots):
|
|
snapshot_list = snapshot_list + ',' + snapshot_dict
|
|
count += 1
|
|
snapshot_list = '[' + snapshot_list + ']'
|
|
resp = body % snapshot_list
|
|
return resp
|
|
|
|
return RUNTIME_VARS['bad_volume']
|
|
|
|
|
|
class FakeRequests(object):
|
|
"""A fake requests for zadara volume driver tests."""
|
|
def __init__(self, method, api_url, params=None, data=None,
|
|
headers=None, **kwargs):
|
|
apiurl_items = parse.urlparse(api_url)
|
|
if apiurl_items.query:
|
|
url = apiurl_items.path + '?' + apiurl_items.query
|
|
else:
|
|
url = apiurl_items.path
|
|
res = FakeResponse(method, url, params, data, headers, **kwargs)
|
|
self.content = res.read()
|
|
self.status_code = res.status
|
|
|
|
|
|
class ZadaraVPSAShareDriverTestCase(test.TestCase):
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def setUp(self):
|
|
super(ZadaraVPSAShareDriverTestCase, self).setUp()
|
|
|
|
def _safe_get(opt):
|
|
return getattr(self.configuration, opt)
|
|
|
|
self._context = context.get_admin_context()
|
|
self.configuration = mock.Mock(spec=configuration.Configuration)
|
|
self.configuration.safe_get = mock.Mock(side_effect=_safe_get)
|
|
|
|
global RUNTIME_VARS
|
|
RUNTIME_VARS = copy.deepcopy(DEFAULT_RUNTIME_VARS)
|
|
|
|
self.configuration.driver_handles_share_servers = False
|
|
self.configuration.network_config_group = (
|
|
'fake_network_config_group')
|
|
self.configuration.admin_network_config_group = (
|
|
'fake_admin_network_config_group')
|
|
self.configuration.reserved_percentage = 0
|
|
self.configuration.zadara_use_iser = True
|
|
self.configuration.zadara_vpsa_host = '192.168.5.5'
|
|
self.configuration.zadara_vpsa_port = '80'
|
|
self.configuration.zadara_user = 'test'
|
|
self.configuration.zadara_password = 'test_password'
|
|
self.configuration.zadara_access_key = '0123456789ABCDEF'
|
|
self.configuration.zadara_vpsa_poolname = 'pool-0001'
|
|
self.configuration.zadara_vol_encrypt = False
|
|
self.configuration.zadara_share_name_template = 'OS_share-%s'
|
|
self.configuration.zadara_share_snap_name_template = (
|
|
'OS_share-snapshot-%s')
|
|
self.configuration.zadara_vpsa_use_ssl = False
|
|
self.configuration.zadara_ssl_cert_verify = False
|
|
self.configuration.zadara_default_snap_policy = False
|
|
self.configuration.zadara_driver_ssl_cert_path = None
|
|
self.configuration.zadara_gen3_vol_compress = True
|
|
self.configuration.zadara_gen3_vol_dedupe = True
|
|
self.configuration.share_backend_name = 'zadaravpsa'
|
|
self.configuration.reserved_share_percentage = '0'
|
|
self.configuration.replication_domain = None
|
|
self.configuration.filter_function = None
|
|
self.configuration.goodness_function = None
|
|
self.configuration.goodness_function = None
|
|
self.driver = (zadara.ZadaraVPSAShareDriver(
|
|
configuration=self.configuration))
|
|
self.driver.do_setup(None)
|
|
self.driver.api.get_share_metadata = mock.Mock(return_value={})
|
|
self.driver._get_share_export_location = mock.Mock()
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_do_setup(self):
|
|
self.driver.do_setup(self._context)
|
|
self.assertIsNotNone(self.driver.vpsa)
|
|
self.assertEqual(self.driver.vpsa.access_key,
|
|
self.configuration.zadara_access_key)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_no_active_ctrl(self):
|
|
share = fake_share.fake_share(id='fakeid', share_proto='NFS',
|
|
share_id='fakeshareid')
|
|
self.driver.create_share(self._context, share)
|
|
access = fake_share.fake_access()
|
|
|
|
RUNTIME_VARS['controllers'] = []
|
|
self.assertRaises(manila_exception.ZadaraVPSANoActiveController,
|
|
self.driver._allow_access,
|
|
self._context,
|
|
share, access)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_create_share_unsupported_proto(self):
|
|
share = fake_share.fake_share(share_proto='INVALID')
|
|
self.assertRaises(manila_exception.ZadaraInvalidProtocol,
|
|
self.driver.create_share,
|
|
self._context,
|
|
share)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_create_delete_share(self):
|
|
"""Create share."""
|
|
share = fake_share.fake_share(share_proto='NFS',
|
|
share_id='fakeshareid')
|
|
self.driver.create_share(self._context, share)
|
|
self.driver.delete_share(self._context, share)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_create_delete_multiple_shares(self):
|
|
"""Create/Delete multiple shares."""
|
|
share1 = fake_share.fake_share(id='fakeid1', share_proto='NFS',
|
|
share_id='fakeshareid1')
|
|
self.driver.create_share(self._context, share1)
|
|
|
|
share2 = fake_share.fake_share(id='fakeid2', share_proto='CIFS',
|
|
share_id='fakeshareid2')
|
|
self.driver.create_share(self._context, share2)
|
|
|
|
self.driver.delete_share(self._context, share1)
|
|
self.driver.delete_share(self._context, share2)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_delete_non_existent(self):
|
|
"""Delete non-existent share."""
|
|
share = fake_share.fake_share(share_proto='NFS',
|
|
share_id='fakeshareid')
|
|
self.driver.delete_share(self._context, share)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_create_delete_share_snapshot(self):
|
|
"""Create/Delete share snapshot."""
|
|
share1 = fake_share.fake_share(id='fakeid1', share_proto='NFS',
|
|
share_id='fakeshareid1')
|
|
self.driver.create_share(self._context, share1)
|
|
snapshot = fake_share.fake_snapshot(name='fakesnap',
|
|
share=share1,
|
|
share_name=share1['name'],
|
|
share_id=share1['id'],
|
|
provider_location='fakelocation')
|
|
|
|
share2 = fake_share.fake_share(id='fakeid2', share_proto='NFS',
|
|
share_id='fakeshareid2')
|
|
self.assertRaises(manila_exception.ManilaException,
|
|
self.driver.create_snapshot,
|
|
self._context,
|
|
{'name': snapshot['name'],
|
|
'id': snapshot['id'],
|
|
'share': share2})
|
|
|
|
self.driver.create_snapshot(self._context, snapshot)
|
|
|
|
# Deleted should succeed for missing volume
|
|
self.driver.delete_snapshot(self._context,
|
|
{'name': snapshot['name'],
|
|
'id': snapshot['id'],
|
|
'share': share2})
|
|
# Deleted should succeed for missing snap
|
|
self.driver.delete_snapshot(self._context,
|
|
{'name': 'wrong_snap',
|
|
'id': 'wrong_id',
|
|
'share': share1})
|
|
|
|
self.driver.delete_snapshot(self._context, snapshot)
|
|
self.driver.delete_share(self._context, share1)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_extend_share(self):
|
|
"""Expand share test."""
|
|
share1 = fake_share.fake_share(id='fakeid1', share_proto='NFS',
|
|
share_id='fakeshareid', size=10)
|
|
share2 = fake_share.fake_share(id='fakeid2',
|
|
share_proto='NFS', size=10)
|
|
self.driver.create_share(self._context, share1)
|
|
|
|
self.assertRaises(manila_exception.ZadaraShareNotFound,
|
|
self.driver.extend_share,
|
|
share2, 15)
|
|
|
|
self.driver.extend_share(share1, 15)
|
|
self.driver.delete_share(self._context, share1)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_create_share_from_snapshot(self):
|
|
"""Create a share from snapshot test."""
|
|
share1 = fake_share.fake_share(id='fakeid1', share_proto='NFS',
|
|
share_id='fakeshareid1')
|
|
share2 = fake_share.fake_share(id='fakeid2', share_proto='NFS',
|
|
share_id='fakeshareid2')
|
|
self.driver.create_share(self._context, share1)
|
|
|
|
snapshot = fake_share.fake_snapshot(name='fakesnap',
|
|
share=share1,
|
|
share_name=share1['name'],
|
|
share_id=share1['id'],
|
|
share_instance_id=share1['id'],
|
|
provider_location='fakelocation')
|
|
self.driver.create_snapshot(self._context, snapshot)
|
|
|
|
self.assertRaises(manila_exception.ManilaException,
|
|
self.driver.create_share_from_snapshot,
|
|
self._context,
|
|
share2,
|
|
{'name': snapshot['name'],
|
|
'id': snapshot['id'],
|
|
'share': share2,
|
|
'share_instance_id': share2['id']})
|
|
|
|
self.assertRaises(manila_exception.ManilaException,
|
|
self.driver.create_share_from_snapshot,
|
|
self._context,
|
|
share2,
|
|
{'name': 'fakesnapname',
|
|
'id': 'fakesnapid',
|
|
'share': share1,
|
|
'share_instance_id': share1['id']})
|
|
|
|
self.driver.create_share_from_snapshot(self._context, share2, snapshot)
|
|
self.driver.delete_share(self._context, share1)
|
|
self.driver.delete_share(self._context, share2)
|
|
|
|
def create_vpsa_backend_share(self):
|
|
vpsashare_params = {}
|
|
vpsashare_params['id'] = 'fake_id'
|
|
vpsashare_params['name'] = 'fake_name'
|
|
vpsashare_params['display-name'] = 'fake_name'
|
|
vpsashare_params['cg-name'] = 'fake_name'
|
|
vpsashare_params['size'] = 1
|
|
vpsashare_params['capacity'] = 1
|
|
vpsashare_params['pool'] = 'pool-0001'
|
|
vpsashare_params['share_proto'] = 'NFS'
|
|
vpsashare_params['nfs-export-path'] = '10.2.1.56:/export/manage_id'
|
|
vpsashare_params['provisioned-capacity'] = 1
|
|
vpsashare_params['server_ext_names'] = ''
|
|
vpsa_volname = 'fake-volume'
|
|
vpsa_share = (vpsa_volname, vpsashare_params)
|
|
RUNTIME_VARS['volumes'].append(vpsa_share)
|
|
return vpsa_share
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_manage_existing_share(self):
|
|
share1 = {'id': 'manage_name',
|
|
'name': 'manage_name',
|
|
'display-name': 'manage_name',
|
|
'size': 1,
|
|
'share_proto': 'NFS',
|
|
'export_locations':
|
|
[{'path': '10.2.1.56:/export/manage_id'}]}
|
|
driver_options = {}
|
|
vpsa_share = self.create_vpsa_backend_share()
|
|
|
|
self.driver.manage_existing(share1, driver_options)
|
|
self.assertEqual(vpsa_share[1]['display-name'].split('-')[1],
|
|
share1['display-name'])
|
|
self.driver.delete_share(self._context, share1)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_get_share_stats(self):
|
|
"""Get stats test."""
|
|
self.configuration.safe_get.return_value = 'ZadaraVPSAShareDriver'
|
|
data = self.driver.get_share_stats(True)
|
|
self.assertEqual('Zadara Storage', data['vendor_name'])
|
|
self.assertEqual('unknown', data['total_capacity_gb'])
|
|
self.assertEqual('unknown', data['free_capacity_gb'])
|
|
self.assertEqual(data['reserved_percentage'],
|
|
self.configuration.reserved_percentage)
|
|
self.assertEqual(data['snapshot_support'], True)
|
|
self.assertEqual(data['create_share_from_snapshot_support'], True)
|
|
self.assertEqual(data['revert_to_snapshot_support'], False)
|
|
self.assertEqual(data['vendor_name'], 'Zadara Storage')
|
|
self.assertEqual(data['driver_version'], self.driver.VERSION)
|
|
self.assertEqual(data['storage_protocol'], 'NFS_CIFS')
|
|
self.assertEqual(data['share_backend_name'],
|
|
self.configuration.share_backend_name)
|
|
|
|
def test_allow_access_with_incorrect_access_type(self):
|
|
share = fake_share.fake_share(id='fakeid1', share_proto='NFS')
|
|
access = fake_share.fake_access(access_type='fake_type')
|
|
|
|
self.assertRaises(manila_exception.ZadaraInvalidShareAccessType,
|
|
self.driver._allow_access,
|
|
self._context, share, access)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_share_allow_deny_access(self):
|
|
"""Test share access allow any deny rules."""
|
|
share = fake_share.fake_share(id='fakeid', share_proto='NFS',
|
|
share_id='fakeshareid')
|
|
self.driver.create_share(self._context, share)
|
|
access = fake_share.fake_access()
|
|
|
|
# Attach server for accessing share with the fake access rules
|
|
allow_access = self.driver._allow_access(self._context, share, access)
|
|
self.assertEqual(allow_access['driver_volume_type'],
|
|
share['share_proto'])
|
|
self.assertEqual('1.1.1.1:3260',
|
|
allow_access['data']['target_portal'])
|
|
(srv_name, srv_params) = RUNTIME_VARS['servers'][0]
|
|
self.assertEqual(srv_params['iscsi'],
|
|
allow_access['data']['target_ip'])
|
|
self.assertEqual(share['id'], allow_access['data']['id'])
|
|
self.assertEqual('CHAP', allow_access['data']['auth_method'])
|
|
self.assertEqual('test_chap_user',
|
|
allow_access['data']['auth_username'])
|
|
self.assertEqual('test_chap_secret',
|
|
allow_access['data']['auth_password'])
|
|
|
|
# Detach will not throw any error with missing access rules
|
|
dup_access = fake_share.fake_access()
|
|
self.driver._deny_access(self._context, share, dup_access)
|
|
# Detach server from the share with deny access rules
|
|
self.driver._deny_access(self._context, share, access)
|
|
self.driver.delete_share(self._context, share)
|
|
|
|
def create_vpsa_backend_share_snapshot(self, share):
|
|
vpsasnap_params = {}
|
|
vpsasnap_params['id'] = 'fakesnapid'
|
|
vpsasnap_params['name'] = 'fakesnapname'
|
|
vpsasnap_params['display-name'] = 'fakesnapname'
|
|
vpsasnap_params['provider-location'] = 'fakesnaplocation'
|
|
(vol_name, vol_params) = RUNTIME_VARS['volumes'][0]
|
|
vol_params['snapshots'].append(vpsasnap_params)
|
|
|
|
@mock.patch.object(requests.Session, 'request', FakeRequests)
|
|
def test_manage_existing_snapshot(self):
|
|
share = {'id': 'fake_id',
|
|
'share_id': 'fake_shareid',
|
|
'name': 'fake_name',
|
|
'display-name': 'fake_name',
|
|
'cg-name': 'fake_name',
|
|
'size': 1,
|
|
'capacity': 1,
|
|
'share_proto': 'NFS',
|
|
'pool': 'pool-0001',
|
|
'nfs-export-path': '10.2.1.56:/export/manage_id',
|
|
'provisioned-capacity': 1}
|
|
|
|
self.driver.create_share(self._context, share)
|
|
# Create a backend share that will be managed for manila
|
|
self.create_vpsa_backend_share_snapshot(share)
|
|
|
|
snapshot = {'id': 'manage_snapname',
|
|
'name': 'manage_snapname',
|
|
'display_name': 'manage_snapname',
|
|
'provider_location': 'fakesnaplocation',
|
|
'share': share}
|
|
driver_options = {}
|
|
|
|
self.driver.manage_existing_snapshot(snapshot, driver_options)
|
|
|
|
# Check that the backend share has been renamed
|
|
(vol_name, vol_params) = RUNTIME_VARS['volumes'][0]
|
|
self.assertEqual(
|
|
vol_params['snapshots'][0]['display-name'].split('-')[2],
|
|
snapshot['display_name'])
|
|
self.driver.delete_snapshot(self._context, snapshot)
|
|
self.driver.delete_share(self._context, share)
|