8181
8282
8383class HuaweiBaseDriver (driver .VolumeDriver ):
84- VERSION = "2.6.RC3 "
84+ VERSION = "2.6.4 "
8585
8686 def __init__ (self , * args , ** kwargs ):
8787 super (HuaweiBaseDriver , self ).__init__ (* args , ** kwargs )
@@ -3642,3 +3642,225 @@ def _delete_zone_and_remove_fc_initiators(self, wwns, host_id):
36423642 'data' : {'target_wwn' : tgt_port_wwns ,
36433643 'initiator_target_map' : init_targ_map }}
36443644 return info , portg_id
3645+
3646+
3647+ class HuaweiROCEDriver (HuaweiBaseDriver ):
3648+ """RoCE driver for Huawei storage arrays.
3649+
3650+ Version history:
3651+ 2.6.4 - start to support RoCE.
3652+ """
3653+
3654+ def __init__ (self , * args , ** kwargs ):
3655+ super (HuaweiROCEDriver , self ).__init__ (* args , ** kwargs )
3656+
3657+ def get_volume_stats (self , refresh = False ):
3658+ """Get volume status."""
3659+ data = HuaweiBaseDriver .get_volume_stats (self , refresh = False )
3660+ backend_name = self .configuration .safe_get ('volume_backend_name' )
3661+ data ['volume_backend_name' ] = backend_name or self .__class__ .__name__
3662+ data ['storage_protocol' ] = 'nvmeof'
3663+ data ['driver_version' ] = self .VERSION
3664+ data ['vendor_name' ] = 'Huawei'
3665+ return data
3666+
3667+ @coordination .synchronized ('huawei-mapping-{connector[host]}' )
3668+ def initialize_connection (self , volume , connector ):
3669+ """Map a volume to a host and return target RoCE information."""
3670+ self ._check_roce_params (volume , connector )
3671+
3672+ # Attach local lun.
3673+ roce_info = self ._initialize_connection (volume , connector )
3674+
3675+ # Attach remote lun if exists.
3676+ metadata = huawei_utils .get_lun_metadata (volume )
3677+ LOG .info ("Attach Volume, metadata is: %s." , metadata )
3678+ if metadata .get ('hypermetro' ):
3679+ try :
3680+ rmt_roce_info = (
3681+ self ._initialize_connection (volume , connector , False ))
3682+ except Exception :
3683+ with excutils .save_and_reraise_exception ():
3684+ self ._terminate_connection (volume , connector )
3685+
3686+ roce_info .get ('data' ).get ('target_portals' ).extend (
3687+ rmt_roce_info .get ('data' ).get ('target_portals' ))
3688+ roce_info .get ('data' ).get ('target_luns' ).extend (
3689+ rmt_roce_info .get ('data' ).get ('target_luns' ))
3690+
3691+ LOG .info ('initialize_common_connection_roce, '
3692+ 'return data is: %s.' , roce_info )
3693+ return roce_info
3694+
3695+ def _initialize_connection (self , volume , connector , local = True ):
3696+ LOG .info ('Initialize RoCE connection for volume %(id)s, '
3697+ 'connector info %(conn)s. array is in %(location)s.' ,
3698+ {'id' : volume .id , 'conn' : connector ,
3699+ 'location' : 'local' if local else 'remote' })
3700+
3701+ host_nqn = connector .get ("host_nqn" )
3702+
3703+ client = self .client if local else self .rmt_client
3704+
3705+ lun_id , lun_type = self .get_lun_id_and_type (
3706+ volume , constants .VOLUME_NOT_EXISTS_RAISE , local )
3707+ lun_info = client .get_lun_info (lun_id , lun_type )
3708+
3709+ target_ips = client .get_roce_params (connector )
3710+
3711+ host_id = client .add_host_with_check (
3712+ connector .get ('host' ), self .is_dorado_v6 , host_nqn )
3713+
3714+ try :
3715+ client .ensure_roceini_added (host_nqn , host_id )
3716+ except Exception :
3717+ with excutils .save_and_reraise_exception ():
3718+ self .remove_host_with_check (host_id )
3719+
3720+ hostgroup_id = client .add_host_to_hostgroup (host_id )
3721+
3722+ metadata = huawei_utils .get_lun_metadata (volume )
3723+ hypermetro_lun = metadata .get ('hypermetro' )
3724+
3725+ map_info = client .do_mapping (
3726+ lun_info , hostgroup_id , host_id ,
3727+ lun_type = lun_type , hypermetro_lun = hypermetro_lun )
3728+ host_lun_id = client .get_host_lun_id (host_id , lun_info , lun_type )
3729+ LOG .info ('initialize_connection, host lun id is: %(id)s. '
3730+ 'View info is %(view)s.' ,
3731+ {'id' : host_lun_id , 'view' : map_info })
3732+ host_lun_id = int (host_lun_id )
3733+
3734+ mapping_info = {
3735+ 'portals' : [(ip , "4420" , "rdma" ) for ip in target_ips ],
3736+ 'target_luns' : [host_lun_id ] * len (target_ips ),
3737+ 'discard' : True ,
3738+ 'volume_nguid' : lun_info .get ("NGUID" )
3739+ }
3740+ conn = {
3741+ 'driver_volume_type' : 'nvmeof' ,
3742+ 'data' : mapping_info
3743+ }
3744+ LOG .info ('Initialize RoCE connection successfully: %s.' , conn )
3745+ return conn
3746+
3747+ @coordination .synchronized ('huawei-mapping-{connector[host]}' )
3748+ def terminate_connection (self , volume , connector , ** kwargs ):
3749+ """Delete map between a volume and a host."""
3750+ self ._check_roce_params (volume , connector )
3751+
3752+ metadata = huawei_utils .get_lun_metadata (volume )
3753+ LOG .info ("terminate_connection, metadata is: %s." , metadata )
3754+ self ._terminate_connection (volume , connector )
3755+
3756+ if metadata .get ('hypermetro' ):
3757+ self ._terminate_connection (volume , connector , False )
3758+
3759+ LOG .info ('terminate_connection success.' )
3760+
3761+ def _terminate_connection (self , volume , connector , local = True ):
3762+ LOG .info ('_terminate_connection, detach %(local)s volume.' ,
3763+ {'local' : 'local' if local else 'remote' })
3764+
3765+ client = self .client if local else self .rmt_client
3766+
3767+ lun_id , lun_type = self .get_lun_id_and_type (
3768+ volume , constants .VOLUME_NOT_EXISTS_WARN , local )
3769+
3770+ initiator_name = connector .get ('host_nqn' )
3771+ host_name = connector .get ('host' )
3772+
3773+ LOG .info ('terminate_connection: initiator name: %(ini)s, LUN ID: %('
3774+ 'lunid)s, lun type: %(lun_type)s, connector: %('
3775+ 'connector)s.' , {'ini' : initiator_name , 'lunid' : lun_id ,
3776+ 'lun_type' : lun_type ,
3777+ 'connector' : connector })
3778+
3779+ lungroup_id = None
3780+ portgroup_id = None
3781+ view_id = None
3782+
3783+ host_id = huawei_utils .get_host_id (client , host_name )
3784+ if host_id :
3785+ mapping_view_name = constants .MAPPING_VIEW_PREFIX + host_id
3786+ view_id = client .find_mapping_view (mapping_view_name )
3787+ if view_id :
3788+ lungroup_id = client .find_lungroup_from_map (view_id )
3789+ portgroup_id = client .get_portgroup_by_view (view_id )
3790+
3791+ if lun_id and lungroup_id :
3792+ lungroup_ids = client .get_lungroupids_by_lunid (lun_id , lun_type )
3793+ if lungroup_id in lungroup_ids :
3794+ client .remove_lun_from_lungroup (lungroup_id , lun_id , lun_type )
3795+ else :
3796+ LOG .warning ("LUN is not in lungroup. LUN ID: %(lun_id)s. "
3797+ "Lungroup id: %(lungroup_id)s." ,
3798+ {"lun_id" : lun_id , "lungroup_id" : lungroup_id })
3799+ if self .configuration .retain_storage_mapping :
3800+ return
3801+
3802+ mapping_param = {'host_id' : host_id , 'initiator_name' : initiator_name ,
3803+ 'lungroup_id' : lungroup_id , 'view_id' : view_id ,
3804+ 'portgroup_id' : portgroup_id }
3805+ self ._delete_storage_mapping (client , mapping_param )
3806+
3807+ def _delete_storage_mapping (self , client , mapping_param ):
3808+ left_lun_num = - 1
3809+ lungroup_id = mapping_param .get ('lungroup_id' )
3810+ view_id = mapping_param .get ('view_id' )
3811+ portgroup_id = mapping_param .get ('portgroup_id' )
3812+ initiator_name = mapping_param .get ('initiator_name' )
3813+ host_id = mapping_param .get ('host_id' )
3814+ if lungroup_id :
3815+ left_lun_num = client .get_obj_count_from_lungroup (lungroup_id )
3816+ if view_id and (int (left_lun_num ) <= 0 ):
3817+ if portgroup_id and client .is_portgroup_associated_to_view (
3818+ view_id , portgroup_id ):
3819+ client .delete_portgroup_mapping_view (view_id , portgroup_id )
3820+
3821+ if client .lungroup_associated (view_id , lungroup_id ):
3822+ client .delete_lungroup_mapping_view (view_id , lungroup_id )
3823+
3824+ client .delete_lungroup (lungroup_id )
3825+
3826+ if client .is_roce_initiator_associated_to_host (
3827+ initiator_name , host_id ):
3828+ client .remove_roce_initiator_from_host (initiator_name , host_id )
3829+
3830+ hostgroup_name = constants .HOSTGROUP_PREFIX + host_id
3831+ hostgroup_id = client .find_hostgroup (hostgroup_name )
3832+ if hostgroup_id :
3833+ if client .hostgroup_associated (view_id , hostgroup_id ):
3834+ client .delete_hostgoup_mapping_view (view_id , hostgroup_id )
3835+ client .remove_host_from_hostgroup (hostgroup_id , host_id )
3836+ client .delete_hostgroup (hostgroup_id )
3837+ client .remove_host (host_id )
3838+
3839+ client .delete_mapping_view (view_id )
3840+
3841+ def _check_roce_params (self , volume , connector ):
3842+ if not volume or not connector :
3843+ msg = _ (
3844+ '%(param)s is none.'
3845+ % {'param' : 'volume' if not volume else 'connector' })
3846+ LOG .error (msg )
3847+ raise exception .VolumeBackendAPIException (data = msg )
3848+
3849+ if not volume .id :
3850+ msg = _ (
3851+ 'volume param is error. volume is %(volume)s.'
3852+ % {'volume' : volume })
3853+ LOG .error (msg )
3854+ raise exception .VolumeBackendAPIException (data = msg )
3855+
3856+ if not connector .get ('host_nqn' ) or not connector .get ('host' ):
3857+ msg = _ (
3858+ 'connector param is error. connector is %(connector)s.'
3859+ % {'connector' : connector })
3860+ LOG .error (msg )
3861+ raise exception .VolumeBackendAPIException (data = msg )
3862+
3863+ if not self .is_dorado_v6 :
3864+ msg = _ ("Current storage doesn't support RoCE." )
3865+ LOG .error (msg )
3866+ raise exception .VolumeBackendAPIException (data = msg )
0 commit comments