Source code for zhmcclient._adapter

# Copyright 2016,2021 IBM Corp. All Rights Reserved.
#
# 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.

"""
An :term:`Adapter` is a physical adapter card (e.g. OSA-Express adapter,
Crypto adapter) or a logical adapter (e.g. HiperSockets switch).

Adapter resources are contained in :term:`CPC` resources.

Adapter resources originally were supported only for CPCs in DPM mode. Since
z16, Adapter resources are in addition supported for CPCs in classic mode, but
in a quite limited fashion: The only HMC operations supported with classic mode
CPCs are "List Permitted Adapters" and "Update Adapter Firmware".

Each method description of adapter related zhmcclient classes details on which
types of CPCs it is supported.

A CPC in DPM mode automatically discovers the physical adapter cards that are
plugged into the system. A CPC in classic mode shows only the adapters that
have been defined in the active IOCDS.
"""


import copy

from ._manager import BaseManager
from ._resource import BaseResource
from ._port import PortManager
from ._logging import logged_api_call
from ._exceptions import CeasedExistence, ConsistencyError

from ._utils import repr_dict, repr_manager, repr_timestamp, matches_filters, \
    divide_filter_args, make_query_str, RC_ADAPTER, repr_obj_id

__all__ = ['AdapterManager', 'Adapter']


[docs] class AdapterManager(BaseManager): """ Manager providing access to the :term:`Adapters <Adapter>` in a particular :term:`CPC`. Derived from :class:`~zhmcclient.BaseManager`; see there for common methods and attributes. Objects of this class are not directly created by the user; they are accessible via the following instance variable of a :class:`~zhmcclient.Cpc` object: * :attr:`~zhmcclient.Cpc.adapters` HMC/SE version requirements: * SE version >= 2.13.1 """ def __init__(self, cpc): # This function should not go into the docs. # Parameters: # cpc (:class:`~zhmcclient.Cpc`): # CPC defining the scope for this manager. # Resource properties that are supported as filter query parameters. # If the support for a resource property changes within the set of HMC # versions that support this type of resource, this list must be set up # for the version of the HMC this session is connected to. query_props = [ 'name', # The adapter-id property is supported for filtering, but due to # a firmware defect, adapters with a hex digit in their adapter-id # property are not found. Disabling the property causes it to # be handled via client-side filtering, so that mitigates the # defect. # TODO: Re-enable the property once the defect is fixed and the fix # is rolled out broadly enough. # 'adapter-id', 'adapter-family', 'type', 'status', ] super().__init__( resource_class=Adapter, class_name=RC_ADAPTER, session=cpc.manager.session, parent=cpc, base_uri='/api/adapters', oid_prop='object-id', uri_prop='object-uri', name_prop='name', query_props=query_props) @property def cpc(self): """ :class:`~zhmcclient.Cpc`: :term:`CPC` defining the scope for this manager. """ return self._parent
[docs] @logged_api_call # pylint: disable=arguments-differ def list(self, full_properties=False, filter_args=None, additional_properties=None): """ List the Adapters in this CPC. Any resource property may be specified in a filter argument. For details about filter arguments, see :ref:`Filtering`. The listing of resources is handled in an optimized way: * If this manager is enabled for :ref:`auto-updating`, a locally maintained resource list is used (which is automatically updated via inventory notifications from the HMC) and the provided filter arguments are applied. * Otherwise, if the filter arguments specify the resource name as a single filter argument with a straight match string (i.e. without regular expressions), an optimized lookup is performed based on a locally maintained name-URI cache. * Otherwise, the HMC List operation is performed with the subset of the provided filter arguments that can be handled on the HMC side and the remaining filter arguments are applied on the client side on the list result. HMC/SE version requirements: * SE version >= 2.13.1 * The CPC must be in DPM mode. Authorization requirements: * Object-access permission to this CPC. * Object-access permission to any Adapter to be included in the result. Parameters: full_properties (bool): Controls whether the full set of resource properties should be retrieved, vs. only the short set as returned by the list operation. filter_args (dict): Filter arguments that narrow the list of returned resources to those that match the specified filter arguments. For details, see :ref:`Filtering`. `None` causes no filtering to happen, i.e. all resources are returned. additional_properties (list of string): List of property names that are to be returned in addition to the default properties. This parameter requires HMC 2.16.0 or higher. Returns: : A list of :class:`~zhmcclient.Adapter` objects. Raises: :exc:`~zhmcclient.HTTPError` :exc:`~zhmcclient.ParseError` :exc:`~zhmcclient.AuthError` :exc:`~zhmcclient.ConnectionError` :exc:`~zhmcclient.FilterConversionError` """ result_prop = 'adapters' list_uri = f'{self.cpc.uri}/adapters' return self._list_with_operation( list_uri, result_prop, full_properties, filter_args, additional_properties)
[docs] @logged_api_call def create_hipersocket(self, properties): """ Create and configure a HiperSockets Adapter in this CPC. z17 CPCs removed the "Create Hipersocket" operation. This method uses the "Create Partition Link" operation when the API feature "dpm-hipersockets-partition-link-management" is enabled, and otherwise the earlier "Create Hipersocket" operation. As a result, this method supports all generations of CPCs. HMC/SE version requirements: * SE version >= 2.13.1 * The CPC must be in DPM mode. Authorization requirements: * Object-access permission to the scoping CPC. * When the "dpm-hipersockets-partition-link-management" API feature is enabled, task permission to the "Create Partition Link" task; otherwise task permission to the "Create HiperSockets Adapter" task. Parameters: properties (dict): Initial property values. Allowable properties are the following: * "name" (str): Required: The adapter's 'name' property. When the Hipersocket adapter is created via a Partition Link, this also becomes the name of the Partition Link. * "description" (str): Optional: The adapter's 'description' property. Default: An empty string. * "maximum-transmission-unit-size" (int): Optional: The adapter's 'maximum-transmission-unit-size' property. Default: 8 * "port-description" (str): Optional: The adapter port's 'description' property. Default: An empty string. This property is ignored on CPCs of z16 and later. These properties are defined in section 'Request body contents' in section 'Create Hipersocket' in the :term:`HMC API` book, but they are valid also when the Hipersocket adapter is created via a Partition Link. Returns: :class:`~zhmcclient.Adapter`: The resource object for the new HiperSockets Adapter. The object will have its 'object-uri' property set as returned by the HMC, and will also have the input properties set. Raises: :exc:`~zhmcclient.HTTPError` :exc:`~zhmcclient.ParseError` :exc:`~zhmcclient.AuthError` :exc:`~zhmcclient.ConnectionError` """ cpc = self.cpc pl_feature = cpc.api_feature_enabled( "dpm-hipersockets-partition-link-management") if pl_feature: name = properties['name'] console = cpc.manager.console part_link = console.partition_links.create({ "name": name, "type": "hipersockets", "cpc-uri": cpc.uri, }) props = { "name": name } description = properties.get("description") if description: props["description"] = description mtu_size = properties.get("maximum-transmission-unit-size") if mtu_size: props["maximum-transmission-unit-size"] = mtu_size # TODO: Use "port-description" to set port description uri = part_link.get_property('adapter-uri') adapter = Adapter(self, uri, name, props) adapter.update_properties(props) else: result = self.session.post(self.cpc.uri + '/adapters', body=properties) # There should not be overlaps, but just in case there are, the # returned props should overwrite the input props: props = copy.deepcopy(properties) props.update(result) name = props.get(self._name_prop, None) uri = props[self._uri_prop] adapter = Adapter(self, uri, name, props) self._name_uri_cache.update(name, uri) return adapter
[docs] class Adapter(BaseResource): """ Representation of an :term:`Adapter`. Note that adapter objects do not correspond 1:1 with the physical adapter cards. Adapter objects do correspond 1:1 with PCHIDs, though. On classic mode CPCs, there are additional adapter objects that correspond 1:1 with the VCHIDs of Network Express adapters. Some examples: * OSA Express cards with 2 ports have a single PCHID for the physical card and thus a single Adapter object. * Network Express cards with 2 ports have one PCHID for each port, and thus 2 Adapter objects. On classic mode CPCs, each port has two additional VCHIDs for the 'osh' and 'neth' support of the adapter, so a 2-port card has 4 additional Adapter objects. * FICON Express cards with 2 ports have one PCHID for each port, and thus 2 Adapter objects. * Crypto Express cards with 2 HSMs (= "ports") have one PCHID for each HSM, and thus 2 Adapter objects. Derived from :class:`~zhmcclient.BaseResource`; see there for common methods and attributes. For the properties of an Adapter, see section 'Data model' in section 'Adapter object' in the :term:`HMC API` book. Objects of this class are not directly created by the user; they are returned from creation or list functions on their manager object (in this case, :class:`~zhmcclient.AdapterManager`). HMC/SE version requirements: * SE version >= 2.13.1 """ # Name of property for port URIs, dependent on adapter family port_uris_prop_by_family = { 'ficon': 'storage-port-uris', 'osa': 'network-port-uris', 'roce': 'network-port-uris', 'hipersockets': 'network-port-uris', 'cna': 'network-port-uris', 'cloud-network': 'network-port-uris', # for preliminary driver 'network-express': 'network-port-uris', 'networking': 'network-port-uris', } # URI segment for port URIs, dependent on adapter family port_uri_segment_by_family = { 'ficon': 'storage-ports', 'osa': 'network-ports', 'roce': 'network-ports', 'hipersockets': 'network-ports', 'cna': 'network-ports', 'cloud-network': 'network-ports', # for preliminary driver 'network-express': 'network-ports', 'networking': 'network-ports', } # Port type, dependent on adapter family port_type_by_family = { 'ficon': 'storage', 'osa': 'network', 'roce': 'network', 'hipersockets': 'network', 'cna': 'network', 'cloud-network': 'network', # for preliminary driver 'network-express': 'network', 'networking': 'network', } def __init__(self, manager, uri, name=None, properties=None): # This function should not go into the docs. # manager (:class:`~zhmcclient.AdapterManager`): # Manager object for this resource object. # uri (string): # Canonical URI path of the resource. # name (string): # Name of the resource. # properties (dict): # Properties to be set for this resource object. May be `None` or # empty. assert isinstance(manager, AdapterManager), ( f"Adapter init: Expected manager type {AdapterManager}, got " f"{type(manager)}") super().__init__(manager, uri, name, properties) # The manager objects for child resources (with lazy initialization): self._ports = None self._port_uris_prop = None self._port_uri_segment = None @property def ports(self): """ :class:`~zhmcclient.PortManager`: Access to the :term:`Ports <Port>` of this Adapter. """ # We do here some lazy loading. if not self._ports: family = self.get_property('adapter-family') try: port_type = self.port_type_by_family[family] except KeyError: port_type = None self._ports = PortManager(self, port_type) return self._ports @property def port_uris_prop(self): """ :term:`string`: Name of adapter property that specifies the adapter port URIs, or the empty string ('') for adapters without ports. For example, 'network-port-uris' for a network adapter. """ if self._port_uris_prop is None: family = self.get_property('adapter-family') try: self._port_uris_prop = self.port_uris_prop_by_family[family] except KeyError: self._port_uris_prop = '' return self._port_uris_prop @property def port_uri_segment(self): """ :term:`string`: Adapter type specific URI segment for adapter port URIs, or the empty string ('') for adapters without ports. For example, 'network-ports' for a network adapter. """ if self._port_uri_segment is None: family = self.get_property('adapter-family') try: self._port_uri_segment = self.port_uri_segment_by_family[ family] except KeyError: self._port_uri_segment = '' return self._port_uri_segment @property @logged_api_call def maximum_crypto_domains(self): """ Integer: The maximum number of crypto domains on this crypto adapter. HMC/SE version requirements: * The parent CPC of the adapter must be in DPM mode. The maximum number of crypto domains on any crypto adapter (= HSM) is always equal to the maximum number of active partitions / LPARs on the CPC. For example, high-end systems support a maximum number of 85 crypto domains per HSM, and mid-range systems support a maximum number of 40 crypto domains per HSM. If this adapter is not a crypto adapter, `None` is returned. Raises: :exc:`~zhmcclient.HTTPError` :exc:`~zhmcclient.ParseError` :exc:`~zhmcclient.AuthError` :exc:`~zhmcclient.ConnectionError` :exc:`ValueError`: Unknown crypto card type """ if self.get_property('adapter-family') != 'crypto': return None card_type = self.get_property('detected-card-type') if card_type.startswith('crypto-express-'): max_domains = self.manager.cpc.maximum_active_partitions else: raise ValueError(f"Unknown crypto card type: {card_type!r}") return max_domains
[docs] @logged_api_call def delete(self): """ Delete this Hipersocket Adapter. The Adapter must be a HiperSockets Adapter and must not currently be the backing adapter of any partition NICs. z17 CPCs removed the "Delete Hipersocket" operation. This method uses the "Delete Partition Link" operation when the API feature "dpm-hipersockets-partition-link-management" is enabled, and otherwise the earlier "Delete Hipersocket" operation. As a result, this method supports all generations of CPCs. HMC/SE version requirements: * SE version >= 2.13.1 * The parent CPC of the adapter must be in DPM mode. Authorization requirements: * Object-access permission to the HiperSockets Adapter to be deleted. * When the "dpm-hipersockets-partition-link-management" API feature is enabled, task permission to the "Delete Partition Link" task; otherwise task permission to the "Delete HiperSockets Adapter" task. Raises: :exc:`~zhmcclient.CeasedExistence`: The Partition Link for the adapter no longer exists (on CPCs of z16 and later) :exc:`~zhmcclient.ConsistencyError`: Found more than one Partition Link for the adapter (on CPCs of z16 and later) :exc:`~zhmcclient.HTTPError` :exc:`~zhmcclient.ParseError` :exc:`~zhmcclient.AuthError` :exc:`~zhmcclient.ConnectionError` """ cpc = self.manager.cpc pl_feature = cpc.api_feature_enabled( "dpm-hipersockets-partition-link-management") if pl_feature: console = cpc.manager.console filter_args = {"cpc-uri": cpc.uri} part_links = console.partition_links.list( filter_args=filter_args, additional_properties=['adapter-uri'], ) part_links_for_adapter = [] for part_link in part_links: if part_link.get_property('adapter-uri') == self.uri: part_links_for_adapter.append(part_link) num_pl = len(part_links_for_adapter) # On z16 or later CPCs, the HMC ensures that Hipersocket Adapter # object and its Partition Link object always exist either both # or none, and that each HiperSocket Adapter can have only one # Partition Link. if num_pl == 0: # The Partition Link is gone, so the HS Adapter is also gone. self.cease_existence_local() raise CeasedExistence(self._uri) if num_pl > 1: # The HMC ensures that this does not happen, so if it happens # it is an inconsistency. pl_names = [pl.name for pl in part_links_for_adapter] raise ConsistencyError( message=f"Found {num_pl} partition links " f"({', '.join(pl_names)}) for adapter {self.name} on CPC " f"{cpc.name} - please report this as a zhmcclient issue") part_link = part_links_for_adapter[0] part_link.delete() # That also deletes the Hipersocket adapter else: self.manager.session.delete(self.uri, resource=self) # pylint: disable=protected-access self.manager._name_uri_cache.delete( self.get_properties_local(self.manager._name_prop, None)) self.cease_existence_local()
[docs] @logged_api_call def update_properties(self, properties): """ Update writeable properties of this Adapter. This method serializes with other methods that access or change properties on the same Python object. HMC/SE version requirements: * SE version >= 2.13.1 * The parent CPC of the adapter must be in DPM mode. Authorization requirements: * Object-access permission to the Adapter. * Task permission for the "Adapter Details" task. Parameters: properties (dict): New values for the properties to be updated. Properties not to be updated are omitted. Allowable properties are the properties with qualifier (w) in section 'Data model' in section 'Adapter object' in the :term:`HMC API` book. Raises: :exc:`~zhmcclient.HTTPError` :exc:`~zhmcclient.ParseError` :exc:`~zhmcclient.AuthError` :exc:`~zhmcclient.ConnectionError` """ # pylint: disable=protected-access self.manager.session.post(self.uri, resource=self, body=properties) is_rename = self.manager._name_prop in properties if is_rename: # Delete the old name from the cache self.manager._name_uri_cache.delete(self.name) self.update_properties_local(copy.deepcopy(properties)) if is_rename: # Add the new name to the cache self.manager._name_uri_cache.update(self.name, self.uri)
[docs] def __repr__(self): """ Return a string with the state of this Adapter, for debug purposes. """ ret = ( f"{repr_obj_id(self)} (\n" f" _manager={repr_obj_id(self._manager)},\n" f" _uri={self._uri!r},\n" f" _full_properties={self._full_properties!r},\n" " _properties_timestamp=" f"{repr_timestamp(self._properties_timestamp)},\n" f" _properties={repr_dict(self._properties, indent=4)},\n" f" _ports(lazy)={repr_manager(self._ports, indent=2)}\n" ")") return ret
[docs] @logged_api_call def change_crypto_type(self, crypto_type, zeroize=None): """ Reconfigures a cryptographic adapter to a different crypto type. This operation is only supported for cryptographic adapters. The cryptographic adapter must be varied offline before its crypto type can be reconfigured. HMC/SE version requirements: * SE version >= 2.13.1 * The parent CPC of the adapter must be in DPM mode. Authorization requirements: * Object-access permission to this Adapter. * Task permission to the "Adapter Details" task. Parameters: crypto_type (:term:`string`): - ``"accelerator"``: Crypto Express5S Accelerator - ``"cca-coprocessor"``: Crypto Express5S CCA Coprocessor - ``"ep11-coprocessor"``: Crypto Express5S EP11 Coprocessor zeroize (bool): Specifies whether the cryptographic adapter will be zeroized when it is reconfigured to a crypto type of ``"accelerator"``. `None` means that the HMC-implemented default of `True` will be used. Raises: :exc:`~zhmcclient.HTTPError` :exc:`~zhmcclient.ParseError` :exc:`~zhmcclient.AuthError` :exc:`~zhmcclient.ConnectionError` """ body = {'crypto-type': crypto_type} if zeroize is not None: body['zeroize'] = zeroize self.manager.session.post( self.uri + '/operations/change-crypto-type', resource=self, body=body)
[docs] @logged_api_call def change_adapter_type(self, adapter_type): """ Reconfigures an adapter from one type to another, or to ungonfigured. Currently, only storage adapters can be reconfigured, and their adapter type is the supported storage protocol (FCP vs. FICON). Storage adapter instances (i.e. :class:`~zhmcclient.Adapter` objects) represent daughter cards on a physical storage card. Current storage cards require both daughter cards to be configured to the same protocol, so changing the type of the targeted adapter will also change the type of the adapter instance that represents the other daughter card on the same physical card. Zhmcclient users that need to determine the related adapter instance can do so by finding the storage adapter with a matching first 9 characters (card ID and slot ID) of their `card-location` property values. The targeted adapter and its related adapter on the same storage card must not already have the desired adapter type, they must not be attached to any partition, and they must not have an adapter status of 'exceptions'. HMC/SE version requirements: * SE version >= 2.13.1 * The parent CPC of the adapter must be in DPM mode. Authorization requirements: * Object-access permission to this Adapter. * Task permission to the "Configure Storage - System Programmer" task. Parameters: adapter_type (:term:`string`): - ``"fcp"``: FCP (Fibre Channel Protocol) - ``"fc"``: FICON (Fibre Connection) protocol - ``"not-configured"``: No adapter type configured Raises: :exc:`~zhmcclient.HTTPError` :exc:`~zhmcclient.ParseError` :exc:`~zhmcclient.AuthError` :exc:`~zhmcclient.ConnectionError` """ body = {'type': adapter_type} self.manager.session.post( self.uri + '/operations/change-adapter-type', resource=self, body=body)
[docs] def dump(self): """ Dump this Adapter resource with its properties and child resources (recursively) as a resource definition. The returned resource definition has the following format:: { # Resource properties: "properties": {...}, # Child resources: "ports": [...], } Returns: dict: Resource definition of this resource. """ # Dump the resource properties resource_dict = super().dump() # Dump the child resources ports = self.ports.dump() if ports: resource_dict['ports'] = ports return resource_dict
[docs] @logged_api_call def list_assigned_partitions(self, full_properties=False, filter_args=None): """ List the partitions assigned to this adapter. This method is not supported for OSA adapters configured as OSM (because those cannot be assigned to partitions). HMC/SE version requirements: * SE version >= 2.13.1 * The parent CPC of the adapter must be in DPM mode. Authorization requirements: * Object-access permission to this Adapter. Parameters: full_properties (bool): Controls whether the full set of partition properties should be retrieved, vs. only a short set (uri, name, status). filter_args (dict): Filter arguments that narrow the list of returned partitions to those that match the specified filter arguments. For details, see :ref:`Filtering`. `None` causes no filtering to happen, i.e. all assigned partitions are returned. Returns: : A list of :class:`~zhmcclient.Partition` objects. Raises: :exc:`~zhmcclient.HTTPError` :exc:`~zhmcclient.ParseError` :exc:`~zhmcclient.AuthError` :exc:`~zhmcclient.ConnectionError` """ query_props = ['name', 'status'] query_parms, client_filters = divide_filter_args( query_props, filter_args) query_parms_str = make_query_str(query_parms) uri = (f'{self.uri}/operations/get-partitions-assigned-to-adapter' f'{query_parms_str}') result = self.manager.session.get(uri, resource=self) partition_mgr = self.manager.parent.partitions resource_obj_list = [] for props in result['partitions-assigned-to-adapter']: # pylint: disable=protected-access resource_obj = partition_mgr.resource_class( manager=partition_mgr, uri=props[partition_mgr._uri_prop], name=props.get(partition_mgr._name_prop, None), properties=props) if matches_filters(resource_obj, client_filters): resource_obj_list.append(resource_obj) if full_properties: resource_obj.pull_full_properties() return resource_obj_list
[docs] @logged_api_call def list_sibling_adapters(self, full_properties=False): """ List the other Adapters on the same adapter card as this Adapter. Some adapter cards are represented as multiple Adapter objects (for example, 2-port FICON Express cards, or 2-port CNA cards). This method lists the other Adapter objects that are on the same adapter card as this Adapter object. This is useful for example to determine the affected Adapter objects when replacing the adapter card, or when changing the type of a FICON Express adepter (see :meth:`~zhmcclient.Adapter.change_adapter_type`). HMC/SE version requirements: * SE version >= 2.13.1 * The parent CPC of the adapter must be in DPM mode. Authorization requirements: * Object-access permission to this CPC. * Object-access permission to any Adapter to be included in the result. Parameters: full_properties (bool): Controls whether the full set of resource properties should be retrieved, vs. only the short set as returned by the list operation. Returns: : A list of :class:`~zhmcclient.Adapter` objects. Raises: :exc:`~zhmcclient.HTTPError` :exc:`~zhmcclient.ParseError` :exc:`~zhmcclient.AuthError` :exc:`~zhmcclient.ConnectionError` """ # This algorithm is based on the fact that physical adapter cards # have their PCHIDs always within a range of 4 adjacent PCHIDs that # start at a multiple of 4. self_pchid = int(self.prop('adapter-id'), 16) if self_pchid >= int('7c0', 16): # A virtual adapter with a single PCHID -> no siblings return [] # A physical adapter with a total of 4 PCHIDs reserved for the slot pchid_base = self_pchid // 4 * 4 sibling_pchids = list(range(pchid_base, pchid_base + 4)) sibling_pchids.remove(self_pchid) sibling_adapter_ids = [f'{p:03x}' for p in sibling_pchids] filter_args = {'adapter-id': sibling_adapter_ids} sibling_adapters = self.manager.cpc.adapters.list( full_properties, filter_args) return sibling_adapters