# Copyright 2024 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.
"""
A :term:`Partition Link` interconnects two or more
:term:`Partitions <Partition>` that share the same network configuration.
Partition Links have been introduced with SE 2.16. Conceptually, Partition Links
can connect different :term:`CPCs <CPC>`. However, the implementation is
currently limited to the same DPM-enabled CPC. Currently, one Partition Link
resource is always associated with a single CPC at any time.
Partition Links are based on different technologies, indicated in their
``type`` property:
* ``smc-d`` - Shared Memory Communications - Direct Memory Access (SMC-D)
(Version 2 or later).
Support for this technology is indicated via :ref:`API feature <API features>`
"dpm-smcd-partition-link-management".
* ``hipersockets`` - Hipersockets.
Support for this technology is indicated via :ref:`API feature <API features>`
"dpm-hipersockets-partition-link-management".
* ``ctc`` - FICON Channel to Channel (CTC) interconnect, using corresponding
cabling between FICON adapters.
Support for this technology is indicated via :ref:`API feature <API features>`
"dpm-ctc-partition-link-management".
Partition Links have a ``state`` property that indicates their current
attachment state to any partitions:
* ``complete`` - All requests for attaching or detaching the partition link to
or from partitions are complete, and the partition link is attached to 2 or
more partitions.
* ``incomplete`` - All requests for attaching or detaching the partition link to
or from partitions are complete, but the partition link is attached to less
than 2 partitions.
* ``updating`` - Some requests for attaching or detaching the partition link to
or from partitions are incomplete.
This section describes the interface for Partition Links using resource class
:class:`~zhmcclient.PartitionLink` and the corresponding manager class
:class:`~zhmcclient.PartitionLinkManager`.
Because conceptually, Partition Links can connect different CPCs, this client
has designed :class:`~zhmcclient.PartitionLink` resources to be available
through the :class:`~zhmcclient.Console` resource, via its
:attr:`~zhmcclient.Console.partition_links` property.
The earlier interfaces for Hipersockets are also supported:
* represented as :class:`~zhmcclient.Adapter`, including support for creation
and deletion.
* Attachment to a partition is managed via creation and deletion of
:class:`~zhmcclient.Nic` resources on the partition.
"""
import copy
import re
import time
from ._manager import BaseManager
from ._resource import BaseResource
from ._logging import get_logger, logged_api_call
from ._constants import HMC_LOGGER_NAME
from ._utils import RC_PARTITION_LINK
from ._exceptions import PartitionLinkError, OperationTimeout
__all__ = ['PartitionLinkManager', 'PartitionLink']
HMC_LOGGER = get_logger(HMC_LOGGER_NAME)
[docs]
class PartitionLinkManager(BaseManager):
"""
Manager providing access to the :term:`partition links <partition link>` of
the HMC.
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:
* :attr:`~zhmcclient.Console.partition_links` of a
:class:`~zhmcclient.Console` object.
HMC/SE version requirements:
* SE version >= 2.16.0
* for technology-specific support, see the API features described in
:ref:`Partition Links`
"""
def __init__(self, console):
# This function should not go into the docs.
# Parameters:
# console (:class:`~zhmcclient.Console`):
# CPC or HMC 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 = [
'cpc-uri',
'name',
'state',
]
super().__init__(
resource_class=PartitionLink,
class_name=RC_PARTITION_LINK,
session=console.manager.session,
parent=console,
base_uri='/api/partition-links',
oid_prop='object-id',
uri_prop='object-uri',
name_prop='name',
query_props=query_props)
self._console = console
@property
def console(self):
"""
:class:`~zhmcclient.Console`: The Console object representing the HMC.
"""
return self._console
[docs]
@logged_api_call
def list(self, full_properties=False, filter_args=None,
additional_properties=None):
"""
List the partition links known to the HMC.
Partition links for which the authenticated user does not have
object-access permission are not included.
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.
This method performs the "List Partition Links" HMC operation.
HMC/SE version requirements:
* SE version >= 2.16.0
* for technology-specific support, see the API features described in
:ref:`Partition Links`
Authorization requirements:
* Object-access permission to any partition links to be included in the
result.
Parameters:
full_properties (bool):
Controls that the full set of resource properties for each returned
partition link is being retrieved, vs. only the following short
set: "object-uri", "cpc-uri", "name", "state", and "type".
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.
additional_properties (list of string):
List of property names that are to be returned in addition to the
short set of properties.
Returns:
: A list of :class:`~zhmcclient.PartitionLink` objects.
Raises:
:exc:`~zhmcclient.HTTPError`
:exc:`~zhmcclient.ParseError`
:exc:`~zhmcclient.AuthError`
:exc:`~zhmcclient.ConnectionError`
:exc:`~zhmcclient.FilterConversionError`
"""
result_prop = 'partition-links'
list_uri = self._base_uri
return self._list_with_operation(
list_uri, result_prop, full_properties, filter_args,
additional_properties)
[docs]
@logged_api_call
def create(self, properties=None):
"""
Create a partition link from the input properties.
The input properties may specify an initial attachment of the partition
link to one or more partitions.
Additional attachments to partitions and detachments from partitions
can be performed with the following :class:`~zhmcclient.Partition`
methods:
* :meth:`zhmcclient.Partition.attach_network_link`
* :meth:`zhmcclient.Partition.detach_network_link`
* :meth:`zhmcclient.Partition.attach_ctc_link`
* :meth:`zhmcclient.Partition.detach_ctc_link`
and also with the :meth:`zhmcclient.PartitionLink.update_properties`
method.
For CTC-type partition links, it is required to specify the paths
describing the physical connectivity between the FICON adapters at
creation time, using the ``paths`` input property.
The new partition link will be associated with the CPC identified by the
``cpc-uri`` input property.
This method performs the "Create Partition Link" HMC operation and
waits for completion of its asynchronous job.
SSC management NICs backed by a Hipersocket adapter can be created
by first creating a Partition Link using this method with a
"bus-connections" property that specifies partitions to be linked, and
then updating the created NIC(s) with the
:meth:`zhmcclient.Nic.update_properties` method to become an SSC
management NIC.
Limitations:
* When the API feature "dpm-hipersockets-partition-link-management"
is enabled on a z16 CPC (and only there), SSC management NICs
cannot be backed by Hipersocket adapters. This limitation can be
lifted by installing the firmware feature "DPM IQD Links - Disable",
which can be requested by customers from IBM support.
HMC/SE version requirements:
* SE version >= 2.16.0
* for technology-specific support, see the API features described in
:ref:`Partition Links`
Authorization requirements:
* Object-access permission to the CPC that will be associated with
the new partition link.
* Task permission to the "Create Partition Link" task.
* Object-access permission to all Partitions for the initially requested
attachments.
* Object-access permission to all FICON adapter objects used for the
CTC connections for the initially requested attachments.
Parameters:
properties (dict): Initial property values.
Allowable properties are defined in section 'Request body contents'
in section 'Create Partition Link' in the :term:`HMC API` book.
Returns:
:class:`~zhmcclient.PartitionLink`: The new partition link.
Raises:
:exc:`~zhmcclient.HTTPError`
:exc:`~zhmcclient.ParseError`
:exc:`~zhmcclient.AuthError`
:exc:`~zhmcclient.ConnectionError`
:exc:`~zhmcclient.OperationTimeout`: The timeout expired while
waiting for completion of the operation.
Examples:
* Create Hipersockets-type partition link with no attached partitions::
part_link = console.partition_links.create({
"name": "mylink",
"type": "hipersockets",
"cpc-uri": cpc.uri
})
* Create CTC-type partition link with one path and two attached
partitions::
part_link = console.partition_links.create({
"name": "mylink",
"type": "ctc",
"cpc-uri": cpc.uri,
"partitions": [part1.uri, part2.uri],
"paths": [
{
"adapter-port-uri": fc_port1.uri,
"connecting-adapter-port-uri": fc_port2.uri
}
]
})
"""
if properties is None:
properties = {}
result = self.session.post(
uri=self._base_uri,
body=properties,
wait_for_completion=True)
# The "Create Partition Link" operation does not return the object-uri
# of the new partition link in the response. however, it does return
# it in the "Location" header, and the session.post() method adds that
# to its return value as the "location-uri" field.
uri = result['location-uri']
name = properties[self._name_prop]
props = copy.deepcopy(properties)
# Remove input parameters that are not 1:1 in the data model
if 'partitions' in props:
del props['partitions'] # not in data model
if 'paths' in props:
del props['paths'] # different structure in data model
props[self._uri_prop] = uri
partition_link = PartitionLink(self, uri, name, props)
self._name_uri_cache.update(name, uri)
return partition_link
[docs]
class PartitionLink(BaseResource):
"""
Representation of a :term:`partition link`.
Derived from :class:`~zhmcclient.BaseResource`; see there for common
methods and attributes.
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.PartitionLinkManager`).
HMC/SE version requirements:
* SE version >= 2.16.0
* for technology-specific support, see the API features described in
:ref:`Partition Links`
"""
def __init__(self, manager, uri, name=None, properties=None):
# This function should not go into the docs.
# manager (:class:`~zhmcclient.PartitionLinkManager`):
# 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, PartitionLinkManager), (
"PartitionLink init: Expected manager type "
f"{PartitionLinkManager}, got {type(manager)}")
super().__init__(manager, uri, name, properties)
# The manager objects for child resources (with lazy initialization):
self._cpc = None
@property
def cpc(self):
"""
:class:`~zhmcclient.Cpc`: The :term:`CPC` to which this partition link
is associated.
The returned :class:`~zhmcclient.Cpc` has only a minimal set of
properties populated.
"""
# We do here some lazy loading.
if not self._cpc:
cpc_uri = self.get_property('cpc-uri')
cpc_mgr = self.manager.console.manager.client.cpcs
self._cpc = cpc_mgr.resource_object(cpc_uri)
return self._cpc
[docs]
@logged_api_call
def list_attached_partitions(self, name=None, status=None):
"""
Return the partitions to which this partition link is currently
attached, optionally filtered by partition name and status.
Properties of the endpoint objects of the partition links are not
returned by this method and can be obtained via the
:class:`~zhmcclient.PartitionLink` properties.
In case of Hipersockets, these properties are also available
on the NIC objects. In case of SMC-D and CTC, the HMC API does not
externalize corresponding endpoint objects other than as properties
of partition link objexts.
This method performs the "Get Partition Link Properties" HMC operation,
if this Python object does not yet have the full properties.
HMC/SE version requirements:
* SE version >= 2.16.0
* for technology-specific support, see the API features described in
:ref:`Partition Links`
Authorization requirements:
* Object-access permission to this partition link.
Parameters:
name (:term:`string`): Filter pattern (regular expression)
to limit returned partitions to those that have a matching
name. If `None`, no filtering for the partition name takes place.
status (:term:`string`): Filter string to limit returned
partitions to those that have a matching status. The value
must be a valid partition 'status' property value. If `None`, no
filtering for the partition status takes place.
Returns:
List of :class:`~zhmcclient.Partition` objects representing the
partitions to which this partition link is currently attached, with
a minimal set of properties ('object-id', 'name').
Raises:
:exc:`~zhmcclient.HTTPError`
:exc:`~zhmcclient.ParseError`
:exc:`~zhmcclient.AuthError`
:exc:`~zhmcclient.ConnectionError`
"""
part_list = []
part_uri_list = []
pl_type = self.get_property('type')
if pl_type == 'ctc':
for path in self.get_property('paths'):
for device in path.get('devices'):
for endpoint in device.get('endpoint-pair'):
part_uri = endpoint.get('partition-uri')
part_name = endpoint.get('partition-name')
part = self.cpc.partitions.resource_object(
part_uri, {'name': part_name})
if name:
if not re.match(name, part_name):
continue
if status:
if part.get_property('status') != status:
continue
if part_uri not in part_uri_list:
part_uri_list.append(part_uri)
part_list.append(part)
else:
# Hipersockets or SMC-D
for bc in self.get_property('bus-connections'):
part_uri = bc.get('partition-uri')
part_name = bc.get('partition-name')
part = self.cpc.partitions.resource_object(
part_uri, {'name': part_name, 'nics': []})
if name:
if not re.match(name, part_name):
continue
if status:
if part.get_property('status') != status:
continue
if part_uri not in part_uri_list:
part_uri_list.append(part_uri)
part_list.append(part)
return part_list
[docs]
@logged_api_call
def wait_for_states(self, states=('complete', 'incomplete'), timeout=30):
"""
Wait for this partition link to reach one of the specified states.
If there are changes in partition connectivity underway, the partition
link' ``state`` property indicates whether the change is still going
on or has reached a stable state, as follows:
* "complete" - All partitions of this partition link are connected.
* "incomplete" - Less than 2 partitions are connected to this
partition link or an adapter is in degraded state.
* "updating" - Partitions are currently being added or removed from
this partition link.
This method pulls the ``state`` property every 2 seconds until one of
the specified states is reached. It times out after the specified
timeout.
HMC/SE version requirements:
* SE version >= 2.16.0
Authorization requirements:
* Object-access permission to this partition link.
Parameters:
states (list of string): The states to wait for.
timeout (int): The timeout in seconds. 0 means not to time out.
Raises:
:exc:`~zhmcclient.HTTPError`
:exc:`~zhmcclient.ParseError`
:exc:`~zhmcclient.AuthError`
:exc:`~zhmcclient.ConnectionError`
:exc:`~zhmcclient.OperationTimeout`: The timeout expired while
waiting for completion of the operation.
"""
if timeout > 0:
start_time = time.time()
while True:
try:
self.pull_properties(['state'])
state = self.properties['state']
if state in states:
return
except ConnectionError:
HMC_LOGGER.debug(
"Retrying after ConnectionError while waiting for states "
"%s in partition link %r (currently has state %s). This "
"could be because HMC is restarting.",
states, self.name, state)
if timeout > 0:
current_time = time.time()
if current_time > start_time + timeout:
raise OperationTimeout(
f"Waiting for states {states} in partition link "
f"{self.name} timed out (timeout: {timeout} s, "
f"currently has state {state})",
timeout)
time.sleep(2) # Avoid hot spin loop
[docs]
@logged_api_call
def delete(self, force_detach=False):
"""
Delete this partition link on the HMC.
If there are active partitions to which the partition link is attached,
the operation will fail by default. The 'force_detach' parameter can be
used to forcefully detach the partition link from active partitions
before deleting it.
This method performs the "Delete Partition Link" HMC operation and
waits for completion of its asynchronous job.
HMC/SE version requirements:
* SE version >= 2.16.0
* for technology-specific support, see the API features described in
:ref:`Partition Links`
Authorization requirements:
* Object-access permission to this partition link.
* Object-access permission to all partitions that currently have this
partition link attached.
* Task permission to the "Delete Partition Link" task.
Parameters:
force_detach (bool): Controls what to do with active partitions
associated with this partition link. If True, such partitions
are detached forcefully. If False, the operation fails with
status code 409 (Conflict) and reason code 100.
Raises:
:exc:`~zhmcclient.HTTPError`
:exc:`~zhmcclient.ParseError`
:exc:`~zhmcclient.AuthError`
:exc:`~zhmcclient.ConnectionError`
:exc:`~zhmcclient.OperationTimeout`: The timeout expired while
waiting for completion of the operation.
"""
body = {}
if force_detach:
body['force-detach'] = True
self.manager.session.post(
uri=f'{self.uri}/operations/delete',
resource=self,
body=body,
wait_for_completion=True,
busy_retries=1, busy_wait=2)
# 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 partition link.
This method can be used to attach and detach this partition link
to and from partitions, by modifying the appropriate resource
properties.
This method serializes with other methods that access or change
properties on the same Python object.
This method performs the "Modify Partition Link" HMC operation and
waits for completion of its asynchronous job.
SSC management NICs backed by a Hipersocket adapter can be created
by first attaching this partition link to a partition using the
"added-connections" property, and then updating the created NIC(s) with
the :meth:`zhmcclient.Nic.update_properties` method to become an SSC
management NIC.
Limitations:
* When the API feature "dpm-hipersockets-partition-link-management"
is enabled on a z16 CPC (and only there), SSC management NICs
cannot be backed by Hipersocket adapters. This limitation can be
lifted by installing the firmware feature "DPM IQD Links - Disable",
which can be requested by customers from IBM support.
HMC/SE version requirements:
* SE version >= 2.16.0
* for technology-specific support, see the API features described in
:ref:`Partition Links`
Authorization requirements:
* Task permission to the "Create Partition Link" task.
* Object-access permission to all Partitions for the initially requested
attachments.
* Object-access permission to all FICON adapter objects used for the
CTC connections for the initially requested attachments.
Parameters:
properties (dict): New values for the properties to be updated.
Properties not to be updated are omitted.
Allowable properties are listed for operation
'Modify Partition Link' in section 'Partition Link object' in the
:term:`HMC API` book.
Raises:
:exc:`~zhmcclient.HTTPError`
:exc:`~zhmcclient.ParseError`
:exc:`~zhmcclient.AuthError`
:exc:`~zhmcclient.ConnectionError`
:exc:`~zhmcclient.OperationTimeout`: The timeout expired while
waiting for completion of the operation.
"""
# pylint: disable=protected-access
result = self.manager.session.post(
uri=f'{self.uri}/operations/modify',
resource=self,
body=properties,
wait_for_completion=True,
busy_retries=1, busy_wait=2)
for op_result in result['operation-results']:
if op_result['operation-status'] \
not in ("attached", "detached"):
raise PartitionLinkError(result['operation-results'])
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 dump(self):
"""
Dump this PartitionLink resource with its properties as a resource
definition.
The returned resource definition has the following format::
{
# Resource properties:
"properties": {...},
}
Returns:
dict: Resource definition of this resource.
"""
# Dump the resource properties
resource_dict = super().dump()
return resource_dict