From: Zhiteng Huang Date: Thu, 5 Jul 2012 07:08:34 +0000 (+0800) Subject: Updated HpSanISCSIDriver to use initialize/terminate methods. X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=c7690fc53dba8ce7221c5f7afd93238be1ea0498;p=openstack-build%2Fcinder-build.git Updated HpSanISCSIDriver to use initialize/terminate methods. This is based on Nirmal Ranganathan's patch for Nova-volume, which added a new key 'host' for initialize/terminate volume connection information in nova to fix bug 992729. HpSanISCSIDriver should adopt the change in order to work properly. Also added missing unit tests for the HpSanISCSIDriver. Fixes bug 992729. Change-Id: I9584811a340799161d8c9ae88b1def282380c9ad Signed-off-by: Zhiteng Huang --- diff --git a/Authors b/Authors index 31f503459..310e6bd7e 100644 --- a/Authors +++ b/Authors @@ -47,4 +47,5 @@ Vladimir Popovski Yoshiaki Tamura Yun Mao Yuriy Taraday +Zhiteng Huang Zhongyue Luo diff --git a/cinder/tests/test_HpSanISCSIDriver.py b/cinder/tests/test_HpSanISCSIDriver.py new file mode 100644 index 000000000..083383c34 --- /dev/null +++ b/cinder/tests/test_HpSanISCSIDriver.py @@ -0,0 +1,212 @@ +# Copyright 2012 OpenStack LLC +# +# 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. +from cinder import exception +from cinder import log as logging +from cinder import test +from cinder.volume import san + +LOG = logging.getLogger(__name__) + + +class HpSanISCSITestCase(test.TestCase): + + def setUp(self): + super(HpSanISCSITestCase, self).setUp() + self.stubs.Set(san.HpSanISCSIDriver, "_cliq_run", + self._fake_cliq_run) + self.stubs.Set(san.HpSanISCSIDriver, "_get_iscsi_properties", + self._fake_get_iscsi_properties) + self.driver = san.HpSanISCSIDriver() + self.volume_name = "fakevolume" + self.connector = {'ip': '10.0.0.2', + 'initiator': 'iqn.1993-08.org.debian:01:222', + 'host': 'fakehost'} + self.properties = {'target_discoverd': True, + 'target_portal': '10.0.1.6:3260', + 'target_iqn': + 'iqn.2003-10.com.lefthandnetworks:group01:25366:fakev', + 'volume_id': 1} + + def tearDown(self): + super(HpSanISCSITestCase, self).tearDown() + + def _fake_get_iscsi_properties(self, volume): + return self.properties + + def _fake_cliq_run(self, verb, cliq_args): + """Return fake results for the various methods.""" + + def create_volume(cliq_args): + """ + input = "createVolume description="fake description" + clusterName=Cluster01 volumeName=fakevolume + thinProvision=0 output=XML size=1GB" + """ + output = """ + + """ + self.assertEqual(cliq_args['volumeName'], self.volume_name) + self.assertEqual(cliq_args['thinProvision'], '1') + self.assertEqual(cliq_args['size'], '1GB') + return output, None + + def delete_volume(cliq_args): + """ + input = "deleteVolume volumeName=fakevolume prompt=false + output=XML" + """ + output = """ + + """ + self.assertEqual(cliq_args['volumeName'], self.volume_name) + self.assertEqual(cliq_args['prompt'], 'false') + return output, None + + def assign_volume(cliq_args): + """ + input = "assignVolumeToServer volumeName=fakevolume + serverName=fakehost + output=XML" + """ + output = """ + + """ + self.assertEqual(cliq_args['volumeName'], self.volume_name) + self.assertEqual(cliq_args['serverName'], self.connector['host']) + return output, None + + def unassign_volume(cliq_args): + """ + input = "unassignVolumeToServer volumeName=fakevolume + serverName=fakehost output=XML + """ + output = """ + + """ + self.assertEqual(cliq_args['volumeName'], self.volume_name) + self.assertEqual(cliq_args['serverName'], self.connector['host']) + return output, None + + def get_cluster_info(cliq_args): + """ + input = "getClusterInfo clusterName=Cluster01 searchDepth=1 + verbose=0 output=XML" + """ + output = """ + + + + + + """ + return output, None + + def get_volume_info(cliq_args): + """ + input = "getVolumeInfo volumeName=fakevolume output=XML" + """ + output = """ + + + + + """ + return output, None + + def test_error(cliq_args): + output = """ + + """ + return output, None + + self.assertEqual(cliq_args['output'], 'XML') + try: + verbs = {'createVolume': create_volume, + 'deleteVolume': delete_volume, + 'assignVolumeToServer': assign_volume, + 'unassignVolumeToServer': unassign_volume, + 'getClusterInfo': get_cluster_info, + 'getVolumeInfo': get_volume_info, + 'testError': test_error} + except KeyError: + raise NotImplementedError() + + return verbs[verb](cliq_args) + + def test_create_volume(self): + volume = {'name': self.volume_name, 'size': 1} + model_update = self.driver.create_volume(volume) + expected_iqn = "iqn.2003-10.com.lefthandnetworks:group01:25366:fakev" + expected_location = "10.0.1.6:3260,1 %s" % expected_iqn + self.assertEqual(model_update['provider_location'], expected_location) + + def test_delete_volume(self): + volume = {'name': self.volume_name} + self.driver.delete_volume(volume) + + def test_initialize_connection(self): + volume = {'name': self.volume_name} + result = self.driver.initialize_connection(volume, self.connector) + self.assertEqual(result['driver_volume_type'], 'iscsi') + self.assertDictMatch(result['data'], self.properties) + + def test_terminate_connection(self): + volume = {'name': self.volume_name} + self.driver.terminate_connection(volume, self.connector) + + def test_create_snapshot(self): + try: + self.driver.create_snapshot("") + except NotImplementedError: + pass + + def test_create_volume_from_snapshot(self): + try: + self.driver.create_volume_from_snapshot("", "") + except NotImplementedError: + pass + + def test_cliq_error(self): + try: + self.driver._cliq_run_xml("testError", {}) + except exception.Error: + pass diff --git a/cinder/volume/san.py b/cinder/volume/san.py index 4edc81add..26e214222 100644 --- a/cinder/volume/san.py +++ b/cinder/volume/san.py @@ -582,6 +582,14 @@ class HpSanISCSIDriver(SanISCSIDriver): return model_update + def create_volume_from_snapshot(self, volume, snapshot): + """Creates a volume from a snapshot.""" + raise NotImplementedError() + + def create_snapshot(self, snapshot): + """Creates a snapshot.""" + raise NotImplementedError() + def delete_volume(self, volume): """Deletes a volume.""" cliq_args = {} @@ -594,64 +602,45 @@ class HpSanISCSIDriver(SanISCSIDriver): # TODO(justinsb): Is this needed here? raise exception.Error(_("local_path not supported")) - def ensure_export(self, context, volume): - """Synchronously recreates an export for a logical volume.""" - return self._do_export(context, volume, force_create=False) - - def create_export(self, context, volume): - return self._do_export(context, volume, force_create=True) + def initialize_connection(self, volume, connector): + """Assigns the volume to a server. - def _do_export(self, context, volume, force_create): - """Supports ensure_export and create_export""" - volume_info = self._cliq_get_volume_info(volume['name']) + Assign any created volume to a compute node/host so that it can be + used from that host. HP VSA requires a volume to be assigned + to a server. - is_shared = 'permission.authGroup' in volume_info + This driver returns a driver_volume_type of 'iscsi'. + The format of the driver data is defined in _get_iscsi_properties. + Example return value: - model_update = {} + { + 'driver_volume_type': 'iscsi' + 'data': { + 'target_discovered': True, + 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000001', + 'target_protal': '127.0.0.1:3260', + 'volume_id': 1, + } + } - should_export = False - - if force_create or not is_shared: - should_export = True - # Check that we have a project_id - project_id = volume['project_id'] - if not project_id: - project_id = context.project_id - - if project_id: - #TODO(justinsb): Use a real per-project password here - chap_username = 'proj_' + project_id - # HP/Lefthand requires that the password be >= 12 characters - chap_password = 'project_secret_' + project_id - else: - msg = (_("Could not determine project for volume %s, " - "can't export") % - (volume['name'])) - if force_create: - raise exception.Error(msg) - else: - LOG.warn(msg) - should_export = False - - if should_export: - cliq_args = {} - cliq_args['volumeName'] = volume['name'] - cliq_args['chapName'] = chap_username - cliq_args['targetSecret'] = chap_password - - self._cliq_run_xml("assignVolumeChap", cliq_args) - - model_update['provider_auth'] = ("CHAP %s %s" % - (chap_username, chap_password)) + """ + cliq_args = {} + cliq_args['volumeName'] = volume['name'] + cliq_args['serverName'] = connector['host'] + self._cliq_run_xml("assignVolumeToServer", cliq_args) - return model_update + iscsi_properties = self._get_iscsi_properties(volume) + return { + 'driver_volume_type': 'iscsi', + 'data': iscsi_properties + } - def remove_export(self, context, volume): - """Removes an export for a logical volume.""" + def terminate_connection(self, volume, connector): + """Unassign the volume from the host.""" cliq_args = {} cliq_args['volumeName'] = volume['name'] - - self._cliq_run_xml("unassignVolume", cliq_args) + cliq_args['serverName'] = connector['host'] + self._cliq_run_xml("unassignVolumeToServer", cliq_args) class SolidFireSanISCSIDriver(SanISCSIDriver):