Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emc vnx block add host mapping view #807

Merged
merged 16 commits into from
May 6, 2022
Merged
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
122 changes: 122 additions & 0 deletions delfin/drivers/dell_emc/vnx/vnx_block/component_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# 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.
import copy
import re

import six
Expand Down Expand Up @@ -443,3 +444,124 @@ def get_iscsi_ports(self):
name = '%s-%s' % (iscsi_port.get('sp'), iscsi_port.get('port_id'))
iscsi_port_map[name] = iscsi_port
return iscsi_port_map

def list_masking_views(self, storage_id):
views = self.navi_handler.list_masking_views()
views_list = []
host_vv_set = set()
if views:
for view in views:
name = view.get('storage_group_name')
host_names = view.get('host_names')
lun_ids = view.get('lun_ids')
if name:
if name == '~physical' or name == '~management':
continue
view_model_template = {
'native_masking_view_id': view.get(
'storage_group_uid'),
"name": view.get('storage_group_name'),
"storage_id": storage_id
}
if host_names and lun_ids:
host_names = list(set(host_names))
for host_name in host_names:
host_id = host_name.replace(' ', '')
for lun_id in lun_ids:
host_vv_key = '%s_%s' % (host_id, lun_id)
if host_vv_key in host_vv_set:
continue
host_vv_set.add(host_vv_key)
view_model = copy.deepcopy(view_model_template)
view_model[
'native_storage_host_id'] = host_id
view_model['native_volume_id'] = lun_id
view_model[
'native_masking_view_id'] = '%s_%s_%s' % (
view_model.get('native_masking_view_id'),
host_id, lun_id)
views_list.append(view_model)
return views_list

def list_storage_host_initiators(self, storage_id):
initiators = self.navi_handler.list_hbas()
initiators_list = []
initiator_set = set()
port_types = {}
if initiators:
ports = self.list_ports(storage_id)
for port in (ports or []):
if port and port.get('type'):
port_types[port.get('name')] = port.get('type')
for initiator in (initiators or []):
if initiator and initiator.get('hba_uid'):
hba_uid = initiator.get('hba_uid')
type = ''
if port_types:
ports = initiator.get('port_ids')
if ports:
port_id = list(ports)[0]
type = port_types.get(port_id, '')
host_id = initiator.get('server_name', '').replace(' ', '')
if host_id == hba_uid:
host_id = None
if not host_id:
continue
if hba_uid in initiator_set:
continue
initiator_set.add(hba_uid)

initiator_model = {
"name": hba_uid,
"storage_id": storage_id,
"native_storage_host_initiator_id": hba_uid,
"wwn": hba_uid,
"type": consts.INITIATOR_TYPE_MAP.get(
type.upper(), constants.InitiatorType.UNKNOWN),
"status": constants.InitiatorStatus.ONLINE,
"native_storage_host_id": host_id
}
initiators_list.append(initiator_model)
return initiators_list

def list_storage_hosts(self, storage_id):
hosts = self.navi_handler.list_hbas()
host_list = []
host_ids = set()
host_ips = {}
for host in (hosts or []):
if host and host.get('server_name'):
os_type = constants.HostOSTypes.UNKNOWN
os_name = host.get('hba_vendor_description')
ip_addr = host.get('server_ip_address')
if ip_addr == 'UNKNOWN':
continue
if os_name and 'VMware ESXi' in os_name:
os_type = constants.HostOSTypes.VMWARE_ESX
id = host.get('server_name').replace(' ', '')
if id in host_ids:
continue
host_ids.add(id)

if ip_addr in host_ips.keys():
first_port_ids = host_ips.get(ip_addr)
cur_port_ids = host.get('port_ids')
add_host = False
intersections = list(
set(first_port_ids).intersection(set(cur_port_ids)))
if not intersections:
add_host = True
if not add_host:
continue
host_ips[ip_addr] = host.get('port_ids')

host_model = {
"name": host.get('server_name'),
"storage_id": storage_id,
"native_storage_host_id": id,
"os_type": os_type,
"status": constants.HostStatus.NORMAL,
"ip_address": ip_addr
}
host_list.append(host_model)
return host_list
11 changes: 11 additions & 0 deletions delfin/drivers/dell_emc/vnx/vnx_block/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
GET_LOG_API = 'getlog -date %(begin_time)s %(end_time)s'
EMCVNX_VENDOR = 'DELL EMC'
RAID_GROUP_ID_PREFIX = 'raid_group_'
GET_SG_LIST_HOST_API = 'storagegroup -messner -list -host'
GET_PORT_LIST_HBA_API = 'port -list -hba'
STATUS_MAP = {
'Ready': constants.StoragePoolStatus.NORMAL,
'Offline': constants.StoragePoolStatus.OFFLINE,
Expand Down Expand Up @@ -143,3 +145,12 @@
'SAS': constants.PortType.SAS,
'UNKNOWN': constants.PortType.OTHER
}
INITIATOR_TYPE_MAP = {
'FC': constants.InitiatorType.FC,
'FCOE': constants.InitiatorType.FC,
'ISCSI': constants.InitiatorType.ISCSI,
'SAS': constants.InitiatorType.SAS,
'UNKNOWN': constants.InitiatorType.UNKNOWN
}
ALU_PAIRS_PATTERN = '^[0-9]+\\s+[0-9]+$'
HBA_UID_PATTERN = "^\\s*HBA UID\\s+SP Name\\s+SPPort"
94 changes: 94 additions & 0 deletions delfin/drivers/dell_emc/vnx/vnx_block/navi_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# 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.
import re
import threading

import six
Expand Down Expand Up @@ -606,3 +607,96 @@ def navi_exe(self, command_str, host_ip=None):
raise e
finally:
self.session_lock.release()

def list_masking_views(self):
return self.get_resources_info(consts.GET_SG_LIST_HOST_API,
self.cli_sg_to_list)

def cli_sg_to_list(self, resource_info):
obj_list = []
obj_model = {}
try:
obj_infos = resource_info.split('\n')
pattern = re.compile(consts.ALU_PAIRS_PATTERN)
for obj_info in obj_infos:
str_line = obj_info.strip()
if str_line:
if ':' not in str_line:
search_obj = pattern.search(str_line)
if search_obj:
str_info = str_line.split()
lun_ids = obj_model.get('lun_ids')
if lun_ids:
lun_ids.add(str_info[1])
else:
lun_ids = set()
lun_ids.add(str_info[1])
obj_model['lun_ids'] = lun_ids
else:
str_info = self.split_str_by_colon(str_line)
if 'Host name:' in str_line:
host_names = obj_model.get('host_names')
if host_names:
host_names.add(str_info[1])
else:
host_names = set()
host_names.add(str_info[1])
obj_model['host_names'] = host_names
continue

obj_model = self.str_info_to_model(str_info, obj_model)

if str_line.startswith('Shareable:'):
obj_list = self.add_model_to_list(obj_model,
obj_list)
obj_model = {}
except Exception as e:
err_msg = "arrange sg info error: %s", six.text_type(e)
LOG.error(err_msg)
raise exception.InvalidResults(err_msg)
return obj_list

def list_hbas(self):
return self.get_resources_info(consts.GET_PORT_LIST_HBA_API,
self.cli_hba_to_list)

def cli_hba_to_list(self, resource_info):
obj_list = []
obj_model = {}
sp_name = ''
port_ids = set()
try:
obj_infos = resource_info.split('\n')
for obj_info in obj_infos:
str_line = obj_info.strip()
if str_line:
if 'Information about each HBA:' in obj_info:
if obj_model:
obj_model['port_ids'] = port_ids
obj_list = self.add_model_to_list(obj_model,
obj_list)
obj_model = {}
port_ids = set()
sp_name = ''
if ':' in obj_info:
str_info = self.split_str_by_colon(str_line)
obj_model = self.str_info_to_model(str_info, obj_model)
if 'SP Name:' in obj_info:
sp_name = obj_info.replace('SP Name:', '').replace(
'SP', '').replace('\r', '').replace(' ', '')
if 'SP Port ID:' in obj_info:
port_id = obj_info.replace('SP Port ID:',
'').replace('\r',
'').replace(
' ', '')
port_id = '%s-%s' % (sp_name, port_id)
port_ids.add(port_id)

if obj_model:
obj_model['port_ids'] = port_ids
obj_list.append(obj_model)
except Exception as e:
err_msg = "arrange host info error: %s", six.text_type(e)
LOG.error(err_msg)
raise exception.InvalidResults(err_msg)
return obj_list
9 changes: 9 additions & 0 deletions delfin/drivers/dell_emc/vnx/vnx_block/vnx_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,12 @@ def clear_alert(self, context, sequence_number):
@staticmethod
def get_access_url():
return 'https://{ip}'

def list_storage_host_initiators(self, context):
return self.com_handler.list_storage_host_initiators(self.storage_id)

def list_storage_hosts(self, context):
return self.com_handler.list_storage_hosts(self.storage_id)

def list_masking_views(self, context):
return self.com_handler.list_masking_views(self.storage_id)
88 changes: 88 additions & 0 deletions delfin/tests/unit/drivers/dell_emc/vnx/vnx_block/test_vnx_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,46 @@
I/O Module Type : SAS
"""

VIEW_DATAS = """
Storage Group Name: AIX_PowerHA_node2
Storage Group UID: 0B:33:4A:6E:81:38:EC:11:90:2B:00:60:16:63
HBA/SP Pairs:

HBA UID SP Name SPPort
------- ------- ------
20:00:00:00:C9:76:5E:79:10:00:00:00:C9:76:5E:79 SP A 6
Host name: AIX_21
20:00:00:00:C9:75:80:4C:10:00:00:00:C9:75:80:4C SP B 3
Host name: AIX_21

HLU/ALU Pairs:

HLU Number ALU Number
---------- ----------
1 335
Shareable: YES
"""
HBA_DATAS = """
Information about each HBA:

HBA UID: 20:00:00:00:C9:9B:57:79:10:00:00:00:C9:9B:57:79
Server Name: aix_ma
Server IP Address: 8.44.129.26
HBA Model Description:
HBA Vendor Description:
HBA Device Driver Name: N/A
Information about each port of this HBA:

SP Name: SP A
SP Port ID: 6
HBA Devicename: N/A
Trusted: NO
Logged In: NO
Defined: YES
Initiator Type: 3
StorageGroup Name: None
"""

AGENT_RESULT = {
'agent_rev': '7.33.1 (0.38)',
'name': 'K10',
Expand Down Expand Up @@ -520,6 +560,35 @@
'ipv6': None,
'ipv6_mask': None
}]
VIEW_RESULT = [
{
'native_masking_view_id': '0B:33:4A:6E:81:38:EC:11:90:2B:00:'
'60:16:63_AIX_21_335',
'name': 'AIX_PowerHA_node2',
'storage_id': '12345',
'native_storage_host_id': 'AIX_21',
'native_volume_id': '335'
}]
INITIATOR_RESULT = [
{
'name': '20:00:00:00:C9:9B:57:79:10:00:00:00:C9:9B:57:79',
'storage_id': '12345',
'native_storage_host_initiator_id': '20:00:00:00:C9:9B:57:79:10:'
'00:00:00:C9:9B:57:79',
'wwn': '20:00:00:00:C9:9B:57:79:10:00:00:00:C9:9B:57:79',
'type': 'fc',
'status': 'online',
'native_storage_host_id': 'aix_ma'
}]
HOST_RESULT = [
{
'name': 'aix_ma',
'storage_id': '12345',
'native_storage_host_id': 'aix_ma',
'os_type': 'Unknown',
'status': 'normal',
'ip_address': '8.44.129.26'
}]


def create_driver():
Expand Down Expand Up @@ -696,3 +765,22 @@ def test_get_ports(self):
BUS_PORT_DATAS, BUS_PORT_STATE_DATAS])
ports = self.driver.list_ports(context)
self.assertDictEqual(ports[0], PORT_RESULT[0])

def test_get_masking_views(self):
NaviClient.exec = mock.Mock(side_effect=[VIEW_DATAS])
views = self.driver.list_masking_views(context)
self.assertDictEqual(views[0], VIEW_RESULT[0])

def test_get_initiators(self):
NaviClient.exec = mock.Mock(side_effect=[HBA_DATAS,
IO_PORT_CONFIG_DATAS,
ISCSI_PORT_DATAS, PORT_DATAS,
BUS_PORT_DATAS,
BUS_PORT_STATE_DATAS])
initiators = self.driver.list_storage_host_initiators(context)
self.assertDictEqual(initiators[0], INITIATOR_RESULT[0])

def test_get_hosts(self):
NaviClient.exec = mock.Mock(side_effect=[HBA_DATAS])
hosts = self.driver.list_storage_hosts(context)
self.assertDictEqual(hosts[0], HOST_RESULT[0])