def ensure_present(params, check_mode): """ Ensure that the HBA exists and has the specified properties. Raises: ParameterError: An issue with the module parameters. StatusError: An issue with the partition status. zhmcclient.Error: Any zhmcclient exception can happen. """ host = params['hmc_host'] userid, password = get_hmc_auth(params['hmc_auth']) cpc_name = params['cpc_name'] partition_name = params['partition_name'] hba_name = params['name'] _faked_session = params.get('_faked_session', None) changed = False result = {} try: session = get_session(_faked_session, host, userid, password) client = zhmcclient.Client(session) cpc = client.cpcs.find(name=cpc_name) # The default exception handling is sufficient for the above. try: partition = cpc.partitions.find(name=partition_name) except zhmcclient.NotFound: if check_mode: # Once the partition is created, the HBA will also need to be # created. Therefore, we set changed. changed = True return changed, result raise try: hba = partition.hbas.find(name=hba_name) hba.pull_full_properties() except zhmcclient.NotFound: hba = None if not hba: # It does not exist. Create it and update it if there are # update-only properties. if not check_mode: create_props, update_props, stop = process_properties( partition, hba, params) hba = partition.hbas.create(create_props) update2_props = {} for name in update_props: if name not in create_props: update2_props[name] = update_props[name] if update2_props: hba.update_properties(update2_props) # We refresh the properties after the update, in case an # input property value gets changed (for example, the # partition does that with memory properties). hba.pull_full_properties() else: # TODO: Show props in module result also in check mode. pass changed = True else: # It exists. Stop the partition if needed due to the HBA property # update requirements, or wait for an updateable partition status, # and update the HBA properties. create_props, update_props, stop = process_properties( partition, hba, params) if update_props: if not check_mode: # HBA properties can all be updated while the partition is # active, therefore: if stop: raise AssertionError() wait_for_transition_completion(partition) hba.update_properties(update_props) # We refresh the properties after the update, in case an # input property value gets changed (for example, the # partition does that with memory properties). hba.pull_full_properties() else: # TODO: Show updated props in mod.result also in chk.mode pass changed = True if hba: result = hba.properties return changed, result finally: session.logoff()
def ensure_present(params, check_mode): """ Ensure that the user role exists and has the specified properties. Raises: ParameterError: An issue with the module parameters. zhmcclient.Error: Any zhmcclient exception can happen. """ host = params['hmc_host'] userid, password, ca_certs, verify = get_hmc_auth(params['hmc_auth']) urole_name = params['name'] _faked_session = params.get('_faked_session', None) changed = False result = {} session = get_session( _faked_session, host, userid, password, ca_certs, verify) try: client = zhmcclient.Client(session) console = client.consoles.console # The default exception handling is sufficient for the above. try: urole = console.user_roles.find(name=urole_name) except zhmcclient.NotFound: urole = None if urole is None: # It does not exist. Create it and update it if there are # update-only properties. create_props, update_props, cur_perms, add_perms, rem_perms = \ process_properties(client, urole, params) update2_props = {} for name, value in update_props.items(): if name not in create_props: update2_props[name] = value if not check_mode: urole = console.user_roles.create(create_props) if update2_props: urole.update_properties(update2_props) # We refresh the properties after the update, in case an # input property value gets changed. urole.pull_full_properties() else: # Create a User Role object locally urole = create_check_mode_urole( client, create_props, update2_props) result = dict(urole.properties) changed = True for perm_key in rem_perms: opt_kwargs, obj = rem_perms[perm_key] if obj is None: # resource class kwargs = dict(permitted_object=perm_key) else: kwargs = dict(permitted_object=obj) kwargs.update(opt_kwargs) LOGGER.debug( "Removing permission %r from user role %r", kwargs, urole_name) if not check_mode: urole.remove_permission(**kwargs) del cur_perms[perm_key] for perm_key in add_perms: opt_kwargs, obj = add_perms[perm_key] if obj is None: # resource class kwargs = dict(permitted_object=perm_key) else: kwargs = dict(permitted_object=obj) kwargs.update(opt_kwargs) LOGGER.debug( "Adding permission %r to user role %r", kwargs, urole_name) if not check_mode: urole.add_permission(**kwargs) cur_perms[perm_key] = add_perms[perm_key] else: # It exists. Update its properties. urole.pull_full_properties() result = dict(urole.properties) create_props, update_props, cur_perms, add_perms, rem_perms = \ process_properties(client, urole, params) if update_props: LOGGER.debug( "Existing user role %r needs to get properties " "updated: %r", urole_name, update_props) if not check_mode: urole.update_properties(update_props) # We refresh the properties after the update, in case an # input property value gets changed. urole.pull_full_properties() result = dict(urole.properties) else: # Update the local User Role object's properties result.update(update_props) changed = True for perm_key in rem_perms: opt_kwargs, obj = rem_perms[perm_key] if obj is None: # resource class kwargs = dict(permitted_object=perm_key) else: kwargs = dict(permitted_object=obj) kwargs.update(opt_kwargs) LOGGER.debug( "Removing permission %r from user role %r", kwargs, urole_name) if not check_mode: urole.remove_permission(**kwargs) del cur_perms[perm_key] for perm_key in add_perms: opt_kwargs, obj = add_perms[perm_key] if obj is None: # resource class kwargs = dict(permitted_object=perm_key) else: kwargs = dict(permitted_object=obj) kwargs.update(opt_kwargs) LOGGER.debug( "Adding permission %r to user role %r", kwargs, urole_name) if not check_mode: urole.add_permission(**kwargs) cur_perms[perm_key] = add_perms[perm_key] if not urole: raise AssertionError() # Process artificial properties result['permissions'] = result_permissions(cur_perms) sys_urole_uri = result['associated-system-defined-user-role-uri'] if sys_urole_uri: sys_urole_name = urole_uri_to_name(console, sys_urole_uri) else: sys_urole_name = None result['associated-system-defined-user-role-name'] = sys_urole_name return changed, result finally: session.logoff()
def perform_list(params): """ List the partitions and return a subset of properties. Raises: ParameterError: An issue with the module parameters. zhmcclient.Error: Any zhmcclient exception can happen. """ host = params['hmc_host'] userid, password, ca_certs, verify = get_hmc_auth(params['hmc_auth']) cpc_name = params.get('cpc_name', None) _faked_session = params.get('_faked_session', None) # No default specified session = get_session(_faked_session, host, userid, password, ca_certs, verify) try: client = zhmcclient.Client(session) # The "List Permitted Partitions" operation was added in HMC # version 2.14.0. The operation depends only on the HMC version and not # on the SE/CPC version, so it is supported e.g. for a 2.14 HMC managing # a z13 CPC. hmc_version = client.query_api_version()['hmc-version'] hmc_version_info = [int(x) for x in hmc_version.split('.')] if hmc_version_info < [2, 14, 0]: # List the partitions in the traditional way if cpc_name: LOGGER.debug("Listing partitions of CPC %s", cpc_name) cpc = client.cpcs.find(name=cpc_name) partitions = cpc.partitions.list() else: LOGGER.debug("Listing partitions of all managed CPCs") cpcs = client.cpcs.list() partitions = [] for cpc in cpcs: partitions.extend(cpc.partitions.list()) else: # List the partitions using the new operation if cpc_name: LOGGER.debug("Listing permitted partitions of CPC %s", cpc_name) filter_args = {'cpc-name': cpc_name} else: LOGGER.debug( "Listing permitted partitions of all managed CPCs") filter_args = None partitions = client.consoles.console.list_permitted_partitions( filter_args=filter_args) # The default exception handling is sufficient for the above. se_versions = {} partition_list = [] for partition in partitions: # se-version has been added to the result of List Permitted # Partitions in HMC/SE 2.14.1. Before that, it triggers the # retrieval of CPC properties. parent_cpc = partition.manager.cpc try: se_version = se_versions[parent_cpc.name] except KeyError: try: se_version = partition.properties['se-version'] except KeyError: se_version = parent_cpc.get_property('se-version') se_versions[parent_cpc.name] = se_version partition_properties = { "name": partition.name, "cpc_name": parent_cpc.name, "se_version": se_version, "status": partition.get_property('status'), "has_unacceptable_status": partition.get_property('has-unacceptable-status'), } partition_list.append(partition_properties) return partition_list finally: session.logoff()
def ensure_present(params, check_mode): """ Ensure that the storage group exists and has the specified properties. Storage volumes are not subject of this function, they are handled by the zhmc_storage_volume.py module. Raises: ParameterError: An issue with the module parameters. zhmcclient.Error: Any zhmcclient exception can happen. """ host = params['hmc_host'] userid, password, ca_certs, verify = get_hmc_auth(params['hmc_auth']) cpc_name = params['cpc_name'] storage_group_name = params['name'] expand = params['expand'] _faked_session = params.get('_faked_session', None) changed = False result = {} session = get_session(_faked_session, host, userid, password, ca_certs, verify) try: client = zhmcclient.Client(session) console = client.consoles.console cpc = client.cpcs.find(name=cpc_name) # The default exception handling is sufficient for the above. try: storage_group = console.storage_groups.find( name=storage_group_name) except zhmcclient.NotFound: storage_group = None if storage_group is None: # It does not exist. Create it and update it if there are # update-only properties. if not check_mode: create_props, update_props = \ process_properties(cpc, storage_group, params) storage_group = console.storage_groups.create(create_props) update2_props = {} for name, value in update_props.items(): if name not in create_props: update2_props[name] = value if update2_props: storage_group.update_properties(update2_props) # We refresh the properties after the update, in case an # input property value gets changed. storage_group.pull_full_properties() else: # TODO: Show props in module result also in check mode. pass changed = True else: # It exists. Update its properties. storage_group.pull_full_properties() create_props, update_props = \ process_properties(cpc, storage_group, params) if create_props: raise AssertionError("Unexpected " "create_props: %r" % create_props) if update_props: if not check_mode: storage_group.update_properties(update_props) # We refresh the properties after the update, in case an # input property value gets changed. storage_group.pull_full_properties() else: # TODO: Show updated props in mod.result also in chk.mode pass changed = True if not check_mode: if not storage_group: raise AssertionError() result = dict(storage_group.properties) add_artificial_properties(result, storage_group, expand) return changed, result finally: session.logoff()
def ensure_detached(params, check_mode): """ Ensure that the target partition has no adapters and no domains attached. Raises: ParameterError: An issue with the module parameters. Error: Other errors during processing. zhmcclient.Error: Any zhmcclient exception can happen. """ # Note: Defaults specified in argument_spec will be set in params dict host = params['hmc_host'] userid, password = get_hmc_auth(params['hmc_auth']) cpc_name = params['cpc_name'] partition_name = params['partition_name'] faked_session = params.get('faked_session', None) # No default specified changed = False result = dict() result_changes = dict() try: session = get_session(faked_session, host, userid, password) client = zhmcclient.Client(session) cpc = client.cpcs.find(name=cpc_name) partition = cpc.partitions.find(name=partition_name) # The default exception handling is sufficient for the above. # Determine all crypto adapters of any crypto type filter_args = { 'adapter-family': 'crypto', } all_adapters = cpc.adapters.list(filter_args=filter_args, full_properties=True) cc = partition.get_property('crypto-configuration') # The 'crypto-configuration' property is None or: # { # 'crypto-adapter-uris': ['/api/...', ...], # 'crypto-domain-configurations': [ # {'domain-index': 15, 'access-mode': 'control-usage'}, # ... # ] # } if cc: attached_adapter_uris = cc['crypto-adapter-uris'] remove_adapters = [] remove_adapter_names = [] for a in all_adapters: if a.uri in attached_adapter_uris: remove_adapters.append(a) remove_adapter_names.append(a.name) remove_domains = [] for dc in cc['crypto-domain-configurations']: di = dc['domain-index'] remove_domains.append(di) LOGGER.debug( "Detaching adapters {!r} and domains {!r} from target " "partition {!r}".format(remove_adapter_names, remove_domains, partition.name)) if not check_mode: try: partition.decrease_crypto_config(remove_adapters, remove_domains) except zhmcclient.Error as exc: raise Error( "Detaching adapters {!r} and domains {!r} from " "target partition {!r} failed: {}".format( remove_adapter_names, remove_domains, partition.name, exc)) changed = True result_changes['removed-adapters'] = remove_adapter_names result_changes['removed-domains'] = remove_domains if not check_mode: # This is not optimal because it does not produce a result # in check mode, but because the actual config is determined, # instead of the artificially calculated one, it seems better # to return no config than the unchanged actual config. result.update(get_partition_config(partition, all_adapters)) return changed, result, result_changes finally: session.logoff()
def test_zhmc_password_rule_absent_present(ansible_mod_cls, desc, initial_pwrule_props, input_state, input_props, exp_pwrule_props, exp_changed, check_mode, hmc_session): # noqa: F811, E501 """ Test the zhmc_password_rule module with state=absent/present. """ hd = hmc_session.hmc_definition hmc_host = hd.host hmc_auth = dict(userid=hd.userid, password=hd.password, ca_certs=hd.ca_certs, verify=hd.verify) faked_session = hmc_session if hd.mock_file else None client = zhmcclient.Client(hmc_session) console = client.consoles.console # Create a password rule name that does not exist pwrule_name = new_pwrule_name() where = "password rule '{u}'".format(u=pwrule_name) # Create initial password rule, if specified so if initial_pwrule_props is not None: pwrule_props = STD_PWRULE_PROPERTIES.copy() pwrule_props.update(initial_pwrule_props) pwrule_props['name'] = pwrule_name try: console.password_rules.create(pwrule_props) except zhmcclient.HTTPError as exc: if exc.http_status == 403 and exc.reason == 1: # User is not permitted to create password rules pytest.skip("HMC user '{u}' is not permitted to create " "initial test password rule".format(u=hd.userid)) else: pwrule_props = None try: # Prepare module input parameters (must be all required + optional) params = { 'hmc_host': hmc_host, 'hmc_auth': hmc_auth, 'name': pwrule_name, 'state': input_state, 'log_file': LOG_FILE, '_faked_session': faked_session, } if input_props is not None: params['properties'] = input_props else: params['properties'] = {} mod_obj = mock_ansible_module(ansible_mod_cls, params, check_mode) # Exercise the code to be tested with pytest.raises(SystemExit) as exc_info: zhmc_password_rule.main() exit_code = exc_info.value.args[0] assert exit_code == 0, \ "{w}: Module failed with exit code {e} and message:\n{m}". \ format(w=where, e=exit_code, m=get_failure_msg(mod_obj)) changed, output_props = get_module_output(mod_obj) if changed != exp_changed: pwrule_props_sorted = \ OrderedDict(sorted(pwrule_props.items(), key=lambda x: x[0])) \ if pwrule_props is not None else None input_props_sorted = \ OrderedDict(sorted(input_props.items(), key=lambda x: x[0])) \ if input_props is not None else None output_props_sorted = \ OrderedDict(sorted(output_props.items(), key=lambda x: x[0])) \ if output_props is not None else None raise AssertionError( "Unexpected change flag returned: actual: {0}, expected: {1}\n" "Initial password rule properties:\n{2}\n" "Module input properties:\n{3}\n" "Resulting password rule properties:\n{4}".format( changed, exp_changed, pformat(pwrule_props_sorted.items(), indent=2), pformat(input_props_sorted.items(), indent=2), pformat(output_props_sorted.items(), indent=2))) if input_state == 'present': assert_pwrule_props(output_props, where) finally: # Delete password rule, if it exists try: # We invalidate the name cache of our client, because the password rule # was possibly deleted by the Ansible module and not through our # client instance. console.password_rules.invalidate_cache() pwrule = console.password_rules.find_by_name(pwrule_name) except zhmcclient.NotFound: pwrule = None if pwrule: pwrule.delete()
def ensure_stopped(params, check_mode): """ Ensure that the partition exists, is stopped, and has the specified properties. Raises: ParameterError: An issue with the module parameters. StatusError: An issue with the partition status. zhmcclient.Error: Any zhmcclient exception can happen. """ host = params['hmc_host'] userid, password = get_hmc_auth(params['hmc_auth']) cpc_name = params['cpc_name'] partition_name = params['name'] faked_session = params.get('faked_session', None) changed = False result = {} try: session = get_session(faked_session, host, userid, password) client = zhmcclient.Client(session) cpc = client.cpcs.find(name=cpc_name) # The default exception handling is sufficient for the above. try: partition = cpc.partitions.find(name=partition_name) partition.pull_full_properties() except zhmcclient.NotFound: partition = None if not partition: # It does not exist. Create it and update it if there are # update-only properties. if not check_mode: create_props, update_props, stop, crypto_changes = \ process_properties(cpc, partition, params) partition = cpc.partitions.create(create_props) update2_props = {} for name in update_props: if name not in create_props: update2_props[name] = update_props[name] if update2_props: partition.update_properties(update2_props) if crypto_changes: change_crypto_config(partition, crypto_changes, check_mode) changed = True else: # It exists. Stop it and update its properties. create_props, update_props, stop, crypto_changes = \ process_properties(cpc, partition, params) changed |= stop_partition(partition, check_mode) if update_props: if not check_mode: partition.update_properties(update_props) changed = True if crypto_changes: changed |= change_crypto_config(partition, crypto_changes, check_mode) if partition and not check_mode: partition.pull_full_properties() status = partition.get_property('status') if status not in ('stopped'): raise StatusError( "Could not get partition {!r} into a stopped state, " "status is: {!r}".format(partition.name, status)) if partition: result = partition.properties return changed, result finally: session.logoff()
def ensure_present(params, check_mode): """ Ensure that the user exists and has the specified properties. Raises: ParameterError: An issue with the module parameters. zhmcclient.Error: Any zhmcclient exception can happen. """ host = params['hmc_host'] userid, password, ca_certs, verify = get_hmc_auth(params['hmc_auth']) user_name = params['name'] expand = params['expand'] _faked_session = params.get('_faked_session', None) changed = False result = {} session = get_session( _faked_session, host, userid, password, ca_certs, verify) try: client = zhmcclient.Client(session) console = client.consoles.console # The default exception handling is sufficient for the above. try: user = console.users.find(name=user_name) except zhmcclient.NotFound: user = None if user is None: # It does not exist. Create it and update it if there are # update-only properties. create_props, update_props, add_roles, rem_roles = \ process_properties(console, user, params) update2_props = {} for name, value in update_props.items(): if name not in create_props: update2_props[name] = value if not check_mode: user = console.users.create(create_props) if update2_props: user.update_properties(update2_props) # We refresh the properties after the update, in case an # input property value gets changed. user.pull_full_properties() else: # Create a User object locally user = create_check_mode_user( console, create_props, update2_props) result = dict(user.properties) changed = True for role in add_roles: LOGGER.debug( "Adding role %r to user %r", role.name, user_name) if not check_mode: user.add_user_role(role) if 'user-roles' not in result: result['user-roles'] = [] result['user-roles'].append(role.uri) if rem_roles: raise AssertionError( "Unexpected attempt to remove user roles {0!r} from newly " "created user {1!r}".format(rem_roles, user.name)) else: # It exists. Update its properties. user.pull_full_properties() result = dict(user.properties) create_props, update_props, add_roles, rem_roles = \ process_properties(console, user, params) if create_props: raise AssertionError("Unexpected " "create_props: %r" % create_props) if update_props: LOGGER.debug( "Existing user %r needs to get properties updated: %r", user_name, update_props) if not check_mode: user.update_properties(update_props) # We refresh the properties after the update, in case an # input property value gets changed. user.pull_full_properties() result = dict(user.properties) else: # Update the local User object's properties result.update(update_props) changed = True for role in add_roles: LOGGER.debug( "Adding role %r to user %r", role.name, user_name) if not check_mode: user.add_user_role(role) if 'user-roles' not in result: result['user-roles'] = [] result['user-roles'].append(role.uri) changed = True for role in rem_roles: LOGGER.debug( "Removing role %r from user %r", role.name, user_name) if not check_mode: user.remove_user_role(role) if 'user-roles' not in result: raise AssertionError( "User {0!r} unexpectedly does not have a " "'user-roles' property".format(user.name)) result['user-roles'].remove(role.uri) changed = True if not user: raise AssertionError() add_artificial_properties(result, console, user, expand, check_mode) return changed, result finally: session.logoff()
def setup_cpc(capsys, hmc_creds, fake_data, rt_config=None): """ Set up and return some objects for the CPC that is to be used for testing. This function uses the get_test_cpc() function to determine the CPC to be used for testing. If no CPC has been defined, this function sets up a faked CPC using the zhmcclient mock support. Parameters: capsys: The pytest 'capsys' fixture the testcase function must specify as an argument and pass to this functionhere. hmc_creds (HmcCredentials): HMC credentials with CPC data. fake_data (dict): Input data in case a mock environment needs to be set up. The dict has the following keys: * hmc_host (string): Hostname or IP address of the faked HMC. * hmc_name (string): HMC name of the faked HMC. * hmc_version (string): HMC version of the faked HMC. * api_version (string): API version of the faked HMC. * cpc_properties (dict): Properties for the faked CPC. 'name' must be set. rt_config (zhmcclient.RetryTimeoutConfig): Retry / timeout config to override the default values. The default values used by this function are the global defaults defined for the zhmcclient package, whereby connection retries, connection timeout, operation timeout, and status timeout have been shortened. The resulting default values should be good for most function testcases. Returns: tuple: Tuple with the objects thathave been set up: * cpc_name (string): Name of the CPC to be used (some fake name or the real name that has been set up). * session (zhmcclient.Session or zhmcclient_mock-FakedSession): zhmcclient session object (faked or real) to be used for accessing the HMC managing that CPC. * client (zhmcclient.Client): zhmcclient Client object to be used for accessing the HMC managing that CPC. * cpc (zhmcclient.Cpc): Cpc resource object representing the CPC to be used (faked or real). * faked_cpc (zhmcclient_mock.FakedCpc): FakedCpc object in case a mock environment was set up (so the caller can add resources to it), or otherwise `None`. """ cpc_name = get_test_cpc() if cpc_name is None: # No test CPC defined in the environment -> use mock support and # add a faked CPC. cpc_properties = fake_data['cpc_properties'] cpc_name = cpc_properties['name'] info(capsys, "Testing with faked CPC %r (ZHMC_TEST_CPC env.var. " "not set)", cpc_name) session = zhmcclient_mock.FakedSession(fake_data['hmc_host'], fake_data['hmc_name'], fake_data['hmc_version'], fake_data['api_version']) faked_cpc = session.hmc.cpcs.add(cpc_properties) else: # A test CPC is defined in the environment -> use it! info(capsys, "Testing with CPC %r", cpc_name) eff_rt_config = DEFAULT_RT_CONFIG if rt_config: eff_rt_config.override_with(rt_config) cpc_item = hmc_creds.get_cpc_item(cpc_name) assert cpc_item, "HMC credentials file not found: {!r}".\ format(hmc_creds.filepath) session = zhmcclient.Session(cpc_item['hmc_host'], cpc_item['hmc_userid'], cpc_item['hmc_password'], retry_timeout_config=eff_rt_config) faked_cpc = None client = zhmcclient.Client(session) cpc = client.cpcs.find(name=cpc_name) return cpc_name, session, client, cpc, faked_cpc
def ensure_present(params, check_mode): """ Ensure that the user exists and has the specified properties. Storage volumes are not subject of this function, they are handled by the zhmc_storage_volume.py module. Raises: ParameterError: An issue with the module parameters. zhmcclient.Error: Any zhmcclient exception can happen. """ host = params['hmc_host'] userid, password = get_hmc_auth(params['hmc_auth']) user_name = params['name'] expand = params['expand'] faked_session = params.get('faked_session', None) changed = False result = {} try: session = get_session(faked_session, host, userid, password) client = zhmcclient.Client(session) console = client.consoles.console # The default exception handling is sufficient for the above. try: user = console.users.find(name=user_name) except zhmcclient.NotFound: user = None if user is None: # It does not exist. Create it and update it if there are # update-only properties. create_props, update_props = process_properties( console, user, params) update2_props = {} for name in update_props: if name not in create_props: update2_props[name] = update_props[name] if not check_mode: user = console.users.create(create_props) if update2_props: user.update_properties(update2_props) # We refresh the properties after the update, in case an # input property value gets changed. user.pull_full_properties() else: # Create a User object locally user = create_check_mode_user(console, create_props, update2_props) changed = True else: # It exists. Update its properties. user.pull_full_properties() create_props, update_props = process_properties( console, user, params) assert not create_props, \ "Unexpected create_props: %r" % create_props if update_props: LOGGER.debug( "Existing user {!r} needs to get properties updated: {!r}". format(user_name, update_props)) if not check_mode: user.update_properties(update_props) # We refresh the properties after the update, in case an # input property value gets changed. user.pull_full_properties() else: # Update the local User object's properties user.properties.update(update_props) changed = True assert user add_artificial_properties(console, user, expand, check_mode) result = user.properties return changed, result finally: session.logoff()
def ensure_present(params, check_mode): """ Ensure that the storage volume is defined and has the specified properties. Raises: ParameterError: An issue with the module parameters. zhmcclient.Error: Any zhmcclient exception can happen. """ host = params['hmc_host'] userid, password, ca_certs, verify = get_hmc_auth(params['hmc_auth']) cpc_name = params['cpc_name'] storage_group_name = params['storage_group_name'] storage_volume_name = params['name'] _faked_session = params.get('_faked_session', None) changed = False result = {} session = get_session(_faked_session, host, userid, password, ca_certs, verify) try: client = zhmcclient.Client(session) console = client.consoles.console cpc = client.cpcs.find(name=cpc_name) storage_group = console.storage_groups.find(name=storage_group_name) # The default exception handling is sufficient for the above. sg_cpc = storage_group.cpc if sg_cpc.uri != cpc.uri: raise ParameterError( "Storage group {0!r} is not associated with the specified " "CPC {1!r}, but with CPC {2!r}.".format( storage_group_name, cpc.name, sg_cpc.name)) try: storage_volume = storage_group.storage_volumes.find( name=storage_volume_name) except zhmcclient.NotFound: storage_volume = None except zhmcclient.NoUniqueMatch: # The name of storage volumes within their storage group is not # enforced to be unique. raise if storage_volume is None: # It does not exist. Create it and update it if there are # update-only properties. if not check_mode: create_props, update_props = \ process_properties(cpc, storage_group, storage_volume, params) storage_volume = storage_group.storage_volumes.create( create_props) update2_props = {} for name, value in update_props.items(): if name not in create_props: update2_props[name] = value if update2_props: storage_volume.update_properties(update2_props) # We refresh the properties after the update, in case an # input property value gets changed. storage_volume.pull_full_properties() else: # TODO: Show props in module result also in check mode. pass changed = True else: # It exists. Update its properties. storage_volume.pull_full_properties() create_props, update_props = \ process_properties(cpc, storage_group, storage_volume, params) if create_props: raise AssertionError("Unexpected " "create_props: %r" % create_props) if update_props: if not check_mode: storage_volume.update_properties(update_props) # We refresh the properties after the update, in case an # input property value gets changed. storage_volume.pull_full_properties() else: # TODO: Show updated props in mod.result also in chk.mode storage_volume.pull_full_properties() changed = True if not check_mode: if not storage_volume: raise AssertionError() if storage_volume: result = dict(storage_volume.properties) add_artificial_properties(result, storage_volume) return changed, result finally: session.logoff()
def cmd_lpar_list(cmd_ctx, cpc_name, options): # pylint: disable=missing-function-docstring client = zhmcclient.Client(cmd_ctx.session) if client.version_info() >= (2, 20): # Starting with HMC 2.14.0 # This approach is faster than going through the CPC. # In addition, starting with HMC API version 3.6 (an update to # HMC 2.15.0), this approach supports users that do not have object # access permission to the CPC. filter_args = {} if cpc_name: filter_args['cpc-name'] = cpc_name lpars = client.consoles.console.list_permitted_lpars( filter_args=filter_args) else: filter_args = {} if cpc_name: filter_args['name'] = cpc_name try: cpcs = client.cpcs.list(filter_args=filter_args) except zhmcclient.Error as exc: raise click_exception(exc, cmd_ctx.error_format) lpars = [] for cpc in cpcs: try: lpars.extend(cpc.lpars.list()) except zhmcclient.Error as exc: raise click_exception(exc, cmd_ctx.error_format) if options['type']: click.echo("The --type option is deprecated and type information " "is now always shown.") # Prepare the additions dict of dicts. It contains additional # (=non-resource) property values by property name and by resource URI. # Depending on options, some of them will not be populated. additions = {} additions['cpc'] = {} show_list = [ 'name', 'cpc', ] if not options['names_only']: show_list.extend([ 'status', 'activation-mode', 'os-type', 'workload-manager-enabled', ]) if options['uri']: show_list.extend([ 'object-uri', ]) for lpar in lpars: cpc = lpar.manager.parent additions['cpc'][lpar.uri] = cpc.name try: print_resources(cmd_ctx, lpars, cmd_ctx.output_format, show_list, additions, all=options['all']) except zhmcclient.Error as exc: raise click_exception(exc, cmd_ctx.error_format)
def ensure_present(params, check_mode): """ Ensure that the specified Hipersockets adapter exists and has the specified properties set. Raises: ParameterError: An issue with the module parameters. Error: Other errors during processing. zhmcclient.Error: Any zhmcclient exception can happen. """ # Note: Defaults specified in argument_spec will be set in params dict host = params['hmc_host'] userid, password = get_hmc_auth(params['hmc_auth']) cpc_name = params['cpc_name'] adapter_name = params['name'] faked_session = params.get('faked_session', None) # No default specified changed = False try: session = get_session(faked_session, host, userid, password) client = zhmcclient.Client(session) cpc = client.cpcs.find(name=cpc_name) # The default exception handling is sufficient for the above. try: adapter = cpc.adapters.find(name=adapter_name) except zhmcclient.NotFound: adapter = None if not adapter: # It does not exist. The only possible adapter type # that can be created is a Hipersockets adapter, but before # creating one we check the 'type' input property to verify that # the intention is really Hipersockets creation, and not just a # mispelled name. input_props = params.get('properties', None) if input_props is None: adapter_type = None else: adapter_type = input_props.get('type', None) if adapter_type is None: raise ParameterError( "Input property 'type' missing when creating " "Hipersockets adapter {!r} (must specify 'hipersockets')". format(adapter_name)) if adapter_type != 'hipersockets': raise ParameterError( "Input property 'type' specifies {!r} when creating " "Hipersockets adapter {!r} (must specify 'hipersockets').". format(adapter_type, adapter_name)) create_props, update_props = process_properties(adapter, params) # This is specific to Hipersockets: There are no update-only # properties, so any remaining such property is an input error invalid_update_props = {} for name in update_props: if name not in create_props: invalid_update_props[name] = update_props[name] if invalid_update_props: raise ParameterError( "Invalid input properties specified when creating " "Hipersockets adapter {!r}: {!r}".format( adapter_name, invalid_update_props)) # While the 'type' input property is required for verifying # the intention, it is not allowed as input for the # Create Hipersocket HMC operation. del create_props['type'] if not check_mode: adapter = cpc.adapters.create_hipersocket(create_props) adapter.pull_full_properties() result = adapter.properties # from actual values else: adapter = None result = dict() result.update(create_props) # from input values changed = True else: # It does exist. # Update its properties and change adapter and crypto type, if # needed. adapter.pull_full_properties() result = adapter.properties create_props, update_props, chg_adapter_type, chg_crypto_type = \ process_properties(adapter, params) if update_props: if not check_mode: adapter.update_properties(update_props) else: result.update(update_props) # from input values changed = True if chg_adapter_type: if not check_mode: adapter.change_adapter_type(chg_adapter_type) else: result['type'] = chg_adapter_type changed = True if chg_crypto_type: if not check_mode: adapter.change_crypto_type(chg_crypto_type) else: result['crypto-type'] = chg_crypto_type changed = True if changed and not check_mode: adapter.pull_full_properties() result = adapter.properties # from actual values if adapter: ports = adapter.ports.list() result_ports = list() for port in ports: port.pull_full_properties() result_ports.append(port.properties) result['ports'] = result_ports else: # For now, we return no ports when creating in check mode result['ports'] = dict() return changed, result finally: session.logoff()
def ensure_set(params, check_mode): """ Identify the target adapter (that must exist) and ensure that the specified properties are set on the adapter. Raises: ParameterError: An issue with the module parameters. Error: Other errors during processing. zhmcclient.Error: Any zhmcclient exception can happen. """ # Note: Defaults specified in argument_spec will be set in params dict host = params['hmc_host'] userid, password = get_hmc_auth(params['hmc_auth']) cpc_name = params['cpc_name'] adapter_name = params['name'] adapter_match = params['match'] faked_session = params.get('faked_session', None) # No default specified changed = False try: session = get_session(faked_session, host, userid, password) client = zhmcclient.Client(session) cpc = client.cpcs.find(name=cpc_name) adapter = identify_adapter(cpc, adapter_name, adapter_match) # The default exception handling is sufficient for the above. adapter.pull_full_properties() result = adapter.properties # It was identified by name or match properties, so it does exist. # Update its properties and change adapter and crypto type, if # needed. create_props, update_props, chg_adapter_type, chg_crypto_type = \ process_properties(adapter, params) if update_props: if not check_mode: adapter.update_properties(update_props) else: result.update(update_props) # from input values changed = True if chg_adapter_type: if not check_mode: adapter.change_adapter_type(chg_adapter_type) else: result['type'] = chg_adapter_type changed = True if chg_crypto_type: if not check_mode: adapter.change_crypto_type(chg_crypto_type) else: result['crypto-type'] = chg_crypto_type changed = True if changed and not check_mode: adapter.pull_full_properties() result = adapter.properties # from actual values ports = adapter.ports.list() result_ports = list() for port in ports: # TODO: Disabling the following line mitigates the recent issue # with HTTP error 404,4 when retrieving port properties. # port.pull_full_properties() result_ports.append(port.properties) result['ports'] = result_ports return changed, result finally: session.logoff()
def cmd_character_rule_add(cmd_ctx, password_rule_name, options): # pylint: disable=missing-function-docstring client = zhmcclient.Client(cmd_ctx.session) console = client.consoles.console password_rule = find_password_rule(cmd_ctx, console, password_rule_name) character_rules = password_rule.get_property('character-rules') size = len(character_rules) before = options.pop('before') after = options.pop('after') last = options.pop('last') if (bool(before is not None) + bool(after is not None) + last) > 1: raise click.ClickException( "Only one of --before, --after and --last can be specified.") name_map = { 'min': 'min-characters', 'max': 'max-characters', 'custom': None, } org_options = original_options(options) cr_properties = options_to_properties(org_options, name_map) # Add custom-character-sets array custom_char_sets = [] for chars, control in options['custom']: custom_char_set = { 'character-set': chars, 'inclusion': control, } custom_char_sets.append(custom_char_set) cr_properties['custom-character-sets'] = custom_char_sets new_character_rules = list(character_rules) if before is not None: if before < 0 or before >= max(size, 1): raise click.ClickException( "Invalid --before index {i} for character rules of password " "rule '{r}'".format(i=before, r=password_rule_name)) new_character_rules.insert(before, cr_properties) elif after is not None: if after < 0 or after >= max(size, 1): raise click.ClickException( "Invalid --after index {i} for character rules of password " "rule '{r}'".format(i=after, r=password_rule_name)) new_character_rules.insert(after + 1, cr_properties) else: # last = default new_character_rules.insert(size, cr_properties) properties = {'character-rules': new_character_rules} try: password_rule.update_properties(properties) except zhmcclient.Error as exc: raise click_exception(exc, cmd_ctx.error_format) cmd_ctx.spinner.stop() where = 'after' if after is not None else 'before' index = after if after is not None else before click.echo("Password rule '{r}' has been updated to add character rule " "{w} index {i}.".format(r=password_rule_name, w=where, i=index))
def cmd_nic_update(cmd_ctx, cpc_name, partition_name, nic_name, options): client = zhmcclient.Client(cmd_ctx.session) nic = find_nic(cmd_ctx, client, cpc_name, partition_name, nic_name) name_map = { # The following options are handled in this function: 'adapter': None, 'port': None, 'virtual-switch': None, } options = original_options(options) properties = options_to_properties(options, name_map) required_roce_option_names = ( 'adapter', 'port') if any([options[name] for name in required_roce_option_names]): missing_option_names = [name for name in required_roce_option_names if options[name] is None] if missing_option_names: raise_click_exception("ROCE adapter specified, but misses the " "following options: %s" % ', '.join(missing_option_names), cmd_ctx.error_format) adapter_name = options['adapter'] try: adapter = nic.manager.partition.manager.cpc.adapters.find( name=adapter_name) except zhmcclient.NotFound: raise_click_exception("Could not find adapter %s in CPC %s." % (adapter_name, cpc_name), cmd_ctx.error_format) port_name = options['port'] try: port = adapter.ports.find(name=port_name) except zhmcclient.NotFound: raise_click_exception("Could not find port %s on adapter %s in " "CPC %s." % (port_name, adapter_name, cpc_name), cmd_ctx.error_format) properties['network-adapter-port-uri'] = port.uri elif options['virtual-switch'] is not None: vswitch_name = options['virtual-switch'] try: vswitch = nic.manager.partition.manager.cpc.virtual_switches.find( name=vswitch_name) except zhmcclient.NotFound: raise_click_exception("Could not find virtual switch %s in " "CPC %s." % (vswitch_name, cpc_name), cmd_ctx.error_format) properties['virtual-switch-uri'] = vswitch.uri else: # The backing adapter port or virtual switch is not being updated. pass if not properties: cmd_ctx.spinner.stop() click.echo("No properties specified for updating NIC %s." % nic_name) return try: nic.update_properties(properties) except zhmcclient.Error as exc: raise_click_exception(exc, cmd_ctx.error_format) cmd_ctx.spinner.stop() if 'name' in properties and properties['name'] != nic_name: click.echo("NIC %s has been renamed to %s and was updated." % (nic_name, properties['name'])) else: click.echo("NIC %s has been updated." % nic_name)
def create_metrics_context(session, yaml_metric_groups, hmc_version): """ Creating a context is mandatory for reading metrics from the Z HMC. Takes the session, the metric_groups dictionary from the metrics YAML file for fetch/do not fetch information, and the name of the YAML file for error output. Returns a tuple(context, resources), where context is the metric context and resources is a dict(key: metric group name, value: list of auto-enabled resource objects for the metric group). Raises: zhmccclient exceptions """ fetched_hmc_metric_groups = [] fetched_res_metric_groups = [] for metric_group in yaml_metric_groups: mg_dict = yaml_metric_groups[metric_group] mg_type = mg_dict.get("type", 'hmc') # fetch is required in the metrics schema: fetch = mg_dict["fetch"] # if is optional in the metrics schema: if fetch and "if" in mg_dict: fetch = eval_condition(mg_dict["if"], hmc_version) if fetch: if mg_type == 'hmc': fetched_hmc_metric_groups.append(metric_group) else: assert mg_type == 'resource' # ensured by enum fetched_res_metric_groups.append(metric_group) client = zhmcclient.Client(session) verbose("Creating a metrics context on the HMC for HMC metric groups:") for metric_group in fetched_hmc_metric_groups: verbose(" {}".format(metric_group)) context = client.metrics_contexts.create({ "anticipated-frequency-seconds": 15, "metric-groups": fetched_hmc_metric_groups }) verbose("Retrieving resources from the HMC for resource metric groups:") resources = {} for metric_group in fetched_res_metric_groups: verbose(" {}".format(metric_group)) try: resource_path = yaml_metric_groups[metric_group]['resource'] except KeyError: new_exc = ImproperExit( "Missing 'resource' item in resource metric group {} in " "metrics file".format(metric_group)) new_exc.__cause__ = None # pylint: disable=invalid-name raise new_exc if resource_path == 'cpc': resources[metric_group] = [] cpcs = client.cpcs.list() for cpc in cpcs: verbose("Enabling auto-update for CPC {}".format(cpc.name)) cpc.enable_auto_update() resources[metric_group].append(cpc) elif resource_path == 'cpc.partition': resources[metric_group] = [] cpcs = client.cpcs.list() for cpc in cpcs: partitions = cpc.partitions.list() for partition in partitions: verbose("Enabling auto-update for partition {}.{}".format( cpc.name, partition.name)) partition.enable_auto_update() resources[metric_group].append(partition) elif resource_path == 'cpc.logical-partition': resources[metric_group] = [] cpcs = client.cpcs.list() for cpc in cpcs: lpars = cpc.lpars.list() for lpar in lpars: verbose("Enabling auto-update for LPAR {}.{}".format( cpc.name, lpar.name)) lpar.enable_auto_update() resources[metric_group].append(lpar) else: new_exc = ImproperExit( "Invalid 'resource' item in resource metric group {} in " "metrics file: {}".format(metric_group, resource_path)) new_exc.__cause__ = None # pylint: disable=invalid-name raise new_exc return context, resources
def test_zhmc_partition_list(ansible_mod_cls, check_mode, with_cpc, dpm_mode_cpcs): # noqa: F811, E501 """ Test the zhmc_partition_list module with DPM mode CPCs. """ if not dpm_mode_cpcs: pytest.skip("HMC definition does not include any CPCs in DPM mode") for cpc in dpm_mode_cpcs: assert cpc.dpm_enabled session = cpc.manager.session hd = session.hmc_definition hmc_host = hd.host hmc_auth = dict(userid=hd.userid, password=hd.password, ca_certs=hd.ca_certs, verify=hd.verify) client = zhmcclient.Client(session) console = client.consoles.console faked_session = session if hd.mock_file else None # Determine the expected partitions on the HMC if DEBUG: print("Debug: Listing expected partitions") hmc_version = client.query_api_version()['hmc-version'] hmc_version_info = [int(x) for x in hmc_version.split('.')] if hmc_version_info < [2, 14, 0]: # List the LPARs in the traditional way if with_cpc: exp_partitions = cpc.partitions.list() else: cpcs_ = client.cpcs.list() exp_partitions = [] for cpc_ in cpcs_: exp_partitions.extend(cpc_.partitions.list()) else: # List the LPARs using the new operation if with_cpc: filter_args = {'cpc-name': cpc.name} else: filter_args = None exp_partitions = console.list_permitted_partitions( filter_args=filter_args) exp_partition_dict = {} se_versions = {} for partition in exp_partitions: if DEBUG: print("Debug: Getting expected properties of partition {p!r} " "on CPC {c!r}".format(p=partition.name, c=cpc.name)) partition.pull_full_properties() cpc = partition.manager.parent try: se_version = se_versions[cpc.name] except KeyError: if DEBUG: print("Debug: Getting expected se-version of CPC {c!r}". format(c=cpc.name)) se_version = cpc.get_property('se-version') se_versions[cpc.name] = se_version exp_properties = {} exp_properties.update(partition.properties) exp_properties['cpc-name'] = cpc.name exp_properties['se-version'] = se_version exp_cpc_part_name = (cpc.name, partition.name) exp_partition_dict[exp_cpc_part_name] = exp_properties # Prepare module input parameters (must be all required + optional) params = { 'hmc_host': hmc_host, 'hmc_auth': hmc_auth, 'log_file': LOG_FILE, '_faked_session': faked_session, } if with_cpc: params['cpc_name'] = cpc.name # Prepare mocks for AnsibleModule object mod_obj = mock_ansible_module(ansible_mod_cls, params, check_mode) # Exercise the code to be tested with pytest.raises(SystemExit) as exc_info: zhmc_partition_list.main() exit_code = exc_info.value.args[0] # Assert module exit code assert exit_code == 0, \ "Module failed with exit code {e} and message:\n{m}". \ format(e=exit_code, m=get_failure_msg(mod_obj)) # Assert module output changed, partition_list = get_module_output(mod_obj) assert changed is False assert_partition_list(partition_list, exp_partition_dict)
def ensure_active(params, check_mode): """ Ensure that the partition exists, is active or degraded, and has the specified properties. Raises: ParameterError: An issue with the module parameters. StatusError: An issue with the partition status. zhmcclient.Error: Any zhmcclient exception can happen. """ host = params['hmc_host'] userid, password = get_hmc_auth(params['hmc_auth']) cpc_name = params['cpc_name'] partition_name = params['name'] faked_session = params.get('faked_session', None) changed = False result = {} try: session = get_session(faked_session, host, userid, password) client = zhmcclient.Client(session) cpc = client.cpcs.find(name=cpc_name) # The default exception handling is sufficient for the above. try: partition = cpc.partitions.find(name=partition_name) partition.pull_full_properties() except zhmcclient.NotFound: partition = None if not partition: # It does not exist. Create it and update it if there are # update-only properties. if not check_mode: create_props, update_props, stop, crypto_changes = \ process_properties(cpc, partition, params) partition = cpc.partitions.create(create_props) update2_props = {} for name in update_props: if name not in create_props: update2_props[name] = update_props[name] if update2_props: partition.update_properties(update2_props) # We refresh the properties after the update, in case an # input property value gets changed (for example, the # partition does that with memory properties). partition.pull_full_properties() if crypto_changes: change_crypto_config(partition, crypto_changes, check_mode) else: # TODO: Show props in module result also in check mode. pass changed = True else: # It exists. Stop if needed due to property update requirements, # or wait for an updateable partition status, and update its # properties. create_props, update_props, stop, crypto_changes = \ process_properties(cpc, partition, params) if update_props: if not check_mode: if stop: stop_partition(partition, check_mode) else: wait_for_transition_completion(partition) partition.update_properties(update_props) # We refresh the properties after the update, in case an # input property value gets changed (for example, the # partition does that with memory properties). partition.pull_full_properties() else: # TODO: Show updated props in mod.result also in chk.mode pass changed = True if crypto_changes: changed |= change_crypto_config(partition, crypto_changes, check_mode) if partition: changed |= start_partition(partition, check_mode) if partition and not check_mode: partition.pull_full_properties() status = partition.get_property('status') if status not in ('active', 'degraded'): raise StatusError( "Could not get partition {!r} into an active state, " "status is: {!r}".format(partition.name, status)) if partition: result = partition.properties return changed, result finally: session.logoff()
def cmd_vstorageresource_list(cmd_ctx, stogrp_name, options): # pylint: disable=missing-function-docstring client = zhmcclient.Client(cmd_ctx.session) stogrp = find_storagegroup(cmd_ctx, client, stogrp_name) cpc = stogrp.cpc try: vsrs = stogrp.virtual_storage_resources.list() except zhmcclient.Error as exc: raise click_exception(exc, cmd_ctx.error_format) show_list = [ 'name', 'storagegroup', ] if not options['names_only']: show_list.extend([ 'device-number', 'partition', 'adapter', 'port', 'wwpn', 'wwpn-status', ]) if options['uri']: show_list.extend([ 'element-uri', ]) sg_additions = {} partition_additions = {} adapter_additions = {} port_additions = {} wwpn_additions = {} wwpn_status_additions = {} for vsr in vsrs: try: sg_additions[vsr.uri] = stogrp_name partition_uri = vsr.prop('partition-uri') partition = cpc.partitions.find(**{'object-uri': partition_uri}) partition_additions[vsr.uri] = partition.name port_uri = vsr.prop('adapter-port-uri') adapter_uri = re.match(r'^(.*)/storage-ports/.*$', port_uri). \ group(1) adapter = cpc.adapters.find(**{'object-uri': adapter_uri}) port = adapter.ports.find(**{'element-uri': port_uri}) adapter_additions[vsr.uri] = adapter.name port_additions[vsr.uri] = port.name wwpn_info = vsr.prop('world-wide-port-name-info') wwpn_additions[vsr.uri] = wwpn_info['world-wide-port-name'] wwpn_status_additions[vsr.uri] = wwpn_info['status'] except zhmcclient.Error as exc: raise click_exception(exc, cmd_ctx.error_format) additions = { 'storagegroup': sg_additions, 'partition': partition_additions, 'adapter': adapter_additions, 'port': port_additions, 'wwpn': wwpn_additions, 'wwpn-status': wwpn_status_additions, } try: print_resources(cmd_ctx, vsrs, cmd_ctx.output_format, show_list, additions, all=options['all']) except zhmcclient.Error as exc: raise click_exception(exc, cmd_ctx.error_format)
cred = hmccreds.get(hmc, None) if cred is None: print("Credentials for HMC %s not found in credentials file %s" % (hmc, hmccreds_file)) sys.exit(1) userid = cred['userid'] password = cred['password'] try: print(__doc__) print("Using HMC %s with userid %s ..." % (hmc, userid)) session = zhmcclient.Session(hmc, userid, password) cl = zhmcclient.Client(session) timestats = example.get("timestats", None) if timestats: session.time_stats_keeper.enable() metric_groups = [ 'cpc-usage-overview', # Only in classic mode 'logical-partition-usage', # Only in classic mode 'channel-usage', # Only in classic mode 'dpm-system-usage-overview', # Only in DPM mode 'partition-usage', # Only in DPM mode 'adapter-usage', # Only in DPM mode 'crypto-usage', # Only in DPM mode 'flash-memory-usage', # Only in DPM mode 'roce-usage', # Only in DPM mode
def test_user_absent_present(ansible_mod_cls, desc, initial_user_props, initial_related_names, input_state, input_props, exp_user_props, exp_changed, check_mode, hmc_session): # noqa: F811, E501 """ Test the zhmc_user module with all combinations of absent & present state. """ expand = False # Expansion is tested elsewhere hd = hmc_session.hmc_definition client = zhmcclient.Client(hmc_session) console = client.consoles.console # Create a user name that does not exist user_name = new_user_name() # Create initial user, if specified so if initial_user_props is not None: user_props = STD_USER_PROPERTIES.copy() user_props.update(initial_user_props) user_props['name'] = user_name if user_props['authentication-type'] == 'local': assert 'password' in user_props assert 'password_rule_name' in initial_related_names password_rule_name = initial_related_names['password_rule_name'] password_rule = console.password_rules.find_by_name( password_rule_name) user_props['password-rule-uri'] = password_rule.uri if user_props['authentication-type'] == 'ldap': assert 'ldap_server_definition_name' in initial_related_names ldap_srv_def_name = \ initial_related_names['ldap_server_definition_name'] ldap_srv_def = console.ldap_server_definitions.find_by_name( ldap_srv_def_name) user_props['ldap-server-definition-uri'] = ldap_srv_def.uri console.users.create(user_props) else: user_props = None try: params = { 'hmc_host': hd.hmc_host, 'hmc_auth': dict(userid=hd.hmc_userid, password=hd.hmc_password), 'name': user_name, 'state': input_state, 'expand': expand, 'log_file': LOG_FILE, 'faked_session': None, } if input_props is not None: params['properties'] = input_props mod_obj = mock_ansible_module(ansible_mod_cls, params, check_mode) # Exercise the code to be tested with pytest.raises(SystemExit) as exc_info: zhmc_user.main() exit_code = exc_info.value.args[0] assert exit_code == 0, \ "Module unexpectedly failed with this message:\n{}". \ format(get_failure_msg(mod_obj)) changed, output_props = get_module_output(mod_obj) if changed != exp_changed: user_props_sorted = \ OrderedDict(sorted(user_props.items(), key=lambda x: x[0])) \ if user_props is not None else None input_props_sorted = \ OrderedDict(sorted(input_props.items(), key=lambda x: x[0])) \ if input_props is not None else None output_props_sorted = \ OrderedDict(sorted(output_props.items(), key=lambda x: x[0])) \ if output_props is not None else None raise AssertionError( "Unexpected change flag returned: actual: {}, expected: {}\n" "Initial user properties:\n{}\n" "Module input properties:\n{}\n" "Resulting user properties:\n{}".format( changed, exp_changed, pformat(user_props_sorted.items(), indent=2), pformat(input_props_sorted.items(), indent=2), pformat(output_props_sorted.items(), indent=2))) if input_state == 'present': assert_user_props(output_props, expand) finally: # Delete user, if it exists try: # We invalidate the name cache of our client, because the user # was possibly deleted by the Ansible module and not through our # client instance. console.users.invalidate_cache() user = console.users.find_by_name(user_name) except zhmcclient.NotFound: user = None if user: user.delete()
def ensure_active(params, check_mode): """ Ensure the CPC is active, and then set the properties. Raises: ParameterError: An issue with the module parameters. Error: Other errors during processing. zhmcclient.Error: Any zhmcclient exception can happen. """ # Note: Defaults specified in argument_spec will be set in params dict host = params['hmc_host'] userid, password, ca_certs, verify = get_hmc_auth(params['hmc_auth']) cpc_name = params['name'] activation_profile_name = params.get('activation_profile_name', None) _faked_session = params.get('_faked_session', None) # No default specified changed = False session = get_session(_faked_session, host, userid, password, ca_certs, verify) try: client = zhmcclient.Client(session) cpc = client.cpcs.find(name=cpc_name) # The default exception handling is sufficient for the above. # Activate the CPC cpc_status = cpc.get_property('status') if cpc_status in ('not-operating', 'no-power'): # CPC is inactive if not check_mode: cpc_dpm_enabled = cpc.get_property('dpm-enabled') if cpc_dpm_enabled: cpc.start() else: if not activation_profile_name: raise ParameterError( "CPC {0!r} is in classic mode and activation " "requires the 'activation_profile_name' parameter " "to be specified".format(cpc_name)) cpc.activate( activation_profile_name=activation_profile_name, force=True) changed = True elif cpc_status in ('active', 'operating', 'exceptions', 'service-required', 'degraded', 'acceptable', 'service'): # CPC is already active pass else: # cpc_status in ('not-communicating', 'status-check') # or any new future status values. raise StatusError( "CPC {0!r} cannot be activated because it is in status {1!r}". format(cpc_name, cpc_status)) # Set the properties cpc.pull_full_properties() result = dict(cpc.properties) update_props = process_properties(cpc, params) if update_props: if not check_mode: cpc.update_properties(update_props) # Some updates of CPC properties are not reflected in a new # retrieval of properties until after a few seconds (usually the # second retrieval). # Therefore, we construct the modified result based upon the input # changes, and not based upon newly retrieved properties. result.update(update_props) changed = True add_artificial_properties(result, cpc) return changed, result finally: session.logoff()
def ensure_attached(params, check_mode): """ Ensure that the specified crypto adapters and crypto domains are attached to the target partition. Raises: ParameterError: An issue with the module parameters. Error: Other errors during processing. zhmcclient.Error: Any zhmcclient exception can happen. """ # Note: Defaults specified in argument_spec will be set in params dict host = params['hmc_host'] userid, password = get_hmc_auth(params['hmc_auth']) cpc_name = params['cpc_name'] partition_name = params['partition_name'] adapter_count = params['adapter_count'] domain_range = params['domain_range'] access_mode = params['access_mode'] crypto_type = params['crypto_type'] faked_session = params.get('faked_session', None) # No default specified try: assert len(domain_range) == 2, \ "len(domain_range)={}".format(len(domain_range)) domain_range_lo = int(domain_range[0]) domain_range_hi = int(domain_range[1]) except (ValueError, AssertionError): raise ParameterError( "The 'domain_range' parameter must be a list containing two " "integer numbers, but is: {!r}".format(domain_range)) hmc_crypto_type = CRYPTO_TYPES_MOD2HMC[crypto_type] hmc_access_mode = ACCESS_MODES_MOD2HMC[access_mode] changed = False result = dict() result_changes = dict() try: session = get_session(faked_session, host, userid, password) client = zhmcclient.Client(session) cpc = client.cpcs.find(name=cpc_name) partition = cpc.partitions.find(name=partition_name) # The default exception handling is sufficient for the above. # Determine all crypto adapters of the specified crypto type. filter_args = { 'adapter-family': 'crypto', 'crypto-type': hmc_crypto_type, } all_adapters = cpc.adapters.list(filter_args=filter_args, full_properties=True) if not all_adapters: raise Error( "No crypto adapters of type {!r} found on CPC {!r} ".format( crypto_type, cpc_name)) # All crypto adapters in a CPC have the same number of domains # (otherwise the concept of attaching domains across all attached # adapters cannot work). Therefore, the max number of domains can be # gathered from any adapter. max_domains = all_adapters[0].maximum_crypto_domains # Parameter checking on domain range. # (can be done only now because it requires the max_domains). if domain_range_hi == -1: domain_range_hi = max_domains - 1 if domain_range_lo > domain_range_hi: raise ParameterError( "In the 'domain_range' parameter, the lower boundary (={}) " "of the range must be less than the higher boundary (={})". format(domain_range_lo, domain_range_hi)) # Parameter checking on adapter count. # (can be done only now because it requires the number of adapters). if adapter_count == -1: adapter_count = len(all_adapters) if adapter_count < 1: raise ParameterError( "The 'adapter_count' parameter must be at least 1, but is: {}". format(adapter_count)) if adapter_count > len(all_adapters): raise ParameterError( "The 'adapter_count' parameter must not exceed the number of " "{} crypto adapters of type {!r} in CPC {!r}, but is {}". format(len(all_adapters), crypto_type, cpc_name, adapter_count)) # # Get current crypto config of the target partition. # # Domains attached to the partition, as a dict with: # key: domain index # value: access mode attached_domains = dict() # Adapters attached to the partition, as a list of Adapter objects: attached_adapters = list() # Adapters not attached to the partition, as a list of Adapter objects: detached_adapters = list() _attached_adapter_uris = list() # URIs of attached adapters cc = partition.get_property('crypto-configuration') if cc: _attached_adapter_uris = cc['crypto-adapter-uris'] for dc in cc['crypto-domain-configurations']: di = int(dc['domain-index']) am = dc['access-mode'] LOGGER.debug("Crypto config of partition {!r}: " "Domain {} is attached in {!r} mode".format( partition.name, di, am)) attached_domains[di] = am for a in all_adapters: if a.uri in _attached_adapter_uris: LOGGER.debug("Crypto config of partition {!r}: " "Adapter {!r} is attached".format( partition.name, a.name)) attached_adapters.append(a) else: LOGGER.debug("Crypto config of partition {!r}: " "Adapter {!r} is not attached".format( partition.name, a.name)) detached_adapters.append(a) del _attached_adapter_uris # # Get the current crypto config of all partitions of the CPC. # # This is needed because finding out whether an adapter has the right # domains available by simply attaching it to the target partition # and reacting to the returned status does not work for stopped # partitions. # # All partition of the CPC, as a dict: # key: partition URI # value: Partition object all_partitions = cpc.partitions.list() all_partitions = dict( zip([p.uri for p in all_partitions], all_partitions)) # Crypto config of all partitions of the CPC, as a dict with: # key: adapter URI # value: dict: # key: domain index (for attached domains) # value: list of tuple(access mode, partition URI) all_crypto_config = dict() for p_uri in all_partitions: p = all_partitions[p_uri] cc = p.get_property('crypto-configuration') # The 'crypto-configuration' property is None or: # { # 'crypto-adapter-uris': ['/api/...', ...], # 'crypto-domain-configurations': [ # {'domain-index': 15, 'access-mode': 'control-usage'}, # ... # ] # } if cc: _adapter_uris = cc['crypto-adapter-uris'] for dc in cc['crypto-domain-configurations']: di = int(dc['domain-index']) am = dc['access-mode'] for a_uri in _adapter_uris: if a_uri not in all_crypto_config: all_crypto_config[a_uri] = dict() domains_dict = all_crypto_config[a_uri] # mutable if di not in domains_dict: domains_dict[di] = list() domains_dict[di].append((am, p.uri)) # # Determine the domains to be attached to the target partition # desired_domains = list(range(domain_range_lo, domain_range_hi + 1)) add_domains = list() # List of domain index numbers to be attached for di in desired_domains: if di not in attached_domains: # This domain is not attached to the target partition add_domains.append(di) elif attached_domains[di] != hmc_access_mode: # This domain is attached to the target partition but not in # the desired access mode. The access mode could be extended # from control to control+usage, but that is not implemented # by this code here. raise Error( "Domain {} is currently attached in {!r} mode to target " "partition {!r}, but requested was {!r} mode".format( di, ACCESS_MODES_HMC2MOD[attached_domains[di]], partition.name, access_mode)) else: # This domain is attached to the target partition in the # desired access mode pass # Create the domain config structure for the domains to be attached add_domain_config = list() for di in add_domains: add_domain_config.append({ 'domain-index': di, 'access-mode': hmc_access_mode }) # Check that the domains to be attached to the partition are available # on the currently attached adapters for a in attached_adapters: domains_dict = all_crypto_config[a.uri] for di in add_domains: if di in domains_dict: for am, p_uri in domains_dict[di]: if am != 'control' and hmc_access_mode != 'control': # Multiple attachments conflict only when both are # in usage mode p = all_partitions[p_uri] raise Error( "Domain {} cannot be attached in {!r} mode to " "target partition {!r} because it is already " "attached in {!r} mode to partition {!r}". format(di, access_mode, partition.name, ACCESS_MODES_HMC2MOD[am], p.name)) # Make sure the desired number of adapters is attached to the partition # and the desired domains are attached. # The HMC enforces the following for non-empty crypto configurations of # a partition: # - In the resulting config, the partition needs to have at least one # adapter attached. # - In the resulting config, the partition needs to have at least one # domain attached in usage mode. # As a result, on an empty crypto config, the first adapter and the # first domain(s) need to be attached at the same time. result_changes['added-adapters'] = [] result_changes['added-domains'] = [] missing_count = max(0, adapter_count - len(attached_adapters)) assert missing_count <= len(detached_adapters), \ "missing_count={}, len(detached_adapters)={}".\ format(missing_count, len(detached_adapters)) if missing_count == 0 and add_domain_config: # Adapters already sufficient, but domains to be attached LOGGER.debug( "Adapters sufficient - attaching domains {!r} in {!r} mode to " "target partition {!r}".format(add_domains, access_mode, partition.name)) if not check_mode: try: partition.increase_crypto_config([], add_domain_config) except zhmcclient.Error as exc: raise Error( "Attaching domains {!r} in {!r} mode to target " "partition {!r} failed: {}".format( add_domains, access_mode, partition.name, exc)) changed = True result_changes['added-domains'].extend(add_domains) elif missing_count > 0: # Adapters need to be attached for adapter in detached_adapters: if missing_count == 0: break # Check that the adapter has all needed domains available conflicting_domains = dict() if adapter.uri in all_crypto_config: domains_dict = all_crypto_config[adapter.uri] for di in desired_domains: if di in domains_dict: # The domain is already attached to some # partition(s) in some access mode for am, p_uri in domains_dict[di]: if am == 'control': # An attachment in control mode does not # prevent additional attachments continue if p_uri == partition.uri and \ am == hmc_access_mode: # This is our target partition, and the # domain is already attached in the desired # mode. continue p = all_partitions[p_uri] conflicting_domains[di] = (am, p.name) if conflicting_domains: LOGGER.debug( "Skipping adapter {!r} because the following of its " "domains are already attached to other partitions: " "{!r}".format(adapter.name, conflicting_domains)) continue LOGGER.debug( "Attaching adapter {!r} and domains {!r} in {!r} mode to " "target partition {!r}".format(adapter.name, add_domains, access_mode, partition.name)) if not check_mode: try: partition.increase_crypto_config([adapter], add_domain_config) except zhmcclient.Error as exc: raise Error( "Attaching adapter {!r} and domains {!r} in {!r} " "mode to target partition {!r} failed: {}".format( adapter.name, add_domains, access_mode, partition.name, exc)) changed = True result_changes['added-adapters'].append(adapter.name) result_changes['added-domains'].extend(add_domains) # Don't try to add again for next adapter: add_domain_config = [] add_domains = [] missing_count -= 1 if missing_count > 0: # Because adapters may be skipped, it is possible that there # are not enough adapters raise Error( "Did not find enough crypto adapters with attachable " "domains - missing adapters: {}; Requested domains: {}, " "Access mode: {}".format(missing_count, desired_domains, access_mode)) if not check_mode: # This is not optimal because it does not produce a result # in check mode, but because the actual config is determined, # instead of the artificially calculated one, it seems better # to return no config than the unchanged actual config. result.update(get_partition_config(partition, all_adapters)) return changed, result, result_changes finally: session.logoff()