def send_iter_request(self, api_name, api_args=None, enable_tunneling=True, max_page_length=DEFAULT_MAX_PAGE_LENGTH): """Invoke an iterator-style getter API.""" if not api_args: api_args = {} api_args['max-records'] = max_page_length # Get first page result = self.send_request(api_name, api_args, enable_tunneling=enable_tunneling) # Most commonly, we can just return here if there is no more data next_tag = result.get_child_content('next-tag') if not next_tag: return result # Ensure pagination data is valid and prepare to store remaining pages num_records = self._get_record_count(result) attributes_list = result.get_child_by_name('attributes-list') if not attributes_list: msg = _('Missing attributes list for API %s.') % api_name raise exception.NetAppDriverException(msg) # Get remaining pages, saving data into first page while next_tag is not None: next_api_args = copy.deepcopy(api_args) next_api_args['tag'] = next_tag next_result = self.send_request(api_name, next_api_args, enable_tunneling=enable_tunneling) next_attributes_list = next_result.get_child_by_name( 'attributes-list') or netapp_api.NaElement('none') for record in next_attributes_list.get_children(): attributes_list.add_child_elem(record) num_records += self._get_record_count(next_result) next_tag = next_result.get_child_content('next-tag') result.get_child_by_name('num-records').set_content( six.text_type(num_records)) result.get_child_by_name('next-tag').set_content('') return result
def test_setter_child_dict(self): """Tests dict is appended as child to root.""" root = netapp_api.NaElement('root') root['d'] = {'e1': 'v1', 'e2': 'v2'} e1 = root.get_child_by_name('d') self.assertIsInstance(e1, netapp_api.NaElement) sub_ch = e1.get_children() self.assertEqual(2, len(sub_ch)) for c in sub_ch: self.assertIn(c.get_name(), ['e1', 'e2']) if c.get_name() == 'e1': self.assertEqual('v1', c.get_content()) else: self.assertEqual('v2', c.get_content())
def test_get_snapshot_if_snapshot_present_busy(self): expected_vol_name = fake.SNAPSHOT['volume_id'] expected_snapshot_name = fake.SNAPSHOT['name'] response = netapp_api.NaElement( fake_client.SNAPSHOT_INFO_FOR_PRESENT_BUSY_SNAPSHOT_7MODE) self.connection.invoke_successfully.return_value = response snapshot = self.client.get_snapshot(expected_vol_name, expected_snapshot_name) self.assertEqual(expected_vol_name, snapshot['volume']) self.assertEqual(expected_snapshot_name, snapshot['name']) self.assertEqual(set([]), snapshot['owners']) self.assertTrue(snapshot['busy'])
def test_get_lun_by_args(self): response = netapp_api.NaElement( etree.XML("""<results status="passed"> <num-records>2</num-records> <attributes-list> <lun-info> </lun-info> </attributes-list> </results>""")) self.connection.invoke_successfully.return_value = response lun = self.client.get_lun_by_args() self.assertEqual(1, len(lun))
def test_get_vol_by_junc_vserver_not_found(self): fake_vserver = 'fake_vserver' fake_junc = 'fake_junction_path' response = netapp_api.NaElement( etree.XML("""<results status="passed"> <num-records>0</num-records> <attributes-list> </attributes-list> </results>""")) self.connection.invoke_successfully.return_value = response self.assertRaises(exception.NotFound, self.client.get_vol_by_junc_vserver, fake_vserver, fake_junc)
def test_create_node_with_children(self): """Tests adding a child node with its own children""" root = netapp_api.NaElement('root') self.mock_object(root, 'add_new_child', return_value='abc') result_xml = str(root.create_node_with_children( 'options', test1=zapi_fakes.FAKE_XML_STR, test2=zapi_fakes.FAKE_XML_STR)) # No ordering is guaranteed for elements in this XML. self.assertTrue(result_xml.startswith("<options>"), result_xml) self.assertTrue("<test1>abc</test1>" in result_xml, result_xml) self.assertTrue("<test2>abc</test2>" in result_xml, result_xml) self.assertTrue(result_xml.rstrip().endswith("</options>"), result_xml)
def clone_lun(self, volume, name, new_name, space_reserved='true', qos_policy_group_name=None, src_block=0, dest_block=0, block_count=0): # zAPI can only handle 2^24 blocks per range bc_limit = 2 ** 24 # 8GB # zAPI can only handle 32 block ranges per call br_limit = 32 z_limit = br_limit * bc_limit # 256 GB z_calls = int(math.ceil(block_count / float(z_limit))) zbc = block_count if z_calls == 0: z_calls = 1 for _call in range(0, z_calls): if zbc > z_limit: block_count = z_limit zbc -= z_limit else: block_count = zbc clone_create = netapp_api.NaElement.create_node_with_children( 'clone-create', **{'volume': volume, 'source-path': name, 'destination-path': new_name, 'space-reserve': space_reserved}) if qos_policy_group_name is not None: clone_create.add_new_child('qos-policy-group-name', qos_policy_group_name) if block_count > 0: block_ranges = netapp_api.NaElement("block-ranges") segments = int(math.ceil(block_count / float(bc_limit))) bc = block_count for _segment in range(0, segments): if bc > bc_limit: block_count = bc_limit bc -= bc_limit else: block_count = bc block_range =\ netapp_api.NaElement.create_node_with_children( 'block-range', **{'source-block-number': six.text_type(src_block), 'destination-block-number': six.text_type(dest_block), 'block-count': six.text_type(block_count)}) block_ranges.add_child_elem(block_range) src_block += int(block_count) dest_block += int(block_count) clone_create.add_child_elem(block_ranges) self.connection.invoke_successfully(clone_create, True)
def test_find_mapped_lun_igroup_no_igroups(self): response = netapp_api.NaElement( etree.XML(""" <results status="passed"> <initiator-groups /> </results>""")) initiators = fake.FC_FORMATTED_INITIATORS self.zapi_client.get_lun_map.return_value = response (igroup, lun_id) = self.library._find_mapped_lun_igroup('path', initiators) self.assertIsNone(igroup) self.assertIsNone(lun_id)
def get_igroup_by_initiators(self, initiator_list): """Get igroups exactly matching a set of initiators.""" igroup_list = [] if not initiator_list: return igroup_list initiator_set = set(initiator_list) igroup_list_info = netapp_api.NaElement('igroup-list-info') result = self.connection.invoke_successfully(igroup_list_info, True) initiator_groups = result.get_child_by_name( 'initiator-groups') or netapp_api.NaElement('none') for initiator_group_info in initiator_groups.get_children(): initiator_set_for_igroup = set() initiators = initiator_group_info.get_child_by_name( 'initiators') or netapp_api.NaElement('none') for initiator_info in initiators.get_children(): initiator_set_for_igroup.add( initiator_info.get_child_content('initiator-name')) if initiator_set == initiator_set_for_igroup: igroup = { 'initiator-group-os-type': initiator_group_info.get_child_content( 'initiator-group-os-type'), 'initiator-group-type': initiator_group_info.get_child_content( 'initiator-group-type'), 'initiator-group-name': initiator_group_info.get_child_content( 'initiator-group-name') } igroup_list.append(igroup) return igroup_list
def test_get_lun_map_multiple_pages(self): path = '/vol/%s/%s' % (self.fake_volume, self.fake_lun) expected_lun_map = { "initiator-group": "igroup", "lun-id": "1337", "vserver": "vserver", } response = netapp_api.NaElement( etree.XML("""<results status="passed"> <num-records>1</num-records> <attributes-list> <lun-map-info> <lun-id>%(lun-id)s</lun-id> <initiator-group>%(initiator-group)s</initiator-group> <vserver>%(vserver)s</vserver> </lun-map-info> </attributes-list> <next-tag>blah</next-tag> </results>""" % expected_lun_map)) response_2 = netapp_api.NaElement( etree.XML("""<results status="passed"> <num-records>1</num-records> <attributes-list> <lun-map-info> <lun-id>%(lun-id)s</lun-id> <initiator-group>%(initiator-group)s</initiator-group> <vserver>%(vserver)s</vserver> </lun-map-info> </attributes-list> </results>""" % expected_lun_map)) self.connection.invoke_successfully.side_effect = [ response, response_2 ] lun_map = self.client.get_lun_map(path) self.assertEqual([expected_lun_map, expected_lun_map], lun_map)
def test_clone_lun_api_error(self): fake_clone_start = netapp_api.NaElement( etree.XML("""<results status="passed"> <clone-id> <clone-id-info> <clone-op-id>1337</clone-op-id> <volume-uuid>volume-uuid</volume-uuid> </clone-id-info> </clone-id> </results>""")) fake_clone_status = netapp_api.NaElement( etree.XML("""<results status="passed"> <status> <ops-info> <clone-state>error</clone-state> </ops-info> </status> </results>""")) self.connection.invoke_successfully.side_effect = [fake_clone_start, fake_clone_status] self.assertRaises(netapp_api.NaApiError, self.client.clone_lun, 'path', 'new_path', 'fakeLUN', 'newFakeLUN')
def _check_clone_status(self, clone_id, vol_uuid, name, new_name): """Checks for the job till completed.""" clone_status = netapp_api.NaElement('clone-list-status') cl_id = netapp_api.NaElement('clone-id') clone_status.add_child_elem(cl_id) cl_id.add_node_with_children('clone-id-info', **{'clone-op-id': clone_id, 'volume-uuid': vol_uuid}) running = True clone_ops_info = None while running: result = self.connection.invoke_successfully(clone_status, True) status = result.get_child_by_name('status') ops_info = status.get_children() if ops_info: for info in ops_info: if info.get_child_content('clone-state') == 'running': time.sleep(1) break else: running = False clone_ops_info = info break else: if clone_ops_info: fmt = {'name': name, 'new_name': new_name} if clone_ops_info.get_child_content('clone-state')\ == 'completed': LOG.debug("Clone operation with src %(name)s" " and dest %(new_name)s completed", fmt) else: LOG.debug("Clone operation with src %(name)s" " and dest %(new_name)s failed", fmt) raise netapp_api.NaApiError( clone_ops_info.get_child_content('error'), clone_ops_info.get_child_content('reason'))
def test_get_performance_instance_names(self): mock_send_request = self.mock_object(self.client, 'send_request') mock_send_request.return_value = netapp_api.NaElement( fake_client.PERF_OBJECT_INSTANCE_LIST_INFO_RESPONSE) result = self.client.get_performance_instance_names('processor') expected = ['processor0', 'processor1'] self.assertEqual(expected, result) perf_object_instance_list_info_args = {'objectname': 'processor'} mock_send_request.assert_called_once_with( 'perf-object-instance-list-info', perf_object_instance_list_info_args, enable_tunneling=False)
def test_clone_lun(self): fake_clone_start = netapp_api.NaElement( etree.XML("""<results status="passed"> <clone-id> <clone-id-info> <clone-op-id>1337</clone-op-id> <volume-uuid>volume-uuid</volume-uuid> </clone-id-info> </clone-id> </results>""")) fake_clone_status = netapp_api.NaElement( etree.XML("""<results status="passed"> <status> <ops-info> <clone-state>completed</clone-state> </ops-info> </status> </results>""")) self.connection.invoke_successfully.side_effect = [fake_clone_start, fake_clone_status] self.client.clone_lun('path', 'new_path', 'fakeLUN', 'newFakeLUN') self.assertEqual(2, self.connection.invoke_successfully.call_count)
def test_get_if_info_by_ip(self, mock_resolve_hostname): fake_ip = '192.168.1.101' response = netapp_api.NaElement( etree.XML("""<results status="passed"> <num-records>1</num-records> <attributes-list> <net-interface-info> </net-interface-info> </attributes-list> </results>""")) self.connection.invoke_successfully.return_value = response results = self.client.get_if_info_by_ip(fake_ip) self.assertEqual(1, len(results))
def test_get_lun_list_with_multiple_pages(self): response = netapp_api.NaElement( etree.XML("""<results status="passed"> <num-records>2</num-records> <attributes-list> <lun-info> </lun-info> <lun-info> </lun-info> </attributes-list> <next-tag>fake-next</next-tag> </results>""")) response_2 = netapp_api.NaElement( etree.XML("""<results status="passed"> <num-records>2</num-records> <attributes-list> <lun-info> </lun-info> <lun-info> </lun-info> </attributes-list> </results>""")) self.connection.invoke_successfully.side_effect = [response, response_2] luns = self.client.get_lun_list() self.assertEqual(4, len(luns))
def test_get_iscsi_service_details(self): expected_iqn = 'iqn.1998-01.org.openstack.iscsi:name1' response = netapp_api.NaElement( etree.XML("""<results status="passed"> <num-records>1</num-records> <attributes-list> <iscsi-service-info> <node-name>%s</node-name> </iscsi-service-info> </attributes-list> </results>""" % expected_iqn)) self.connection.invoke_successfully.return_value = response iqn = self.client.get_iscsi_service_details() self.assertEqual(expected_iqn, iqn)
def get_iscsi_target_details(self): """Gets the iSCSI target portal details.""" iscsi_if_iter = netapp_api.NaElement('iscsi-portal-list-info') result = self.connection.invoke_successfully(iscsi_if_iter, True) tgt_list = [] portal_list_entries = result.get_child_by_name( 'iscsi-portal-list-entries') if portal_list_entries: portal_list = portal_list_entries.get_children() for iscsi_if in portal_list: d = dict() d['address'] = iscsi_if.get_child_content('ip-address') d['port'] = iscsi_if.get_child_content('ip-port') d['tpgroup-tag'] = iscsi_if.get_child_content('tpgroup-tag') tgt_list.append(d) return tgt_list
def test_query_aggr_storage_disk(self): na_server = netapp_api.NaServer('127.0.0.1') body = etree.XML("""<results status="passed"> <attributes-list> <storage-disk-info> <disk-raid-info> <effective-disk-type>SATA</effective-disk-type> </disk-raid-info> </storage-disk-info> </attributes-list> </results>""") self.mock_object(ssc_cmode.netapp_api, 'invoke_api', mock.Mock(return_value=[netapp_api.NaElement(body)])) eff_disk_type = ssc_cmode.query_aggr_storage_disk(na_server, 'aggr0') self.assertEqual('SATA', eff_disk_type)
def test_get_snapshots_marked_for_deletion(self, mock_return, expected): api_response = netapp_api.NaElement(mock_return) volume_list = [fake.SNAPSHOT['volume_id']] self.mock_object(self.client, 'send_request', mock.Mock(return_value=api_response)) result = self.client.get_snapshots_marked_for_deletion(volume_list) api_args = { 'target-name': fake.SNAPSHOT['volume_id'], 'target-type': 'volume', 'terse': 'true', } self.client.send_request.assert_called_once_with( 'snapshot-list-info', api_args) self.assertListEqual(expected, result)
def test_get_actual_path_for_export(self): fake_export_path = 'fake_export_path' expected_actual_pathname = 'fake_actual_pathname' response = netapp_api.NaElement( etree.XML("""<results status="passed"> <actual-pathname>%(path)s</actual-pathname> </results>""" % {'path': expected_actual_pathname})) self.connection.invoke_successfully.return_value = response actual_pathname = self.client.get_actual_path_for_export( fake_export_path) __, __, _kwargs = self.connection.invoke_successfully.mock_calls[0] enable_tunneling = _kwargs['enable_tunneling'] self.assertEqual(expected_actual_pathname, actual_pathname) self.assertTrue(enable_tunneling)
def test_get_igroup_by_initiators_multiple(self): initiators = ['11:22:33:44:55:66:77:88', '88:77:66:55:44:33:22:11'] expected_igroup = { 'initiator-group-os-type': 'default', 'initiator-group-type': 'fcp', 'initiator-group-name': 'openstack-igroup1', } response = netapp_api.NaElement( etree.XML("""<results status="passed"> <attributes-list> <initiator-group-info> <initiator-group-alua-enabled>true</initiator-group-alua-enabled> <initiator-group-name>%(initiator-group-name)s</initiator-group-name> <initiator-group-os-type>default</initiator-group-os-type> <initiator-group-throttle-borrow>false</initiator-group-throttle-borrow> <initiator-group-throttle-reserve>0</initiator-group-throttle-reserve> <initiator-group-type>%(initiator-group-type)s</initiator-group-type> <initiator-group-use-partner>true</initiator-group-use-partner> <initiator-group-uuid>f8aa707a-57fa-11e4-ad08-123478563412 </initiator-group-uuid> <initiator-group-vsa-enabled>false</initiator-group-vsa-enabled> <initiators> <initiator-info> <initiator-name>11:22:33:44:55:66:77:88</initiator-name> </initiator-info> <initiator-info> <initiator-name>88:77:66:55:44:33:22:11</initiator-name> </initiator-info> </initiators> <vserver>cinder-iscsi</vserver> </initiator-group-info> </attributes-list> <num-records>1</num-records> </results>""" % expected_igroup)) self.connection.invoke_successfully.return_value = response igroups = self.client.get_igroup_by_initiators(initiators) # make these lists of dicts comparable using hashable dictionaries igroups = set( [netapp_utils.hashabledict(igroup) for igroup in igroups]) expected = set([netapp_utils.hashabledict(expected_igroup)]) self.assertSetEqual(igroups, expected)
def get_fc_target_wwpns(self): """Gets the FC target details.""" wwpns = [] port_name_list_api = netapp_api.NaElement('fcp-port-name-get-iter') port_name_list_api.add_new_child('max-records', '100') result = self.connection.invoke_successfully(port_name_list_api, True) num_records = result.get_child_content('num-records') if num_records and int(num_records) >= 1: for port_name_info in result.get_child_by_name( 'attributes-list').get_children(): if port_name_info.get_child_content('is-used') != 'true': continue wwpn = port_name_info.get_child_content('port-name').lower() wwpns.append(wwpn) return wwpns
def get_performance_instance_names(self, object_name): """Get names of performance instances for a node.""" api_args = {'objectname': object_name} result = self.send_request('perf-object-instance-list-info', api_args, enable_tunneling=False) instance_names = [] instances = result.get_child_by_name( 'instances') or netapp_api.NaElement('None') for instance_info in instances.get_children(): instance_names.append(instance_info.get_child_content('name')) return instance_names
def get_iscsi_target_details(self): """Gets the iSCSI target portal details.""" iscsi_if_iter = netapp_api.NaElement('iscsi-interface-get-iter') result = self.connection.invoke_successfully(iscsi_if_iter, True) tgt_list = [] num_records = result.get_child_content('num-records') if num_records and int(num_records) >= 1: attr_list = result.get_child_by_name('attributes-list') iscsi_if_list = attr_list.get_children() for iscsi_if in iscsi_if_list: d = dict() d['address'] = iscsi_if.get_child_content('ip-address') d['port'] = iscsi_if.get_child_content('ip-port') d['tpgroup-tag'] = iscsi_if.get_child_content('tpgroup-tag') d['interface-enabled'] = iscsi_if.get_child_content( 'is-interface-enabled') tgt_list.append(d) return tgt_list
def test_get_performance_counter_info(self): self.mock_send_request.return_value = netapp_api.NaElement( fake_client.PERF_OBJECT_COUNTER_LIST_INFO_WAFL_RESPONSE) result = self.client.get_performance_counter_info('wafl', 'cp_phase_times') expected = { 'name': 'cp_phase_times', 'base-counter': 'total_cp_msecs', 'labels': fake_client.PERF_OBJECT_COUNTER_TOTAL_CP_MSECS_LABELS, } self.assertEqual(expected, result) perf_object_counter_list_info_args = {'objectname': 'wafl'} self.mock_send_request.assert_called_once_with( 'perf-object-counter-list-info', perf_object_counter_list_info_args, enable_tunneling=False)
def test_get_igroup_by_initiators_multiple(self): initiators = fake.FC_FORMATTED_INITIATORS response = netapp_api.NaElement( etree.XML("""<results status="passed"> <initiator-groups> <initiator-group-info> <initiator-group-name>%(initiator-group-name)s</initiator-group-name> <initiator-group-type>%(initiator-group-type)s</initiator-group-type> <initiator-group-uuid>1477ee47-0e1f-4b35-a82c-dcca0b76fc44 </initiator-group-uuid> <initiator-group-os-type>linux</initiator-group-os-type> <initiators> <initiator-info> <initiator-name>21:00:00:24:ff:40:6c:c3</initiator-name> </initiator-info> <initiator-info> <initiator-name>21:00:00:24:ff:40:6c:c2</initiator-name> </initiator-info> </initiators> </initiator-group-info> <initiator-group-info> <initiator-group-name>openstack-igroup2</initiator-group-name> <initiator-group-type>fcp</initiator-group-type> <initiator-group-uuid>1477ee47-0e1f-4b35-a82c-dcca0b76fc44 </initiator-group-uuid> <initiator-group-os-type>linux</initiator-group-os-type> <initiators> <initiator-info> <initiator-name>21:00:00:24:ff:40:6c:c2</initiator-name> </initiator-info> </initiators> </initiator-group-info> </initiator-groups> </results>""" % fake.IGROUP1)) self.connection.invoke_successfully.return_value = response igroups = self.client.get_igroup_by_initiators(initiators) # make these lists of dicts comparable using hashable dictionaries igroups = set( [netapp_utils.hashabledict(igroup) for igroup in igroups]) expected = set([netapp_utils.hashabledict(fake.IGROUP1)]) self.assertSetEqual(igroups, expected)
def _create_ems(netapp_backend, app_version, server_type): """Create ems API request.""" ems_log = netapp_api.NaElement('ems-autosupport-log') host = socket.getfqdn() or 'Cinder_node' if server_type == "cluster": dest = "cluster node" else: dest = "7 mode controller" ems_log.add_new_child('computer-name', host) ems_log.add_new_child('event-id', '0') ems_log.add_new_child('event-source', 'Cinder driver %s' % netapp_backend) ems_log.add_new_child('app-version', app_version) ems_log.add_new_child('category', 'provisioning') ems_log.add_new_child('event-description', 'OpenStack Cinder connected to %s' % dest) ems_log.add_new_child('log-level', '6') ems_log.add_new_child('auto-support', 'false') return ems_log
def test_find_mapped_lun_igroup(self): response = netapp_api.NaElement( etree.XML(""" <results status="passed"> <initiator-groups> <initiator-group-info> <initiator-group-name>%(initiator-group-name)s</initiator-group-name> <initiator-group-type>%(initiator-group-type)s</initiator-group-type> <initiator-group-uuid>1477ee47-0e1f-4b35-a82c-dcca0b76fc44 </initiator-group-uuid> <initiator-group-os-type>linux</initiator-group-os-type> <initiator-group-throttle-reserve>0</initiator-group-throttle-reserve> <initiator-group-throttle-borrow>false </initiator-group-throttle-borrow> <initiator-group-vsa-enabled>false</initiator-group-vsa-enabled> <initiator-group-alua-enabled>true</initiator-group-alua-enabled> <initiator-group-report-scsi-name-enabled>true </initiator-group-report-scsi-name-enabled> <initiator-group-use-partner>true</initiator-group-use-partner> <initiators> <initiator-info> <initiator-name>21:00:00:24:ff:40:6c:c3</initiator-name> </initiator-info> <initiator-info> <initiator-name>21:00:00:24:ff:40:6c:c2</initiator-name> <initiator-alias-info> <initiator-alias>Centos</initiator-alias> </initiator-alias-info> </initiator-info> </initiators> <lun-id>2</lun-id> </initiator-group-info> </initiator-groups> </results>""" % fake.IGROUP1)) initiators = fake.FC_FORMATTED_INITIATORS self.zapi_client.get_lun_map.return_value = response (igroup, lun_id) = self.library._find_mapped_lun_igroup('path', initiators) self.assertEqual(fake.IGROUP1_NAME, igroup) self.assertEqual('2', lun_id)
def _handle_get_snapshot_return_failure(self, result, snapshot_name): error_record_list = result.get_child_by_name( 'volume-errors') or netapp_api.NaElement('none') errors = error_record_list.get_children() if errors: error = errors[0] error_code = error.get_child_content('errno') error_reason = error.get_child_content('reason') msg = _('Could not read information for snapshot %(name)s. ' 'Code: %(code)s. Reason: %(reason)s') msg_args = { 'name': snapshot_name, 'code': error_code, 'reason': error_reason, } if error_code == netapp_api.ESNAPSHOTNOTALLOWED: raise exception.SnapshotUnavailable(data=msg % msg_args) else: raise exception.VolumeBackendAPIException(data=msg % msg_args)