def process_properties(cpc, params):
    """
    Process the properties specified in the 'properties' module parameter,
    and return a dictionary (update_props) that contains the properties that
    can be updated. The input property values are compared with the existing
    resource property values and the returned set of properties is the minimal
    set of properties that need to be changed.

    - Underscores in the property names are translated into hyphens.
    - The presence of properties that cannot be updated is surfaced by raising
      ParameterError.

    Parameters:

      cpc (zhmcclient.Cpc): CPC to be updated.

      params (dict): Module input parameters.

    Returns:
      update_props: dict of properties for zhmcclient.Cpc.update_properties()

    Raises:
      ParameterError: An issue with the module parameters.
    """

    input_props = params.get('properties', None)
    if input_props is None:
        input_props = {}

    update_props = {}
    for prop_name in input_props:

        try:
            allowed, create, update, update_active, eq_func, type_cast = \
                ZHMC_CPC_PROPERTIES[prop_name]
        except KeyError:
            allowed = False

        if not allowed:
            raise ParameterError(
                "CPC property {!r} specified in the 'properties' module "
                "parameter cannot be updated.".format(prop_name))

        # Process a normal (= non-artificial) property
        _create_props, _update_props, _stop = process_normal_property(
            prop_name, ZHMC_CPC_PROPERTIES, input_props, cpc)
        update_props.update(_update_props)
        assert not _create_props
        assert _stop is False

    return update_props
def process_properties(partition, vfunction, params):
    """
    Process the properties specified in the 'properties' module parameter,
    and return two dictionaries (create_props, update_props) that contain
    the properties that can be created, and the properties that can be updated,
    respectively. If the resource exists, the input property values are
    compared with the existing resource property values and the returned set
    of properties is the minimal set of properties that need to be changed.

    - Underscores in the property names are translated into hyphens.
    - The presence of read-only properties, invalid properties (i.e. not
      defined in the data model for partitions), and properties that are not
      allowed because of restrictions or because they are auto-created from
      an artificial property is surfaced by raising ParameterError.
    - The properties resulting from handling artificial properties are
      added to the returned dictionaries.

    Parameters:

      partition (zhmcclient.Partition): Partition containing the virtual
        function. Must exist.

      vfunction (zhmcclient.VirtualFunction): Virtual function to be updated
        with the full set of current properties, or `None` if it did not
        previously exist.

      params (dict): Module input parameters.

    Returns:
      tuple of (create_props, update_props, stop), where:
        * create_props: dict of properties for
          zhmcclient.VirtualFunctionManager.create()
        * update_props: dict of properties for
          zhmcclient.VirtualFunction.update_properties()
        * stop (bool): Indicates whether some update properties require the
          partition containg the virtual function to be stopped when doing the
          update.

    Raises:
      ParameterError: An issue with the module parameters.
    """
    create_props = {}
    update_props = {}
    stop = False

    # handle 'name' property
    vfunction_name = to_unicode(params['name'])
    create_props['name'] = vfunction_name
    # We looked up the virtual function by name, so we will never have to
    # update its name

    # Names of the artificial properties
    adapter_name_art_name = 'adapter_name'

    # handle the other properties
    input_props = params.get('properties', {})
    if input_props is None:
        input_props = {}
    for prop_name in input_props:

        if prop_name not in ZHMC_VFUNCTION_PROPERTIES:
            raise ParameterError(
                "Property {!r} is not defined in the data model for "
                "virtual functions.".format(prop_name))

        allowed, create, update, update_while_active, eq_func, type_cast = \
            ZHMC_VFUNCTION_PROPERTIES[prop_name]

        if not allowed:
            raise ParameterError(
                "Property {!r} is not allowed in the 'properties' module "
                "parameter.".format(prop_name))

        if prop_name == adapter_name_art_name:
            # Artificial properties will be processed together after this loop
            continue

        # Process a normal (= non-artificial) property
        _create_props, _update_props, _stop = process_normal_property(
            prop_name, ZHMC_VFUNCTION_PROPERTIES, input_props, vfunction)
        create_props.update(_create_props)
        update_props.update(_update_props)
        if _stop:
            stop = True

    # Process artificial properties
    if adapter_name_art_name in input_props:
        adapter_name = to_unicode(input_props[adapter_name_art_name])
        try:
            adapter = partition.manager.cpc.adapters.find(name=adapter_name)
        except zhmcclient.NotFound:
            raise ParameterError(
                "Artificial property {!r} does not specify the name of an "
                "existing adapter: {!r}".format(adapter_name_art_name,
                                                adapter_name))

        # Here we perform the same logic as in the property loop, just now
        # simplified by the knowledge about the property flags (create, update,
        # etc.).
        hmc_prop_name = 'adapter-uri'
        input_prop_value = adapter.uri
        if vfunction:
            if vfunction.properties.get(hmc_prop_name) != input_prop_value:
                update_props[hmc_prop_name] = input_prop_value
        else:
            update_props[hmc_prop_name] = input_prop_value
        create_props[hmc_prop_name] = input_prop_value

    return create_props, update_props, stop
def process_properties(cpc, partition, params):
    """
    Process the properties specified in the 'properties' module parameter,
    and return two dictionaries (create_props, update_props) that contain
    the properties that can be created, and the properties that can be updated,
    respectively. If the resource exists, the input property values are
    compared with the existing resource property values and the returned set
    of properties is the minimal set of properties that need to be changed.

    - Underscores in the property names are translated into hyphens.
    - The presence of read-only properties, invalid properties (i.e. not
      defined in the data model for partitions), and properties that are not
      allowed because of restrictions or because they are auto-created from
      an artificial property is surfaced by raising ParameterError.
    - The properties resulting from handling artificial properties are
      added to the returned dictionaries.

    Parameters:

      cpc (zhmcclient.Cpc): CPC with the partition to be updated, and
        with the adapters to be used for the partition.

      partition (zhmcclient.Partition): Partition to be updated with the full
        set of current properties, or `None` if it did not previously exist.

      params (dict): Module input parameters.

    Returns:
      tuple of (create_props, update_props, stop, crypto_changes), where:
        * create_props: dict of properties for
          zhmcclient.PartitionManager.create()
        * update_props: dict of properties for
          zhmcclient.Partition.update_properties()
        * stop (bool): Indicates whether some update properties require the
          partition to be stopped when doing the update.
        * crypto_changes (tuple): Changes to the crypto configuration if any
          (or `None` if no changes were specified), as a tuple of:
          * remove_adapters: List of Adapter objects to be removed
          * remove_domain_indexes: List of domain indexes to be removed
          * add_adapters: List of Adapter objects to be added
          * add_domain_configs: List of domain configs to be added (dict of
            'domain-index', 'access-mode')
          * change_domain_configs: List of domain configs for changing the
            access mode of existing domain indexes.

    Raises:
      ParameterError: An issue with the module parameters.
    """
    create_props = {}
    update_props = {}
    stop = False
    crypto_changes = None

    # handle 'name' property
    part_name = to_unicode(params['name'])
    create_props['name'] = part_name
    # We looked up the partition by name, so we will never have to update
    # the partition name

    # handle the other properties
    input_props = params.get('properties', {})
    if input_props is None:
        input_props = {}
    for prop_name in input_props:

        if prop_name not in ZHMC_PARTITION_PROPERTIES:
            raise ParameterError(
                "Property {!r} is not defined in the data model for "
                "partitions.".format(prop_name))

        allowed, create, update, update_while_active, eq_func, type_cast = \
            ZHMC_PARTITION_PROPERTIES[prop_name]

        if not allowed:
            raise ParameterError(
                "Property {!r} is not allowed in the 'properties' module "
                "parameter.".format(prop_name))

        if prop_name == 'boot_storage_hba_name':
            # Process this artificial property

            if not partition:
                raise ParameterError(
                    "Artificial property {!r} can only be specified when the "
                    "partition previously exists.".format(prop_name))

            if partition.hbas is None:
                raise ParameterError(
                    "Artificial property {!r} can only be specified when the "
                    "'dpm-storage-management' feature is disabled.".
                    format(prop_name))

            hba_name = input_props[prop_name]
            if type_cast:
                hba_name = type_cast(hba_name)

            try:
                hba = partition.hbas.find(name=hba_name)
            except zhmcclient.NotFound:
                raise ParameterError(
                    "Artificial property {!r} does not name an existing HBA: "
                    "{!r}".format(prop_name, hba_name))

            hmc_prop_name = 'boot-storage-device'
            if partition.properties.get(hmc_prop_name) != hba.uri:
                update_props[hmc_prop_name] = hba.uri
                assert update_while_active

        elif prop_name == 'boot_network_nic_name':
            # Process this artificial property

            if not partition:
                raise ParameterError(
                    "Artificial property {!r} can only be specified when the "
                    "partition previously exists.".format(prop_name))

            nic_name = input_props[prop_name]
            if type_cast:
                nic_name = type_cast(nic_name)

            try:
                nic = partition.nics.find(name=nic_name)
            except zhmcclient.NotFound:
                raise ParameterError(
                    "Artificial property {!r} does not name an existing NIC: "
                    "{!r}".format(prop_name, nic_name))

            hmc_prop_name = 'boot-network-device'
            if partition.properties.get(hmc_prop_name) != nic.uri:
                update_props[hmc_prop_name] = nic.uri
                assert update_while_active

        elif prop_name == 'crypto_configuration':
            # Process this artificial property

            crypto_config = input_props[prop_name]

            if not isinstance(crypto_config, dict):
                raise ParameterError(
                    "Artificial property {!r} is not a dictionary: {!r}.".
                    format(prop_name, crypto_config))

            if partition:
                hmc_prop_name = 'crypto-configuration'
                current_crypto_config = partition.properties.get(hmc_prop_name)
            else:
                current_crypto_config = None

            # Determine adapter changes
            try:
                adapter_field_name = 'crypto_adapter_names'
                adapter_names = crypto_config[adapter_field_name]
            except KeyError:
                raise ParameterError(
                    "Artificial property {!r} does not have required field "
                    "{!r}.".format(prop_name, adapter_field_name))
            adapter_uris = set()
            adapter_dict = {}  # adapters by uri
            if adapter_names is None:
                # Default: Use all crypto adapters of the CPC
                adapters = cpc.adapters.findall(type='crypto')
                for adapter in adapters:
                    adapter_dict[adapter.uri] = adapter
                    adapter_uris.add(adapter.uri)
            else:
                for adapter_name in adapter_names:
                    try:
                        adapter = cpc.adapters.find(name=adapter_name,
                                                    type='crypto')
                    except zhmcclient.NotFound:
                        raise ParameterError(
                            "Artificial property {!r} does not specify the "
                            "name of an existing crypto adapter in its {!r} "
                            "field: {!r}".
                            format(prop_name, adapter_field_name,
                                   adapter_name))
                    adapter_dict[adapter.uri] = adapter
                    adapter_uris.add(adapter.uri)
            if current_crypto_config:
                current_adapter_uris = set(
                    current_crypto_config['crypto-adapter-uris'])
            else:
                current_adapter_uris = set()
            if adapter_uris != current_adapter_uris:
                add_adapter_uris = adapter_uris - current_adapter_uris
                # Result: List of adapters to be added:
                add_adapters = [adapter_dict[uri] for uri in add_adapter_uris]
                remove_adapter_uris = current_adapter_uris - adapter_uris
                for uri in remove_adapter_uris:
                    adapter = cpc.adapters.find(**{'object-uri': uri})
                    # We assume the current crypto config lists only valid URIs
                    adapter_dict[adapter.uri] = adapter
                # Result: List of adapters to be removed:
                remove_adapters = \
                    [adapter_dict[uri] for uri in remove_adapter_uris]
            else:
                # Result: List of adapters to be added:
                add_adapters = []
                # Result: List of adapters to be removed:
                remove_adapters = []

            # Determine domain config changes.
            try:
                config_field_name = 'crypto_domain_configurations'
                domain_configs = crypto_config[config_field_name]
            except KeyError:
                raise ParameterError(
                    "Artificial property {!r} does not have required field "
                    "{!r}.".format(prop_name, config_field_name))
            di_field_name = 'domain_index'
            am_field_name = 'access_mode'
            domain_indexes = set()
            for dc in domain_configs:
                try:
                    # Convert to integer in case the domain index is provided
                    # as a string:
                    domain_index = int(dc[di_field_name])
                except KeyError:
                    raise ParameterError(
                        "Artificial property {!r} does not have required "
                        "sub-field {!r} in one of its {!r} fields.".
                        format(prop_name, di_field_name, config_field_name))
                domain_indexes.add(domain_index)
            current_access_mode_dict = {}  # dict: acc.mode by dom.index
            if current_crypto_config:
                current_domain_configs = \
                    current_crypto_config['crypto-domain-configurations']
                di_prop_name = 'domain-index'
                am_prop_name = 'access-mode'
                for dc in current_domain_configs:
                    # Here the domain index is always an integer because it is
                    # returned from the HMC that way, so no type cast needed.
                    current_access_mode_dict[dc[di_prop_name]] = \
                        dc[am_prop_name]
            current_domain_indexes = \
                set([di for di in current_access_mode_dict])
            # Result: List of domain indexes to be removed:
            remove_domain_indexes = \
                list(current_domain_indexes - domain_indexes)
            # Building result: List of domain configs to be added:
            add_domain_configs = []
            # Building result: List of domain configs to be changed:
            change_domain_configs = []
            for dc in domain_configs:
                # Convert to integer in case the domain index is provided
                # as a string:
                domain_index = int(dc[di_field_name])
                try:
                    access_mode = dc[am_field_name]
                except KeyError:
                    raise ParameterError(
                        "Artificial property {!r} does not have required "
                        "sub-field {!r} in one of its {!r} fields.".
                        format(prop_name, am_field_name, config_field_name))
                hmc_domain_config = {
                    'domain-index': domain_index,
                    'access-mode': access_mode,
                }
                if domain_index not in current_access_mode_dict:
                    # Domain is not included yet
                    add_domain_configs.append(hmc_domain_config)
                elif access_mode != current_access_mode_dict[domain_index]:
                    # Domain is included but access mode needs to be changed
                    change_domain_configs.append(hmc_domain_config)

            crypto_changes = (remove_adapters, remove_domain_indexes,
                              add_adapters, add_domain_configs,
                              change_domain_configs)

        else:
            # Process a normal (= non-artificial) property
            if prop_name == 'ssc_ipv4_gateway':
                # Undo conversion from None to empty string in Ansible
                if input_props[prop_name] == '':
                    input_props[prop_name] = None
            _create_props, _update_props, _stop = process_normal_property(
                prop_name, ZHMC_PARTITION_PROPERTIES, input_props, partition)
            create_props.update(_create_props)
            update_props.update(_update_props)
            if _stop:
                stop = True

    return create_props, update_props, stop, crypto_changes
def process_properties(console, user, params):
    """
    Process the properties specified in the 'properties' module parameter,
    and return two dictionaries (create_props, update_props) that contain
    the properties that can be created, and the properties that can be updated,
    respectively. If the resource exists, the input property values are
    compared with the existing resource property values and the returned set
    of properties is the minimal set of properties that need to be changed.

    - Underscores in the property names are translated into hyphens.
    - The presence of read-only properties, invalid properties (i.e. not
      defined in the data model for users), and properties that are
      not allowed because of restrictions or because they are auto-created from
      an artificial property is surfaced by raising ParameterError.

    Parameters:

      user (zhmcclient.User): User object to be updated with the full set of
        current properties, or `None` if it did not previously exist.

      params (dict): Module input parameters.

    Returns:
      tuple of (create_props, update_props), where:
        * create_props: dict of properties for
          zhmcclient.UserManager.create()
        * update_props: dict of properties for
          zhmcclient.User.update_properties()

    Raises:
      ParameterError: An issue with the module parameters.
    """
    create_props = {}
    update_props = {}

    # handle 'name' property
    user_name = to_unicode(params['name'])
    if user is None:
        # User does not exist yet.
        create_props['name'] = user_name
    else:
        # User does already exist.
        # We looked up the user by name, so we will never have to
        # update the user name.
        pass

    # handle the other properties
    input_props = params.get('properties', None)
    if input_props is None:
        input_props = {}

    # Check required input properties
    for prop_name in ('type', 'authentication_type'):
        if prop_name not in input_props:
            raise ParameterError(
                "Property {!r} is required but is missing in the module "
                "input parameters.".format(prop_name))
    auth_type = input_props['authentication_type']
    if auth_type == 'local':
        for prop_name in ('password_rule_name', 'password'):
            if prop_name not in input_props:
                raise ParameterError(
                    "Property {!r} is required for "
                    "authentication_type='local' but is missing in the "
                    "module input parameters.".format(prop_name))
    if auth_type == 'ldap':
        for prop_name in ('ldap_server_definition_name', ):
            if prop_name not in input_props:
                raise ParameterError(
                    "Property {!r} is required for "
                    "authentication_type='ldap' but is missing in the "
                    "module input parameters.".format(prop_name))

    for prop_name in input_props:

        if prop_name not in ZHMC_USER_PROPERTIES:
            raise ParameterError(
                "Property {!r} is not defined in the data model for "
                "users.".format(prop_name))

        allowed, create, update, update_while_active, eq_func, type_cast = \
            ZHMC_USER_PROPERTIES[prop_name]

        if not allowed:
            raise ParameterError(
                "Property {!r} is not allowed in the 'properties' module "
                "parameter.".format(prop_name))

        # Process artificial properties allowed in input parameters

        if prop_name == 'user_pattern_name':
            user_pattern_name = input_props[prop_name]
            if user_pattern_name:
                user_pattern = console.user_patterns.find_by_name(
                    user_pattern_name)
                user_pattern_uri = user_pattern.uri
            else:
                user_pattern_uri = None
            if user is None:
                create_props['user-pattern-uri'] = user_pattern_uri
            elif user.prop('user-pattern-uri') != user_pattern_uri:
                update_props['user-pattern-uri'] = user_pattern_uri
            continue

        if prop_name == 'password_rule_name':
            password_rule_name = input_props[prop_name]
            if password_rule_name:
                password_rule = console.password_rules.find_by_name(
                    password_rule_name)
                password_rule_uri = password_rule.uri
            else:
                password_rule_uri = None
            if user is None:
                create_props['password-rule-uri'] = password_rule_uri
            elif user.prop('password-rule-uri') != password_rule_uri:
                update_props['password-rule-uri'] = password_rule_uri
            continue

        if prop_name == 'ldap_server_definition_name':
            ldap_srv_def_name = input_props[prop_name]
            if ldap_srv_def_name:
                ldap_srv_def = console.ldap_server_definitions.find_by_name(
                    ldap_srv_def_name)
                ldap_srv_def_uri = ldap_srv_def.uri
            else:
                ldap_srv_def_uri = None
            if user is None:
                create_props['ldap-server-definition-uri'] = ldap_srv_def_uri
            elif user.prop('ldap-server-definition-uri') != ldap_srv_def_uri:
                update_props['ldap-server-definition-uri'] = ldap_srv_def_uri
            continue

        if prop_name == 'default_group_name':
            default_group_name = input_props[prop_name]
            # TODO: Add support for Group objects to zhmcclient
            # default_group = console.groups.find_by_name(default_group_name)
            default_group_uri = 'fake-uri-{}'.format(default_group_name)
            if user is None:
                create_props['default-group-uri'] = default_group_uri
            elif user.prop('default-group-uri') != default_group_uri:
                update_props['default-group-uri'] = default_group_uri
            continue

        # Process a normal (= non-artificial) property
        _create_props, _update_props, _stop = process_normal_property(
            prop_name, ZHMC_USER_PROPERTIES, input_props, user)
        create_props.update(_create_props)
        update_props.update(_update_props)
        assert _stop is False

    return create_props, update_props
def process_properties(partition, hba, params):
    """
    Process the properties specified in the 'properties' module parameter,
    and return two dictionaries (create_props, update_props) that contain
    the properties that can be created, and the properties that can be updated,
    respectively. If the resource exists, the input property values are
    compared with the existing resource property values and the returned set
    of properties is the minimal set of properties that need to be changed.

    - Underscores in the property names are translated into hyphens.
    - The presence of read-only properties, invalid properties (i.e. not
      defined in the data model for partitions), and properties that are not
      allowed because of restrictions or because they are auto-created from
      an artificial property is surfaced by raising ParameterError.
    - The properties resulting from handling artificial properties are
      added to the returned dictionaries.

    Parameters:

      partition (zhmcclient.Partition): Partition containing the HBA. Must
        exist.

      hba (zhmcclient.Hba): HBA to be updated with the full set of current
        properties, or `None` if it did not previously exist.

      params (dict): Module input parameters.

    Returns:
      tuple of (create_props, update_props, stop), where:
        * create_props: dict of properties for
          zhmcclient.HbaManager.create()
        * update_props: dict of properties for
          zhmcclient.Hba.update_properties()
        * stop (bool): Indicates whether some update properties require the
          partition containg the HBA to be stopped when doing the update.

    Raises:
      ParameterError: An issue with the module parameters.
    """
    create_props = {}
    update_props = {}
    stop = False

    # handle 'name' property
    hba_name = to_unicode(params['name'])
    create_props['name'] = hba_name
    # We looked up the HBA by name, so we will never have to update its name

    # Names of the artificial properties
    adapter_name_art_name = 'adapter_name'
    adapter_port_art_name = 'adapter_port'

    # handle the other properties
    input_props = params.get('properties', {})
    if input_props is None:
        input_props = {}
    for prop_name in input_props:

        if prop_name not in ZHMC_HBA_PROPERTIES:
            raise ParameterError(
                "Property {!r} is not defined in the data model for "
                "HBAs.".format(prop_name))

        allowed, create, update, update_while_active, eq_func, type_cast = \
            ZHMC_HBA_PROPERTIES[prop_name]

        if not allowed:
            raise ParameterError(
                "Property {!r} is not allowed in the 'properties' module "
                "parameter.".format(prop_name))

        if prop_name in (adapter_name_art_name, adapter_port_art_name):
            # Artificial properties will be processed together after this loop
            continue

        # Process a normal (= non-artificial) property
        _create_props, _update_props, _stop = process_normal_property(
            prop_name, ZHMC_HBA_PROPERTIES, input_props, hba)
        create_props.update(_create_props)
        update_props.update(_update_props)
        if _stop:
            stop = True

    # Process artificial properties
    if (adapter_name_art_name in input_props) != \
            (adapter_port_art_name in input_props):
        raise ParameterError(
            "Artificial properties {!r} and {!r} must either both be "
            "specified or both be omitted.".format(adapter_name_art_name,
                                                   adapter_port_art_name))
    if adapter_name_art_name in input_props and \
            adapter_port_art_name in input_props:
        adapter_name = to_unicode(input_props[adapter_name_art_name])
        adapter_port_index = int(input_props[adapter_port_art_name])
        try:
            adapter = partition.manager.cpc.adapters.find(name=adapter_name)
        except zhmcclient.NotFound:
            raise ParameterError(
                "Artificial property {!r} does not specify the name of an "
                "existing adapter: {!r}".format(adapter_name_art_name,
                                                adapter_name))
        try:
            port = adapter.ports.find(index=adapter_port_index)
        except zhmcclient.NotFound:
            raise ParameterError(
                "Artificial property {!r} does not specify the index of an "
                "existing port on adapter {!r}: {!r}".format(
                    adapter_port_art_name, adapter_name, adapter_port_index))
        hmc_prop_name = 'adapter-port-uri'
        if hba:
            existing_port_uri = hba.get_property(hmc_prop_name)
            if port.uri != existing_port_uri:
                raise ParameterError(
                    "Artificial properties {!r} and {!r} cannot be used to "
                    "change the adapter port of an existing HBA".format(
                        adapter_name_art_name, adapter_port_art_name))
        create_props[hmc_prop_name] = port.uri

    return create_props, update_props, stop
def process_properties(partition, nic, params):
    """
    Process the properties specified in the 'properties' module parameter,
    and return two dictionaries (create_props, update_props) that contain
    the properties that can be created, and the properties that can be updated,
    respectively. If the resource exists, the input property values are
    compared with the existing resource property values and the returned set
    of properties is the minimal set of properties that need to be changed.

    - Underscores in the property names are translated into hyphens.
    - The presence of read-only properties, invalid properties (i.e. not
      defined in the data model for partitions), and properties that are not
      allowed because of restrictions or because they are auto-created from
      an artificial property is surfaced by raising ParameterError.
    - The properties resulting from handling artificial properties are
      added to the returned dictionaries.

    Parameters:

      partition (zhmcclient.Partition): Partition containing the NIC. Must
        exist.

      nic (zhmcclient.Nic): NIC to be updated with the full set of current
        properties, or `None` if it did not previously exist.

      params (dict): Module input parameters.

    Returns:
      tuple of (create_props, update_props, stop), where:
        * create_props: dict of properties for
          zhmcclient.NicManager.create()
        * update_props: dict of properties for
          zhmcclient.Nic.update_properties()
        * stop (bool): Indicates whether some update properties require the
          partition containg the NIC to be stopped when doing the update.

    Raises:
      ParameterError: An issue with the module parameters.
    """
    create_props = {}
    update_props = {}
    stop = False

    # handle 'name' property
    nic_name = to_unicode(params['name'])
    create_props['name'] = nic_name
    # We looked up the NIC by name, so we will never have to update its name

    # Names of the artificial properties
    adapter_name_art_name = 'adapter_name'
    adapter_port_art_name = 'adapter_port'

    # handle the other properties
    input_props = params.get('properties', {})
    if input_props is None:
        input_props = {}
    for prop_name in input_props:

        if prop_name not in ZHMC_NIC_PROPERTIES:
            raise ParameterError(
                "Property {!r} is not defined in the data model for "
                "NICs.".format(prop_name))

        allowed, create, update, update_while_active, eq_func, type_cast = \
            ZHMC_NIC_PROPERTIES[prop_name]

        if not allowed:
            raise ParameterError(
                "Property {!r} is not allowed in the 'properties' module "
                "parameter.".format(prop_name))

        if prop_name in (adapter_name_art_name, adapter_port_art_name):
            # Artificial properties will be processed together after this loop
            continue

        # Process a normal (= non-artificial) property
        _create_props, _update_props, _stop = process_normal_property(
            prop_name, ZHMC_NIC_PROPERTIES, input_props, nic)
        create_props.update(_create_props)
        update_props.update(_update_props)
        if _stop:
            stop = True

    # Process artificial properties
    if (adapter_name_art_name in input_props) != \
            (adapter_port_art_name in input_props):
        raise ParameterError(
            "Artificial properties {!r} and {!r} must either both be "
            "specified or both be omitted.".format(adapter_name_art_name,
                                                   adapter_port_art_name))
    if adapter_name_art_name in input_props and \
            adapter_port_art_name in input_props:
        adapter_name = to_unicode(input_props[adapter_name_art_name])
        adapter_port_index = int(input_props[adapter_port_art_name])
        try:
            adapter = partition.manager.cpc.adapters.find(name=adapter_name)
        except zhmcclient.NotFound:
            raise ParameterError(
                "Artificial property {!r} does not specify the name of an "
                "existing adapter: {!r}".format(adapter_name_art_name,
                                                adapter_name))
        try:
            port = adapter.ports.find(index=adapter_port_index)
        except zhmcclient.NotFound:
            raise ParameterError(
                "Artificial property {!r} does not specify the index of an "
                "existing port on adapter {!r}: {!r}".format(
                    adapter_port_art_name, adapter_name, adapter_port_index))

        # The rest of it depends on the network adapter family:
        adapter_family = adapter.get_property('adapter-family')
        if adapter_family == 'roce':
            # Here we perform the same logic as in the property loop, just now
            # simplified by the knowledge about the property flags (create,
            # update, etc.).
            hmc_prop_name = 'network-adapter-port-uri'
            input_prop_value = port.uri
            if nic:
                if nic.properties.get(hmc_prop_name) != input_prop_value:
                    update_props[hmc_prop_name] = input_prop_value
            else:
                update_props[hmc_prop_name] = input_prop_value
            create_props[hmc_prop_name] = input_prop_value
        elif adapter_family in ('osa', 'hipersockets'):
            vswitches = partition.manager.cpc.virtual_switches.findall(
                **{'backing-adapter-uri': adapter.uri})
            # Adapters of this family always have a vswitch (one for each
            # port), so we assert that we can find one or more:
            assert vswitches
            found_vswitch = None
            for vswitch in vswitches:
                if vswitch.get_property('port') == adapter_port_index:
                    found_vswitch = vswitch
                    break
            # Because we already checked for the existence of the specified
            # port index, we can now assert that we found the vswitch for that
            # port:
            assert found_vswitch
            # Here we perform the same logic as in the property loop, just now
            # simplified by the knowledge about the property flags (create,
            # update, etc.).
            hmc_prop_name = 'virtual-switch-uri'
            input_prop_value = found_vswitch.uri
            if nic:
                if nic.properties.get(hmc_prop_name) != input_prop_value:
                    update_props[hmc_prop_name] = input_prop_value
            else:
                update_props[hmc_prop_name] = input_prop_value
            create_props[hmc_prop_name] = input_prop_value
        else:
            raise ParameterError(
                "Artificial property {!r} specifies the name of a non-network "
                "adapter of family {!r}: {!r}".format(adapter_name_art_name,
                                                      adapter_family,
                                                      adapter_name))

    return create_props, update_props, stop
Exemple #7
0
def process_properties(cpc, storage_group, params):
    """
    Process the properties specified in the 'properties' module parameter,
    and return two dictionaries (create_props, update_props) that contain
    the properties that can be created, and the properties that can be updated,
    respectively. If the resource exists, the input property values are
    compared with the existing resource property values and the returned set
    of properties is the minimal set of properties that need to be changed.

    - Underscores in the property names are translated into hyphens.
    - The presence of read-only properties, invalid properties (i.e. not
      defined in the data model for storage groups), and properties that are
      not allowed because of restrictions or because they are auto-created from
      an artificial property is surfaced by raising ParameterError.
    - The properties resulting from handling artificial properties are
      added to the returned dictionaries.

    Parameters:

      cpc (zhmcclient.Cpc): CPC with the partition to be updated, and
        with the adapters to be used for the partition.

      storage_group (zhmcclient.StorageGroup): Storage group object to be
        updated with the full set of current properties, or `None` if it did
        not previously exist.

      params (dict): Module input parameters.

    Returns:
      tuple of (create_props, update_props), where:
        * create_props: dict of properties for
          zhmcclient.StorageGroupManager.create()
        * update_props: dict of properties for
          zhmcclient.StorageGroup.update_properties()

    Raises:
      ParameterError: An issue with the module parameters.
    """
    create_props = {}
    update_props = {}

    # handle 'name' and 'cpc-uri' properties.
    sg_name = to_unicode(params['name'])
    if storage_group is None:
        # SG does not exist yet.
        create_props['name'] = sg_name
        create_props['cpc-uri'] = cpc.uri
    else:
        # SG does already exist.
        # We looked up the storage group by name, so we will never have to
        # update the storage group name.
        # Updates to the associated CPC are not possible.
        sg_cpc = storage_group.cpc
        if sg_cpc.uri != cpc.uri:
            raise ParameterError(
                "Storage group {!r} is not associated with the specified "
                "CPC %r, but with CPC %r.".format(sg_name, cpc.name,
                                                  sg_cpc.name))

    # handle the other properties
    input_props = params.get('properties', None)
    if input_props is None:
        input_props = {}
    for prop_name in input_props:

        if prop_name not in ZHMC_STORAGE_GROUP_PROPERTIES:
            raise ParameterError(
                "Property {!r} is not defined in the data model for "
                "storage groups.".format(prop_name))

        allowed, create, update, update_while_active, eq_func, type_cast = \
            ZHMC_STORAGE_GROUP_PROPERTIES[prop_name]

        if not allowed:
            raise ParameterError(
                "Property {!r} is not allowed in the 'properties' module "
                "parameter.".format(prop_name))

        # Process a normal (= non-artificial) property
        _create_props, _update_props, _stop = process_normal_property(
            prop_name, ZHMC_STORAGE_GROUP_PROPERTIES, input_props,
            storage_group)
        create_props.update(_create_props)
        update_props.update(_update_props)
        assert _stop is False

    return create_props, update_props
Exemple #8
0
def process_properties(adapter, params):
    """
    Process the properties specified in the 'properties' module parameter,
    and return a dictionary (update_props) that contains the properties that
    can be updated. The input property values are compared with the existing
    resource property values and the returned set of properties is the minimal
    set of properties that need to be changed.

    - Underscores in the property names are translated into hyphens.
    - The presence of properties that cannot be updated is surfaced by raising
      ParameterError.

    Parameters:

      adapter (zhmcclient.Adapter): Existing adapter to be updated, or `None`
        if the adapter does not exist.

      params (dict): Module input parameters.

    Returns:
      tuple of (create_props, update_props), where:
        * create_props: dict of properties from params that may be specified
          in zhmcclient.AdapterManager.create_hipersocket() (may overlap with
          update_props).
        * update_props: dict of properties from params that may be specified
          in zhmcclient.Adapter.update_properties() (may overlap with
          create_props).
        * change_adapter_type: String with new adapter type (i.e. input for
          Change Adapter Type operation), or None if no change needed.
        * change_crypto_type: String with new crypto type (i.e. input for
          Change Crypto Type operation), or None if no change needed.

    Raises:
      ParameterError: An issue with the module parameters.
    """

    # Prepare return values
    create_props = {}
    update_props = {}
    change_adapter_type = None  # New adapter type, if needed
    change_crypto_type = None  # New crypto type, if needed

    # handle the 'name' module parameter
    adapter_name = to_unicode(params['name'])
    if adapter and adapter.properties.get('name', None) == adapter_name:
        pass  # adapter exists and has the desired name
    else:
        create_props['name'] = adapter_name
        update_props['name'] = adapter_name

    # handle the other input properties
    input_props = params.get('properties', None)
    if input_props is None:
        input_props = {}
    for prop_name in input_props:

        try:
            allowed, create, update, update_active, eq_func, type_cast = \
                ZHMC_ADAPTER_PROPERTIES[prop_name]
        except KeyError:
            allowed = False

        if not allowed:
            raise ParameterError(
                "Invalid adapter property {!r} specified in the 'properties' "
                "module parameter.".format(prop_name))

        if prop_name == 'type':
            # Determine need to change the adapter type
            _current_adapter_type = adapter.properties.get('type', None)
            _input_adapter_type = input_props[prop_name]
            if _input_adapter_type != _current_adapter_type:
                change_adapter_type = _input_adapter_type
        elif prop_name == 'crypto_type':
            # Determine need to change the crypto type
            _current_crypto_type = adapter.properties.get('crypto-type', None)
            _input_crypto_type = CRYPTO_TYPES_MOD2HMC[input_props[prop_name]]
            if _input_crypto_type != _current_crypto_type:
                change_crypto_type = _input_crypto_type
        else:
            # Process a normal (= non-artificial) property
            _create_props, _update_props, _stop = process_normal_property(
                prop_name, ZHMC_ADAPTER_PROPERTIES, input_props, adapter)
            create_props.update(_create_props)
            update_props.update(_update_props)
            assert _stop is False

    return create_props, update_props, change_adapter_type, change_crypto_type