diff --git a/delfin/drivers/hpe/hpe_msa/__init__.py b/delfin/drivers/hpe/hpe_msa/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/delfin/drivers/hpe/hpe_msa/hpe_msastor.py b/delfin/drivers/hpe/hpe_msa/hpe_msastor.py
new file mode 100644
index 000000000..345c3b306
--- /dev/null
+++ b/delfin/drivers/hpe/hpe_msa/hpe_msastor.py
@@ -0,0 +1,43 @@
+from delfin.drivers import driver
+from delfin.drivers.hpe.hpe_msa import ssh_handler
+
+
+class HpeMsaStorDriver(driver.StorageDriver):
+
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ self.ssh_hanlder = ssh_handler.SSHHandler(**kwargs)
+
+ def reset_connection(self, context, **kwargs):
+ self.ssh_hanlder.login()
+
+ def get_storage(self, context):
+ return self.ssh_hanlder.get_storage(self.storage_id)
+
+ def list_storage_pools(self, context):
+ return self.ssh_hanlder.list_storage_pools(self.storage_id)
+
+ def list_volumes(self, context):
+ return self.ssh_hanlder.list_storage_volume(self.storage_id)
+
+ def list_controllers(self, context):
+ return self.ssh_hanlder.\
+ list_storage_controller(self.storage_id)
+
+ def list_ports(self, context):
+ return self.ssh_hanlder.list_storage_ports(self.storage_id)
+
+ def list_disks(self, context):
+ return self.ssh_hanlder.list_storage_disks(self.storage_id)
+
+ def list_alerts(self, context, query_para=None):
+ return self.ssh_hanlder.list_storage_error(query_para)
+
+ def add_trap_config(self, context, trap_config):
+ pass
+
+ def remove_trap_config(self, context, trap_config):
+ pass
+
+ def clear_alert(self):
+ pass
diff --git a/delfin/drivers/hpe/hpe_msa/ssh_handler.py b/delfin/drivers/hpe/hpe_msa/ssh_handler.py
new file mode 100644
index 000000000..845bcd4d4
--- /dev/null
+++ b/delfin/drivers/hpe/hpe_msa/ssh_handler.py
@@ -0,0 +1,370 @@
+import six
+
+from oslo_log import log as logging
+from delfin import exception
+from delfin.common import constants, alert_util
+from delfin.drivers.utils.ssh_client import SSHPool
+from delfin.drivers.utils.tools import Tools
+
+try:
+ import xml.etree.cElementTree as ET
+except ImportError:
+ import xml.etree.cElementTree as ET
+
+LOG = logging.getLogger(__name__)
+
+
+class SSHHandler():
+
+ SECONDS_TO_MS = 1000
+
+ DISK_PHYSICAL_TYPE = {
+ 'fc': constants.DiskPhysicalType.FC,
+ 'SAS': constants.DiskPhysicalType.SAS
+ }
+
+ def __init__(self, **kwargs):
+ self.ssh_pool = SSHPool(**kwargs)
+
+ def login(self):
+ try:
+ result = self.ssh_pool.do_exec('show pools')
+ if 'is not a recognized command' in result:
+ raise exception.InvalidIpOrPort()
+ except Exception as e:
+ LOG.error("Failed to login msa storwize_svc %s" %
+ (six.text_type(e)))
+ raise e
+
+ def get_storage(self, storage_id):
+ try:
+ result = self.ssh_pool.do_exec('show system')
+ list = self.analysisDataToList(result, 'system')
+ version_result = self.ssh_pool.do_exec('show version')
+ version_list = self.analysisDataToList(version_result,
+ 'versions')
+ if list:
+ pool_list = self.list_storage_pools(storage_id)
+ total_capacity = 0
+ for pool in pool_list:
+ total_capacity += int(pool['total_capacity'])
+ disk_list = self.list_storage_disks(storage_id)
+ disk_all = 0
+ for disk in disk_list:
+ disk_all += int(disk['capacity'])
+ volume_list = self.list_storage_volume(storage_id)
+ volume_all = 0
+ for volume in volume_list:
+ volume_all += int(volume['total_capacity'])
+ health = list[0]['health']
+ status = constants.StoragePoolStatus.OFFLINE
+ if health == 'OK':
+ status = constants.StoragePoolStatus.NORMAL
+ dataMap = {
+ 'name': list[0]['system-name'],
+ 'vendor': list[0]['vendor-name'],
+ 'model': list[0]['product-id'],
+ 'status': status,
+ 'serial_number': list[0]['midplane-serial-number'],
+ 'firmware_version': version_list[0]['bundle-version'],
+ 'location': list[0]['system-location'],
+ 'raw_capacity': int(disk_all),
+ 'total_capacity': int(total_capacity),
+ 'used_capacity': int(volume_all),
+ 'free_capacity': int(total_capacity - volume_all)
+ }
+ return dataMap
+ except Exception as e:
+ err_msg = "Failed to get storage : %s" % (six.text_type(e))
+ LOG.error(err_msg)
+ raise e
+
+ def list_storage_disks(self, storage_id):
+ try:
+ result = self.ssh_pool.do_exec('show disks')
+ list = self.analysisDataToList(result, 'drives')
+ list_disks = []
+ if list:
+ for data in list:
+ health = data['health']
+ status = constants.StoragePoolStatus.OFFLINE
+ if health == 'OK':
+ status = constants.StoragePoolStatus.NORMAL
+ size = self.parse_string(data['size'])
+ physical_type = SSHHandler.DISK_PHYSICAL_TYPE.\
+ get(data.get('description'),
+ constants.DiskPhysicalType.UNKNOWN)
+ data_map = {
+ 'native_disk_id': data['location'],
+ 'name': data['location'],
+ 'physical_type': physical_type,
+ 'status': status,
+ 'storage_id': storage_id,
+ 'native_disk_group_id': data['disk-group'],
+ 'serial_number': data['serial-number'],
+ 'manufacturer': data['vendor'],
+ 'model': data['model'],
+ 'speed': data['rpm'],
+ 'capacity': int(size),
+ 'health_score': status
+ }
+ list_disks.append(data_map)
+ return list_disks
+ except Exception as e:
+ err_msg = "Failed to get storage disk: %s" % (six.text_type(e))
+ LOG.error(err_msg)
+ raise e
+
+ def list_storage_ports(self, storage_id):
+ try:
+ result = self.ssh_pool.do_exec('show ports')
+ result_info = result.split('\n')
+ result_list = result_info[1:len(result_info) - 1]
+ result_xml = ''.join(result_list)
+ root_elem = ET.fromstring(result_xml)
+ list_port_detail = []
+ for ch in root_elem.iter("OBJECT"):
+ propertyName = ch.get('basetype')
+ if propertyName != 'status':
+ msg = {}
+ for child in ch.iter("PROPERTY"):
+ msg[child.get('name')] = child.text
+ list_port_detail.append(msg)
+ list_all_ports = []
+ for i in range(0, len(list_port_detail) - 1, 2):
+ aa = list_port_detail[i].copy()
+ aa.update(list_port_detail[i + 1])
+ list_all_ports.append(aa)
+ list_ports = []
+ if list_all_ports:
+ for data in list_all_ports:
+ status = constants.PortHealthStatus.NORMAL
+ conn_status = constants.PortConnectionStatus.CONNECTED
+ if data.get('health') != 'OK':
+ status = constants.PortHealthStatus.ABNORMAL
+ conn_status = constants.PortConnectionStatus.\
+ DISCONNECTED
+ location = '%s_%s' % (data.get('port'),
+ data.get('durable-id'))
+ port_type = constants.PortType.FC
+ if data.get('port-type') == 'iSCSI':
+ port_type = constants.PortType.ETH
+ speed = data.get('configured-speed', None)
+ max_speed = 0
+ if speed != 'Auto' and speed is not None:
+ max_speed = self.parse_string(speed)
+ speed = data.get('configured-speed')
+ dataMap = {
+ 'native_port_id': data.get('durable-id', ''),
+ 'name': data.get('port', ''),
+ 'type': port_type,
+ 'connection_status': conn_status,
+ 'health_status': status,
+ 'location': location,
+ 'storage_id': storage_id,
+ 'speed': max_speed,
+ 'max_speed': max_speed,
+ 'wwn': '',
+ 'mac_address': data.get('mac-address', ''),
+ 'ipv4': data.get('ip-address', '')
+ }
+ list_ports.append(dataMap)
+ return list_ports
+ except Exception as e:
+ err_msg = "Failed to get storage ports: %s" % (six.text_type(e))
+ LOG.error(err_msg)
+ raise e
+
+ def list_storage_controller(self, storage_id):
+ try:
+ result = self.ssh_pool.do_exec('show controllers')
+ list = self.analysisDataToList(result, 'controllers')
+ list_controllers = []
+ if list:
+ for data in list:
+ health = data.get('health', '')
+ status = constants.StoragePoolStatus.OFFLINE
+ if health == 'OK':
+ status = constants.StoragePoolStatus.NORMAL
+ cpu_info = str(data['sc-cpu-type'])
+ memory_size = data['system-memory-size'] + "MB"
+ system_memory_size = self.parse_string(
+ memory_size)
+ data_map = {
+ 'native_controller_id': data['controller-id'],
+ 'name': data['durable-id'],
+ 'storage_id': storage_id,
+ 'status': status,
+ 'location': data['position'],
+ 'soft_version': data['sc-fw'],
+ 'cpu_info': cpu_info,
+ 'memory_size': int(system_memory_size)
+ }
+ list_controllers.append(data_map)
+ return list_controllers
+ except Exception as e:
+ err_msg = "Failed to get storage controllers: %s"\
+ % (six.text_type(e))
+ LOG.error(err_msg)
+ raise e
+
+ def list_storage_volume(self, storage_id):
+ try:
+ result = self.ssh_pool.do_exec('show volumes')
+ list = self.analysisDataToList(result, 'volumes')
+ result_pools = self.ssh_pool.do_exec('show pools')
+ list_pools = self.analysisDataToList(result_pools, 'pools')
+ list_volumes = []
+ if list:
+ for data in list:
+ health = data.get('health', '')
+ status = constants.StoragePoolStatus.OFFLINE
+ if health == 'OK':
+ status = constants.StoragePoolStatus.NORMAL
+ total_size = self.parse_string(data['total-size'])
+ total_avail = self.parse_string(data['allocated-size'])
+ native_storage_pool_id = list_pools[0].\
+ get("serial-number")
+ for pools in list_pools:
+ if data.get("virtual-disk-name") == pools.\
+ get("name"):
+ native_storage_pool_id = pools.\
+ get("serial-number")
+ dataMap = {
+ 'name': data['volume-name'],
+ 'storage_id': storage_id,
+ 'description': data['volume-name'],
+ 'status': status,
+ 'native_volume_id': str(data['durable-id']),
+ 'native_storage_pool_id': native_storage_pool_id,
+ 'wwn': str(data['wwn']),
+ 'type': data['volume-type'],
+ 'total_capacity': int(total_size),
+ 'free_capacit': int(total_avail),
+ 'used_capacity': int(total_size - total_avail),
+ 'blocks': int(data.get("blocks")),
+ 'compressed': True,
+ 'deduplicated': True
+ }
+ list_volumes.append(dataMap)
+ return list_volumes
+ except Exception as e:
+ err_msg = "Failed to get storage volume: %s" % (six.text_type(e))
+ LOG.error(err_msg)
+ raise e
+
+ def list_storage_pools(self, storage_id):
+ try:
+ result = self.ssh_pool.do_exec('show pools')
+ list = self.analysisDataToList(result, 'pools')
+ volume_list = self.list_storage_volume(storage_id)
+ pools_list = []
+ if list:
+ for data in list:
+ volume_size = 0
+ blocks = 0
+ for volume in volume_list:
+ if volume.get("native_storage_pool_id") == data.\
+ get("serial-number"):
+ volume_size += volume.get("total_capacity")
+ blocks += volume.get("blocks")
+ health = data.get('health', '')
+ status = constants.StoragePoolStatus.OFFLINE
+ if health == 'OK':
+ status = constants.StoragePoolStatus.NORMAL
+ total_size = self.parse_string(data['total-size'])
+ dataMap = {
+ 'name': data['name'],
+ 'storage_id': storage_id,
+ 'native_storage_pool_id': data['serial-number'],
+ 'description': '',
+ 'status': status,
+ 'storage_type': constants.StorageType.BLOCK,
+ 'total_capacity': int(total_size),
+ 'subscribed_capacity': int(blocks),
+ 'used_capacity': volume_size,
+ 'free_capacity': int(total_size - volume_size)
+ }
+ pools_list.append(dataMap)
+ return pools_list
+ except Exception as e:
+ err_msg = "Failed to get storage pool: %s" % (six.text_type(e))
+ LOG.error(err_msg)
+ raise e
+
+ def parse_string(self, value):
+ capacity = 0
+ if value:
+ if value.isdigit():
+ capacity = float(value)
+ else:
+ if value == '0B':
+ capacity = 0
+ else:
+ unit = value[-2:]
+ capacity = float(value[:-2]) * int(
+ Tools.change_capacity_to_bytes(unit))
+ return capacity
+
+ def analysisDataToList(self, result, dataName):
+ list_xml = []
+ result_info = result.split('\n')
+ result_list = result_info[1:len(result_info) - 1]
+ result_xml = ''.join(result_list)
+ root_elem = ET.fromstring(result_xml)
+ for ch in root_elem.iter("OBJECT"):
+ propertyName = ch.get('basetype')
+ if dataName == propertyName:
+ msg = {}
+ for child in ch.iter("PROPERTY"):
+ msg[child.get('name')] = child.text
+ list_xml.append(msg)
+ return list_xml
+
+ def list_storage_error(self, query_para):
+ alert_list = []
+ try:
+ result = self.ssh_pool.do_exec('show events error')
+ list = self.analysisDataToList(result, 'events')
+ if list:
+ for alert_map in list:
+ time_stamp = alert_map.get('time-stamp-numeric')
+ occur_time = int(time_stamp) * self.SECONDS_TO_MS
+ if not alert_util.is_alert_in_time_range(query_para,
+ occur_time):
+ continue
+ alert_name = alert_map.get('message', '')
+ event_id = alert_map.get('event-id')
+ location = alert_map.get('serial-number', '')
+ resource_type = alert_map.get('event-code', '')
+ severity = alert_map.get('severity')
+ if severity == 'Informational' or severity is None:
+ continue
+ alert_model = {
+ 'alert_id': event_id,
+ 'alert_name': alert_name,
+ 'severity': severity,
+ 'category': constants.Category.FAULT,
+ 'type': 'EquipmentAlarm',
+ 'sequence_number': alert_map.get('event-code'),
+ 'occur_time': occur_time,
+ 'description': alert_name,
+ 'resource_type': resource_type,
+ 'location': location
+ }
+ alert_list.append(alert_model)
+ return self.distinct2(alert_list)
+ except Exception as e:
+ err_msg = "Failed to get storage error: %s" % (six.text_type(e))
+ LOG.error(err_msg)
+ raise e
+
+ def distinct2(self, result_list):
+ exist_questions = set()
+ result = []
+ for item in result_list:
+ question = item['resource_type']
+ if question not in exist_questions:
+ exist_questions.add(question)
+ result.append(item)
+ return result
diff --git a/delfin/tests/unit/drivers/hpe/hpe_msa/__init__.py b/delfin/tests/unit/drivers/hpe/hpe_msa/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/delfin/tests/unit/drivers/hpe/hpe_msa/test_constans.py b/delfin/tests/unit/drivers/hpe/hpe_msa/test_constans.py
new file mode 100644
index 000000000..b8843d8fd
--- /dev/null
+++ b/delfin/tests/unit/drivers/hpe/hpe_msa/test_constans.py
@@ -0,0 +1,543 @@
+LIST_CONTROLLERS = """
+
+
+
+
+
+
+
+"""
+
+LIST_SYSTEM = """
+
+
+
+
+
+"""
+
+LIST_VISION = """
+
+
+
+
+
+"""
+
+LIST_PORTS = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+LIST_POOLS = """
+
+
+
+
+
+"""
+
+LIST_VOLUMES = """
+
+
+
+
+
+
+
+"""
+
+LIST_DISKS = """
+
+
+
+
+
+
+
+
+
+
+
+"""
+LIST_ERROR = """
+
+
+
+
+
+"""
+
+error_result = [
+ {
+ 'alert_id': 'A35',
+ 'alert_name': 'ERROE_INFOMATION',
+ 'severity': 'ERROR',
+ 'category': 'Fault',
+ 'type': 'EquipmentAlarm',
+ 'sequence_number': '557',
+ 'occur_time': 1630305906000,
+ 'description': 'ERROE_INFOMATION',
+ 'resource_type': '557',
+ 'location': '00C0FF26C236'
+ }
+]
+
+
+volume_result = [
+ {
+ 'name': 'Vol0001',
+ 'storage_id': 'kkk',
+ 'description': 'Vol0001',
+ 'status': 'normal',
+ 'native_volume_id': 'V1',
+ 'native_storage_pool_id': '00c0ff26c4ea0000d980546101000000',
+ 'wwn': '600C0FF00026C4EAFA80546101000000',
+ 'type': 'base',
+ 'total_capacity': 107266808217,
+ 'free_capacit': 0,
+ 'used_capacity': 107266808217,
+ 'blocks': 195305472,
+ 'compressed': True,
+ 'deduplicated': True
+ }, {
+ 'name': 'Vol0002',
+ 'storage_id': 'kkk',
+ 'description': 'Vol0002',
+ 'status': 'normal',
+ 'native_volume_id': 'V2',
+ 'native_storage_pool_id': '00c0ff26c4ea0000d980546101000000',
+ 'wwn': '600C0FF00026C4EA0A81546101000000',
+ 'type': 'base',
+ 'total_capacity': 107266808217,
+ 'free_capacit': 0,
+ 'used_capacity': 107266808217,
+ 'blocks': 195305472,
+ 'compressed': True,
+ 'deduplicated': True
+ }
+]
+
+pools_result = [
+ {
+ 'name': 'A',
+ 'storage_id': 'kkk',
+ 'native_storage_pool_id': '00c0ff26c4ea0000d980546101000000',
+ 'description': '',
+ 'status': 'normal',
+ 'storage_type': 'block',
+ 'total_capacity': 1285054214963,
+ 'subscribed_capacity': 390610944,
+ 'used_capacity': 214533616434,
+ 'free_capacity': 1070520598529
+ }
+]
+
+ports_result = [
+ {'native_port_id': 'hostport_A1',
+ 'name': 'A1',
+ 'type': 'fc',
+ 'connection_status': 'disconnected',
+ 'health_status': 'abnormal',
+ 'location': 'A1_hostport_A1',
+ 'storage_id': 'kkk',
+ 'speed': 8589934592.0,
+ 'max_speed': 8589934592.0,
+ 'wwn': '',
+ 'mac_address': '',
+ 'ipv4': ''
+ }, {
+ 'native_port_id': 'hostport_A2',
+ 'name': 'A2',
+ 'type': 'fc',
+ 'connection_status': 'disconnected',
+ 'health_status': 'abnormal',
+ 'location': 'A2_hostport_A2',
+ 'storage_id': 'kkk',
+ 'speed': 8589934592.0,
+ 'max_speed': 8589934592.0,
+ 'wwn': '',
+ 'mac_address': '',
+ 'ipv4': ''
+ }, {
+ 'native_port_id': 'hostport_A3',
+ 'name': 'A3',
+ 'type': 'eth',
+ 'connection_status': 'disconnected',
+ 'health_status': 'abnormal',
+ 'location': 'A3_hostport_A3',
+ 'storage_id': 'kkk',
+ 'speed': 0,
+ 'max_speed': 0,
+ 'wwn': '',
+ 'mac_address': '00:C0:FF:35:BD:64',
+ 'ipv4': '0.0.0.0'
+ }, {
+ 'native_port_id': 'hostport_A4',
+ 'name': 'A4',
+ 'type': 'eth',
+ 'connection_status': 'disconnected',
+ 'health_status': 'abnormal',
+ 'location': 'A4_hostport_A4',
+ 'storage_id': 'kkk',
+ 'speed': 0,
+ 'max_speed': 0,
+ 'wwn': '',
+ 'mac_address': '00:C0:FF:35:BD:65',
+ 'ipv4': '0.0.0.0'
+ }, {
+ 'native_port_id': 'hostport_B1',
+ 'name': 'B1',
+ 'type': 'fc',
+ 'connection_status': 'disconnected',
+ 'health_status': 'abnormal',
+ 'location': 'B1_hostport_B1',
+ 'storage_id': 'kkk',
+ 'speed': 8589934592.0,
+ 'max_speed': 8589934592.0,
+ 'wwn': '',
+ 'mac_address': '',
+ 'ipv4': ''
+ }, {
+ 'native_port_id': 'hostport_B2',
+ 'name': 'B2',
+ 'type': 'fc',
+ 'connection_status': 'disconnected',
+ 'health_status': 'abnormal',
+ 'location': 'B2_hostport_B2',
+ 'storage_id': 'kkk',
+ 'speed': 8589934592.0,
+ 'max_speed': 8589934592.0,
+ 'wwn': '',
+ 'mac_address': '',
+ 'ipv4': ''
+ }, {
+ 'native_port_id': 'hostport_B3',
+ 'name': 'B3',
+ 'type': 'eth',
+ 'connection_status': 'disconnected',
+ 'health_status': 'abnormal',
+ 'location': 'B3_hostport_B3',
+ 'storage_id': 'kkk',
+ 'speed': 0,
+ 'max_speed': 0,
+ 'wwn': '',
+ 'mac_address': '00:C0:FF:35:BA:BC',
+ 'ipv4': '0.0.0.0'
+ }, {
+ 'native_port_id': 'hostport_B4',
+ 'name': 'B4',
+ 'type': 'eth',
+ 'connection_status': 'disconnected',
+ 'health_status': 'abnormal',
+ 'location': 'B4_hostport_B4',
+ 'storage_id': 'kkk',
+ 'speed': 0,
+ 'max_speed': 0,
+ 'wwn': '',
+ 'mac_address': '00:C0:FF:35:BA:BD',
+ 'ipv4': '0.0.0.0'
+ }
+]
+
+disks_result = [
+ {
+ 'native_disk_id': '1.1',
+ 'name': '1.1',
+ 'physical_type': 'sas',
+ 'status': 'normal',
+ 'storage_id': 'kkk',
+ 'native_disk_group_id': 'dgA01',
+ 'serial_number': '6SL9CD560000N51404EF',
+ 'manufacturer': 'SEAGATE',
+ 'model': 'ST3600057SS',
+ 'speed': '15',
+ 'capacity': 644352468582,
+ 'health_score': 'normal'
+ }, {
+ 'native_disk_id': '1.2',
+ 'name': '1.2',
+ 'physical_type': 'sas',
+ 'status': 'normal',
+ 'storage_id': 'kkk',
+ 'native_disk_group_id': 'dgA01',
+ 'serial_number': '6SL7X4RE0000B42601SF',
+ 'manufacturer': 'SEAGATE',
+ 'model': 'ST3600057SS',
+ 'speed': '15',
+ 'capacity': 644352468582,
+ 'health_score': 'normal'
+ }, {
+ 'native_disk_id': '1.3',
+ 'name': '1.3',
+ 'physical_type': 'sas',
+ 'status': 'normal',
+ 'storage_id': 'kkk',
+ 'native_disk_group_id': 'dgA01',
+ 'serial_number': '6SL9QR5T0000N52120SK',
+ 'manufacturer': 'SEAGATE',
+ 'model': 'ST3600057SS',
+ 'speed': '15', 'capacity': 644352468582,
+ 'health_score': 'normal'
+ }, {
+ 'native_disk_id': '1.4',
+ 'name': '1.4',
+ 'physical_type': 'sas',
+ 'status': 'normal',
+ 'storage_id': 'kkk',
+ 'native_disk_group_id': 'dgA01',
+ 'serial_number': '3SL0WT7G00009051YBTF',
+ 'manufacturer': 'SEAGATE',
+ 'model': 'ST3600057SS',
+ 'speed': '15',
+ 'capacity': 644352468582,
+ 'health_score': 'normal'
+ }
+]
+
+system_info = {
+ 'name': 'msa2040',
+ 'vendor': 'HP',
+ 'model': 'MSA 2040 SAN',
+ 'status': 'normal',
+ 'serial_number': '00C0FF26DCB0',
+ 'firmware_version': 'GL210R004',
+ 'location': 'Uninitialized Location',
+ 'raw_capacity': 2577409874328,
+ 'total_capacity': 1285054214963,
+ 'used_capacity': 214533616434,
+ 'free_capacity': 1070520598529
+}
+
+controller_result = [
+ {
+ 'native_controller_id': 'A',
+ 'name': 'controller_a',
+ 'storage_id': 'kkk',
+ 'status': 'normal',
+ 'location': 'Top',
+ 'soft_version': 'GLS210R04-01',
+ 'cpu_info': 'Gladden',
+ 'memory_size': 6442450944
+ },
+ {
+ 'native_controller_id': 'B',
+ 'name': 'controller_b',
+ 'storage_id': 'kkk',
+ 'status': 'normal',
+ 'location': 'Bottom',
+ 'soft_version': 'GLS210R04-01',
+ 'cpu_info': 'Gladden',
+ 'memory_size': 6442450944
+ }
+]
diff --git a/delfin/tests/unit/drivers/hpe/hpe_msa/test_hpe_msastor.py b/delfin/tests/unit/drivers/hpe/hpe_msa/test_hpe_msastor.py
new file mode 100644
index 000000000..8ea1c6daa
--- /dev/null
+++ b/delfin/tests/unit/drivers/hpe/hpe_msa/test_hpe_msastor.py
@@ -0,0 +1,101 @@
+import sys
+import paramiko
+
+from delfin import context
+from unittest import TestCase, mock
+from delfin.tests.unit.drivers.hpe.hpe_msa import test_constans
+from delfin.drivers.utils.ssh_client import SSHPool
+from delfin.drivers.hpe.hpe_msa.ssh_handler import SSHHandler
+from delfin.drivers.hpe.hpe_msa.hpe_msastor import HpeMsaStorDriver
+
+sys.modules['delfin.cryptor'] = mock.Mock()
+
+ACCESS_INFO = {
+ "storage_id": "kkk",
+ "ssh": {
+ "host": "110.143.132.231",
+ "port": 22,
+ "username": "user",
+ "password": "pass",
+ "pub_key": "ddddddddddddddddddddddddd"
+ }
+}
+
+
+class Request:
+ def __init__(self):
+ self.environ = {'delfin.context': context.RequestContext()}
+ pass
+
+
+class TestHpeMsaStorageDriver(TestCase):
+
+ @mock.patch.object(SSHPool, 'do_exec')
+ @mock.patch.object(SSHPool, 'get')
+ def test_list_ports(self, mock_ssh_get, mock_control):
+ mock_ssh_get.return_value = {paramiko.SSHClient()}
+ mock_control.side_effect = [test_constans.LIST_PORTS]
+ ports = HpeMsaStorDriver(**ACCESS_INFO).list_ports(context)
+ self.assertEqual(ports, test_constans.ports_result)
+
+ @mock.patch.object(SSHPool, 'do_exec')
+ @mock.patch.object(SSHPool, 'get')
+ def test_list_disks(self, mock_ssh_get, mock_control):
+ mock_ssh_get.return_value = {paramiko.SSHClient()}
+ mock_control.side_effect = [test_constans.LIST_DISKS]
+ disks = HpeMsaStorDriver(**ACCESS_INFO).list_disks(context)
+ self.assertEqual(disks, test_constans.disks_result)
+
+ @mock.patch.object(SSHPool, 'do_exec')
+ @mock.patch.object(SSHPool, 'get')
+ def test_list_controllers(self, mock_ssh_get, mock_control):
+ mock_ssh_get.return_value = {paramiko.SSHClient()}
+ mock_control.side_effect = [test_constans.LIST_CONTROLLERS]
+ controller = HpeMsaStorDriver(**ACCESS_INFO).\
+ list_controllers(context)
+ self.assertEqual(controller, test_constans.controller_result)
+
+ @mock.patch.object(SSHPool, 'do_exec')
+ @mock.patch.object(SSHPool, 'get')
+ def test_list_volumes(self, mock_ssh_get, mock_control):
+ mock_ssh_get.return_value = {paramiko.SSHClient()}
+ mock_control.side_effect = [test_constans.LIST_VOLUMES,
+ test_constans.LIST_POOLS]
+ volumes = HpeMsaStorDriver(**ACCESS_INFO).list_volumes(context)
+ self.assertEqual(volumes, test_constans.volume_result)
+
+ @mock.patch.object(SSHPool, 'do_exec')
+ @mock.patch.object(SSHPool, 'get')
+ @mock.patch.object(SSHHandler, 'list_storage_pools')
+ @mock.patch.object(SSHHandler, 'list_storage_disks')
+ @mock.patch.object(SSHHandler, 'list_storage_volume')
+ def test_list_storage(self, mock_system, mock_ssh_get,
+ mock_pools, mock_disks, mock_volume):
+ mock_volume.side_effect = [test_constans.LIST_SYSTEM,
+ test_constans.LIST_VISION]
+ mock_disks.return_value = {paramiko.SSHClient()}
+ mock_pools.side_effect = [test_constans.pools_result]
+ mock_ssh_get.side_effect = [test_constans.disks_result]
+ mock_system.side_effect = [test_constans.volume_result]
+ system = HpeMsaStorDriver(**ACCESS_INFO).get_storage(context)
+ self.assertEqual(system, test_constans.system_info)
+
+ @mock.patch.object(SSHPool, 'do_exec')
+ @mock.patch.object(SSHPool, 'get')
+ @mock.patch.object(SSHHandler, 'list_storage_volume')
+ def test_list_storage_pools(self, mock_ssh_get, mock_control,
+ mock_volume):
+ mock_ssh_get.return_value = test_constans.volume_result
+ mock_control.side_effect = {paramiko.SSHClient()}
+ mock_volume.side_effect = [test_constans.LIST_POOLS]
+ pools = HpeMsaStorDriver(**ACCESS_INFO).list_storage_pools(context)
+ self.assertEqual(pools, test_constans.pools_result)
+
+ @mock.patch.object(SSHPool, 'do_exec')
+ @mock.patch.object(SSHPool, 'get')
+ def test_list_alerts(self, mock_ssh_get, mock_control):
+ query_para = None
+ mock_ssh_get.return_value = {paramiko.SSHClient()}
+ mock_control.side_effect = [test_constans.LIST_ERROR]
+ alerts = HpeMsaStorDriver(**ACCESS_INFO).list_alerts(query_para)
+ self.assertEqual(alerts, test_constans.error_result)