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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions test/HPE3ParClient_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from testconfig import config
import datetime
from functools import wraps
from hpe3parclient import file_client
from hpe3par_sdk import client
#from hpe3parclient import client, file_client

Expand Down Expand Up @@ -127,8 +128,7 @@ def setUp(self, withSSH=False, withFilePersona=False):

time.sleep(1)
if self.withFilePersona:
pass
#self.cl = file_client.HPE3ParFilePersonaClient(self.flask_url)
self.cl = file_client.HPE3ParFilePersonaClient(self.flask_url)
else:
self.cl = client.HPE3ParClient(self.flask_url)

Expand Down Expand Up @@ -209,10 +209,16 @@ def setUp(self, withSSH=False, withFilePersona=False):

if not self.port:
ports = self.cl.getPorts()
for port in ports:
#print port.mode
ports = [port for port in ports if port.linkState == 4 and ( port.device is not None or not port.device) and port.mode == 2]
self.port = ports[0].port_pos
if withFilePersona:
ports = [p for p in ports['members']
if p['linkState'] == 4 and # Ready
('device' not in p or not p['device']) and
p['mode'] == self.cl.PORT_MODE_TARGET]
self.port = ports[0]['portPos']
else:
ports = [port for port in ports if port.linkState == 4 and ( port.device is not None or not port.device) and port.mode == 2]
self.port = ports[0].port_pos


def tearDown(self):
self.cl.logout()
Expand Down
136 changes: 131 additions & 5 deletions test/HPE3ParMockServer_flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@
RCOPY_STARTED = 3
RCOPY_STOPPED = 5

THIN_DEDUP_TYPE = 6
COMPRESSION_ENABLED = 1
TDVV_ENABLED = 1

THIN_PROVISIONING_TYPE = 2
COMPRESSION_DISABLED = 2
THIN_PROVISIONING_ENABLED = 2

parser = argparse.ArgumentParser()
parser.add_argument("-debug", help="Turn on http debugging",
default=False, action="store_true")
Expand Down Expand Up @@ -821,7 +829,17 @@ def getPort(portPos):
@app.route('/api/v1/vluns', methods=['GET'])
def get_vluns():
debugRequest(flask.request)
resp = flask.make_response(json.dumps(vluns), 200)
query = flask.request.args.get('query')
vlun_dir = dict()
if query is not None and "volumeName" in query:
if vluns:
for vlun in vluns['members']:
if vlun['volumeName'] in query:
vlun_dir.update({'members': [vlun]})
resp = flask.make_response(json.dumps(vlun_dir), 200)
break
else:
resp = flask.make_response(json.dumps(vluns), 200)
return resp


Expand Down Expand Up @@ -866,7 +884,7 @@ def create_snapshot(volume_name):
valid_online_param_keys = {'online': None, 'destCPG': None,
'tpvv': None, 'tdvv': None,
'snapCPG': None, 'saveSnapshot': None,
'priority': None}
'priority': None, 'reduce': None}
params = data['parameters']
if 'online' in params and params['online']:
# we are checking online copy
Expand Down Expand Up @@ -964,8 +982,8 @@ def create_volumes():
'tpvv': None, 'usrSpcAllocWarningPct': None,
'usrSpcAllocLimitPct': None, 'isCopy': None,
'copyOfName': None, 'copyRO': None, 'expirationHours': None,
'retentionHours': None}

'retentionHours': None, 'objectKeyValues': None,
'reduce': None}
for key in list(data.keys()):
if key not in list(valid_keys.keys()):
throw_error(400, INV_INPUT, "Invalid Parameter '%s'" % key)
Expand All @@ -990,12 +1008,21 @@ def create_volumes():
throw_error(400, TOO_LARGE,
'Volume size is above architectural limit : 16TiB')

if 'tpvv' in list(data.keys()):
if data['tpvv'] not in [True, False, None]:
throw_error(400, INV_INPUT_WRONG_TYPE,
'Invalid input:wrong type for value - tpvv')

if 'id' in list(data.keys()):
for vol in volumes['members']:
if vol['id'] == data['id']:
throw_error(409, EXISTENT_ID,
'Specified volume ID already exists.')

if 'reduce' in list(data.keys()):
data['provisioningType'] = 6
data['deduplicationState'] = 1
data['compressionState'] = 1
volumes['members'].append(data)
return flask.make_response("", 200)

Expand Down Expand Up @@ -1067,6 +1094,52 @@ def modify_volume(volume_name):
resp = flask.make_response(json.dumps(task), 200)
return resp

if data.get('action') == 6:
valid_keys = {'action': None, 'tuneOperation': None, 'userCPG': None,
'snapCPG': None, 'conversionOperation': None,
'keepVV': None, 'compression': None}

for key in list(data.keys()):
if key not in list(valid_keys.keys()):
throw_error(400, INV_INPUT, "Invalid Parameter '%s'" % key)

if 'conversionOperation' in list(data.keys()):
if data['conversionOperation'] not in [1, 2, 3, 4]:
throw_error(400, INV_INPUT_WRONG_TYPE,
"Invalid input:wrong type for value"
" - conversionOperation")

if 'compression' in list(data.keys()):
if data['compression'] not in [True, False, None]:
throw_error(400, INV_INPUT_WRONG_TYPE,
"Invalid input:wrong type for value"
" - compression")

if 'tuneOperation' in list(data.keys()):
if data['tuneOperation'] not in [1, 2]:
throw_error(400, INV_INPUT_WRONG_TYPE,
"Invalid input:wrong type for value"
" - tuneOperation")

if 'keepVV' in list(data.keys()) and len(data['keepVV']) > 31:
throw_error(400, INV_INPUT_EXCEEDS_LENGTH,
'Invalid Input: String length exceeds limit : keepVV')

conversion_operation = data.get('conversionOperation')

if conversion_operation == 4:
volume['provisioningType'] = THIN_DEDUP_TYPE
volume['deduplicationState'] = TDVV_ENABLED
volume['compressionState'] = COMPRESSION_ENABLED
else:
volume['provisioningType'] = THIN_PROVISIONING_TYPE
volume['deduplicationState'] = THIN_PROVISIONING_ENABLED
volume['compressionState'] = COMPRESSION_DISABLED
#task['taskid'] = '123'
task['taskid'] = 123
tasks['members'].append({'id':123})
resp = flask.make_response(json.dumps(task), 200)
return resp
_grow_volume(volume, data)

# do volume renames last
Expand Down Expand Up @@ -1153,6 +1226,19 @@ def delete_remote_copy_group(rcg_name):
throw_error(404, NON_EXISTENT_RCOPY_GROUP,
"The remote copy group '%s' does not exist." % rcg_name)

@app.route('/api/v1/remotecopygroups/<rcg_name>/volumes/<volume_name>', methods=['DELETE'])
def remove_volume_from_remote_copy_group(rcg_name, volume_name):
debugRequest(flask.request)
for rcg in remote_copy_groups['members']:
if rcg['name'] == rcg_name:
for vol in rcg['volumes']:
if volume_name == vol['name']:
rcg['volumes'].remove(vol)
resp = flask.make_response(json.dumps(rcg), 200)
return resp

throw_error(404, NON_EXISTENT_RCOPY_GROUP,
"The remote copy group '%s' does not exist." % rcg_name)

@app.route('/api/v1/remotecopygroups/<rcg_name>', methods=['PUT'])
def modify_remote_copy_group(rcg_name):
Expand Down Expand Up @@ -1226,6 +1312,46 @@ def modify_remote_copy_group(rcg_name):
throw_error(404, NON_EXISTENT_RCOPY_GROUP,
"remote copy group doesn't exist")

@app.route('/api/v1/remotecopygroups/<rcg_name>/volumes', methods=['POST'])
def modify_remote_copy_group_post(rcg_name):
debugRequest(flask.request)
data = json.loads(flask.request.data.decode('utf-8'))

valid_keys = {'targets': None, 'targetName': None,
'mode': None, 'userCPG': None, 'snapCPG': None,
'localSnapCPG': None, 'localUserCPG': None, 'domain': None,
'unsetUserCPG': None, 'unsetSnapCPG': None, '': None,
'remoteUserCPG': None, 'remoteSnapCPG': None,
'syncPeriod': None, 'rmSyncPeriod': None,
'snapFrequency': None, 'rmSnapFrequency': None,
'policies': None, 'autoRecover': None,
'overPeriodAlert': None, 'autoFailover': None,
'pathManagement': None, 'secVolumeName': None,
'snapshotName': None, 'volumeAutoCreation': None,
'skipInitialSync': None, 'volumeName': None, 'action': None}

for key in list(data.keys()):
if key not in list(valid_keys.keys()):
throw_error(400, INV_INPUT, "Invalid Parameter '%s'" % key)

action = data.get('action')
for rcg in remote_copy_groups['members']:
if rcg['name'] == rcg_name:
vol_found = False
for vol in volumes['members']:
if data['volumeName'] == vol['name']:
vol_found = True
vol['localVolumeName'] = data['volumeName']
vol['remoteVolumes'] = [{ 'targetName': data['targets'][0]['targetName']}]
rcg['volumes'].append(vol)
if not vol_found:
throw_error(404, NON_EXISTENT_VOL, "volume doesn't exist")
resp = flask.make_response(json.dumps(rcg), 200)

return resp

throw_error(404, NON_EXISTENT_RCOPY_GROUP,
"remote copy group doesn't exist")

@app.route('/api/v1/remotecopygroups/<rcg_name>', methods=['POST'])
def recover_remote_copy_group(rcg_name):
Expand All @@ -1246,7 +1372,7 @@ def recover_remote_copy_group(rcg_name):
if rcg['name'] == rcg_name:
# We are failing over a remote copy group
if action == FAILOVER_GROUP:
rcg['roleReversed'] = True
rcg['targets'] = [{'roleReversed': True}]
resp = flask.make_response(json.dumps(rcg), 200)

return resp
Expand Down
46 changes: 35 additions & 11 deletions test/test_HPE3ParClient_MockSSH.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from test import HPE3ParClient_base
from hpe3parclient import exceptions
from hpe3parclient import ssh
from hpe3parclient import client
from hpe3par_sdk import client as sdk_client

# Python 3+ override
try:
Expand Down Expand Up @@ -52,7 +54,7 @@ def mock_paramiko(self, known_hosts_file, missing_key_policy):
'set_missing_host_key_policy',
mock_smhkp, create=True):
try:
self.cl.setSSHOptions(
self.cl.client.setSSHOptions(
ip, user, password,
known_hosts_file=known_hosts_file,
missing_key_policy=missing_key_policy)
Expand Down Expand Up @@ -80,17 +82,18 @@ def mock_paramiko(self, known_hosts_file, missing_key_policy):
expected = missing_key_policy.__class__.__name__
self.assertEqual(actual, expected)

def do_mock_create_ssh(self, known_hosts_file, missing_key_policy):
@mock.patch('hpe3parclient.client.ssh.HPE3PARSSHClient', spec=True)
def do_mock_create_ssh(self, known_hosts_file, missing_key_policy, mock_ssh_client):
"""Verify that params are getting forwarded to _create_ssh()."""

mock_ssh = mock.Mock()
with mock.patch('hpe3parclient.ssh.HPE3PARSSHClient._create_ssh',
mock_ssh, create=True):

self.cl.setSSHOptions(ip, user, password,
client.ssh.HPE3PARSSHClient._create_ssh(
known_hosts_file=known_hosts_file,
missing_key_policy=missing_key_policy)

mock_ssh_client.close.return_value = True
mock_ssh.assert_called_with(missing_key_policy=missing_key_policy,
known_hosts_file=known_hosts_file)

Expand All @@ -103,9 +106,10 @@ def do_mock_ssh(self, known_hosts_file, missing_key_policy,
mock_ssh_client):
"""Verify that params are getting forwarded to HPE3PARSSHClient."""

self.cl.setSSHOptions(ip, user, password,
known_hosts_file=known_hosts_file,
missing_key_policy=missing_key_policy)
self.cl.client.setSSHOptions(ip, user, password, 22, None, None,
known_hosts_file=known_hosts_file,
missing_key_policy=missing_key_policy)


mock_ssh_client.assert_called_with(
ip, user, password, 22, None, None,
Expand Down Expand Up @@ -163,13 +167,13 @@ def test_create_ssh_except(self):
known_hosts_file=None,
missing_key_policy=paramiko.AutoAddPolicy)

self.cl.ssh.ssh = mock.Mock()
self.cl.ssh.ssh.invoke_shell.side_effect = Exception('boom')

self.cl.client.ssh.ssh = mock.Mock()
self.cl.client.ssh.ssh.invoke_shell.side_effect = Exception('boom')
cmd = ['fake']
self.assertRaises(exceptions.SSHException, self.cl.ssh._run_ssh, cmd)
self.assertRaises(exceptions.SSHException, self.cl.client.ssh._run_ssh, cmd)

self.cl.ssh.ssh.assert_has_calls(
self.cl.client.ssh.ssh.assert_has_calls(
[
mock.call.get_transport(),
mock.call.get_transport().is_alive(),
Expand Down Expand Up @@ -247,3 +251,23 @@ def test_strip_input_from_output(self):
'totals']
result = ssh.HPE3PARSSHClient.strip_input_from_output(cmd, output)
self.assertEqual(['out1', 'out2', 'out3'], result)

@mock.patch('hpe3parclient.client.ssh.HPE3PARSSHClient', spec=True)
def test_verify_get_port(self, mock_ssh_client):
known_hosts_file = "test_bogus_known_hosts_file"
missing_key_policy = "AutoAddPolicy"
cli_output = ["-Service-,-State-,HTTPS_Port,-Version-,"
"------------------API_URL-------------------",
"Enabled,Active,443,1.7.0,"
"https://vp2-157.in.rdlabs.hpecorp.net/api/v1"]
with mock.patch.object(client.HPE3ParClient,
"_getSshClient") as mock_get_ssh_client:
mock_get_ssh_client.return_value = mock_ssh_client
mock_ssh_client.open.return_value = True
mock_ssh_client.run.return_value = cli_output
result = sdk_client.HPE3ParClient.getPortNumber(
ip, user, password,
22, None, None,
known_hosts_file=known_hosts_file,
missing_key_policy=missing_key_policy)
self.assertEqual('443', result)
37 changes: 21 additions & 16 deletions test/test_HPE3ParClient_Stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import mock
from test import HPE3ParClient_base
from hpe3parclient import exceptions
from hpe3parclient import client


class HPE3ParClientStatsTestCase(HPE3ParClient_base.HPE3ParClientBaseTestCase):
Expand All @@ -33,11 +34,8 @@ def test_getCPGStatData(self):

# Find a cpg name
cpgs = self.cl.getCPGs()
cpg = cpgs['members'][0]
name = cpg['name']
self.assertTrue(self.findInDict(cpgs['members'], 'name', name))
# Make srstatld give valid output
self.cl._run = mock.Mock(return_value=[
name = cpgs[0].name
return_value=[
',,---IO/s----,,,-KBytes/s--,,,---Svct ms----,,,-IOSz KBytes-,,,,',
'Time,Secs,Rd,Wr,Tot,Rd,Wr,Tot,Rd,Wr,Tot,Rd,Wr,Tot,QLen,AvgBusy%',
'2015-07-02 01:45:00 PDT,1435826700,1.3,6.2,7.5,4.6,89.3,93.9,'
Expand All @@ -48,18 +46,25 @@ def test_getCPGStatData(self):
'-----------------------------------------',
',144,1.3,6.7,8.0,4.7,95.6,100.4,0.59,10.24,8.69,3.7,14.2,12.5,'
'0.9,0.1',
])
# Get result
interval = 'daily'
history = '7d'
result = self.cl.getCPGStatData(name, interval, history)
# Check result
self.assertIsNotNone(result)
self.assertTrue(type(result) is dict)
]
with mock.patch.object(client.HPE3ParClient,
"_run") as mock_client_run:
mock_client_run.return_value = return_value

# Get result
interval = 'daily'
history = '7d'
result = self.cl.getCPGStatData(name, interval, history)
# Check result
self.assertIsNotNone(result)
self.assertTrue(type(result) is dict)
# Make srstatld give invalid output
self.cl._run = mock.Mock(return_value=[''])
# Expect exception
self.assertRaises(exceptions.SrstatldException,
return_value=['']
with mock.patch.object(client.HPE3ParClient,
"_run") as mock_client_run:
mock_client_run.return_value = return_value
# Expect exception
self.assertRaises(exceptions.SrstatldException,
self.cl.getCPGStatData,
name, interval, history)
self.printFooter('getCPGStatData')
Expand Down
Loading