### Edit PyU4V.conf [setup] section Source: https://pyu4v.readthedocs.io/en/latest/_sources/configuration.rst.txt Example configuration settings for the [setup] section of PyU4V.conf. Replace the values with your environment: username, password, server_ip, port, array (12-digit serial), and verify (True, file path, or False). ```bash [setup] username=pyu4v-user password=secret-pass server_ip=10.0.0.75 port=8443 array=00012345678 verify=/path-to-file/server_hostname.pem ``` -------------------------------- ### Configure [setup] section in PyU4V.conf Source: https://pyu4v.readthedocs.io/en/latest/configuration.html Example [setup] section in PyU4V.conf. Replace the values with your environment: username and password for Unisphere REST login, server_ip and port for the Unisphere server, array for the 12-digit serial, and verify for SSL verification (True, a file path, or False). ```ini [setup] username=pyu4v-user password=secret-pass server_ip=10.0.0.75 port=8443 array=00012345678 verify=/path-to-file/server_hostname.pem ``` -------------------------------- ### Create and Manage Clones (clone.py) Source: https://pyu4v.readthedocs.io/en/latest/_sources/programmers_guide_src/clone.rst.txt Example of replication calls to create independent copies of data using clone technology. Referenced from the Dell Solutions Enabler TimeFinder/Clone CLI User Guide. ```python .. literalinclude:: code/clone.py :linenos: :language: python :lines: 14-34 ``` -------------------------------- ### Install PyU4V from source using git and pip Source: https://pyu4v.readthedocs.io/en/latest/_sources/installation.rst.txt Clones the PyU4V repository from GitHub and installs it using pip. Run these commands in a terminal. ```bash $ git clone https://github.com/dell/PyU4V $ cd PyU4V/ $ pip install . ``` -------------------------------- ### Build PyU4V documentation with Sphinx Source: https://pyu4v.readthedocs.io/en/latest/_sources/index.rst.txt Commands to install Sphinx and the Read the Docs theme, then build the HTML documentation. Run from the PyU4V/docs directory. Output is generated in /PyU4V/docs/build. ```bash $ pip install sphinx $ pip install sphinx-rtd-theme $ cd PyU4V/docs $ make clean && make html ``` -------------------------------- ### Perform System Health Check and Retrieve Disk Details Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/system.html Demonstrates performing a system health check, retrieving the last check's details, and iterating through installed disk IDs to get their specific information. ```python 1"""docs/source/programmers_guide_src/code/system-health_check.py.""" 2 3import PyU4V 4 5# Initialise PyU4V connection to Unisphere 6conn = PyU4V.U4VConn() 7 8# Perform a system health check, this call can take 15-20 minutes to 9# complete in Unisphere due to the nature of the checks performed 10conn.system.perform_health_check(description='test-hc-dec19') 11 12# Get details of the last system health check 13health_check = conn.system.get_system_health() 14 15# Get a list of physical disks installed in the array 16disk_list = conn.system.get_disk_id_list() 17# Get disk information for each disk installed 18for disk in disk_list.get('disk_ids'): 19 disk_info = conn.system.get_disk_details(disk_id=disk) 20 21# Close the session 22conn.close_session() ``` -------------------------------- ### Check PyU4V installation with pip show Source: https://pyu4v.readthedocs.io/en/latest/_sources/quick_start.rst.txt Verifies that PyU4V is installed before initialising a connection. Run this command in a terminal after following the installation instructions. ```bash $ pip show PyU4V ``` -------------------------------- ### Pass configuration directly to U4VConn() Source: https://pyu4v.readthedocs.io/en/latest/_sources/configuration.rst.txt Initializes U4VConn() with environment settings passed as keyword arguments, overriding any PyU4V.conf settings. The example shows a successful call to get_unisphere_version() returning a version dictionary. ```Python >>> import PyU4V >>> conn = U4VConn( username='pyu4v-user', password='secret-pass', server_ip='10.0.0.75', port='8443', verify=True, array_id='00012345678') >>> conn.common.get_unisphere_version() {'version': 'V9.1.0.5'} ``` -------------------------------- ### Install PyU4V via pip Source: https://pyu4v.readthedocs.io/en/latest/_sources/installation.rst.txt Installs PyU4V directly from PyPI using pip. The second command installs a specific version (10.4.0.0). ```bash $ pip install PyU4V # Install a specific version $ pip install PyU4V==10.4.0.0 ``` -------------------------------- ### Download and upload system settings with PyU4V Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/system.html Downloads system settings from the primary array, excluding system thresholds, then uploads and applies them to a target array. Requires a file_password to verify settings when applying them elsewhere, preventing unauthorized changes. The example prints the saved settings location and closes the session. ```python 1"""docs/source/programmers_guide_src/code/system-settings_management.py.""" 2 3import PyU4V 4 5# Initialise PyU4V connection to Unisphere 6conn = PyU4V.U4VConn() 7 8# Download system settings for primary array defined in PyU4V.conf, exclude the 9# system thresholds settings 10settings_info = conn.system.download_system_settings( 11 file_password='PyU4V', dir_path='~/datastore/system_settings', 12 file_name='settings_gold_master', exclude_system_threshold_settings=True) 13 14# Assert the download operation was successful and print the location of the 15# downloaded file 16assert settings_info.get('success') is True 17print('The system settings for {array} are saved to: {loc}'.format( 18 array=conn.array_id, loc=settings_info.get('settings_path'))) 19 20# Upload the settings and apply to target array 21upload_details = conn.system.upload_settings( 22 file_password='PyU4V', file_path=settings_info.get('settings_path'), 23 array_id='000111222333') 24 25# Close the session 26conn.close_session() ``` -------------------------------- ### Restore Clone (restoreclone.py) Source: https://pyu4v.readthedocs.io/en/latest/_sources/programmers_guide_src/clone.rst.txt Example of restoring a clone, as part of the clone management workflow. ```python .. literalinclude:: code/restoreclone.py :linenos: :language: python :lines: 14-18 ``` -------------------------------- ### Create, modify, and delete a snapshot policy with PyU4V Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/replication.html Connects to Unisphere for PowerMax, creates a storage group, creates a snapshot policy, modifies it, and deletes it. The example asserts the policy is created and renamed, then closes the session. ```python 1"""docs/source/programmers_guide_src/code/replication-snapshot_policy.py""" 2 3import PyU4V 4 5# Set up connection to Unisphere for PowerMax Server, details collected 6# from configuration file in working directory where script is stored. 7conn = PyU4V.U4VConn() 8 9# Create storage Group with one volume 10storage_group = conn.provisioning.create_non_empty_storage_group( 11 srp_id='SRP_1', storage_group_id='PyU4V_SG', service_level='Diamond', 12 workload=None, num_vols=1, vol_size=1, cap_unit='GB') 13 14# Create a snapshot policy for the new storage group 15policy_name = 'PyU4V-Test_Policy' 16snap_policy = conn.snapshot_policy.create_snapshot_policy( 17 snapshot_policy_name=policy_name, interval='1 Day', 18 cloud_retention_days=7, cloud_provider_name='Generic_Provider', 19 local_snapshot_policy_snapshot_count=5) 20 21# Confirm the snapshot policy was created successfully 22assert policy_name in conn.snapshot_policy.get_snapshot_policy_list() 23 24# Get snapshot policy detailed info 25snap_policy_details = conn.snapshot_policy.get_snapshot_policy( 26 snapshot_policy_name=policy_name) 27 28# Modify the snapshot policy 29new_policy_name = 'PyU4V-Test_Policy-5mins' 30new_snap_policy = conn.snapshot_policy.modify_snapshot_policy( 31 snapshot_policy_name=policy_name, action='Modify', 32 new_snapshot_policy_name=new_policy_name, interval_mins=5) 33 34# Confirm the snapshot policy was renamed successfully 35assert new_policy_name in conn.snapshot_policy.get_snapshot_policy_list() 36assert policy_name not in conn.snapshot_policy.get_snapshot_policy_list() 37 38# Delete the snapshot policy 39conn.snapshot_policy.delete_snapshot_policy( 40 snapshot_policy_name=new_policy_name) 41 42# Close the session 43conn.close_session() ``` -------------------------------- ### Asynchronous storage provisioning with PyU4V: check headroom, create SG, host, port group, masking view Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/provisioning.html Use this script to asynchronously provision storage on a Unisphere array. It first checks available headroom via conn.wlp.get_headroom() and only proceeds if the requested capacity (10GB) fits. It then creates a storage group asynchronously, waits for the job, creates a host, port group, and masking view, and finally closes the session. The example uses hard-coded initiator IDs and port IDs; these can be retrieved via conn.provisioning.get_available_initiator() and conn.provisioning.get_port_list(). ```python 1"""docs/source/programmers_guide_src/code/provision-async_create_storage.py""" 2 3import PyU4V 4 5# Initialise PyU4V connection to Unisphere 6conn = PyU4V.U4VConn() 7 8# Before provisioning storage we are going to check that there is enough 9# headroom left on the array for our provisioning operations 10REQUESTED_CAPACITY = 10 11 12# Get the available headroom for the SRP and service level required 13headroom_info = conn.wlp.get_headroom(array_id=conn.array_id, 14 srp='SRP_1', slo='Diamond') 15 16# Extract the capacity value from the headroom_info REST response 17headroom_capacity = headroom_info[ 18 'OLTP'][0]['headroom'][0]['headroomCapacity'] 19 20# If the requested capacity of 10GB is less than or equal to the available 21# capacity proceed with the provisioning operations 22if REQUESTED_CAPACITY <= int(headroom_capacity): 23 # Create a non-empty storage group using asynchronous request - we can 24 # wait until the job completes or proceed with operations and check 25 # back at a later time 26 storage_group_async_job = ( 27 conn.provisioning.create_non_empty_storage_group( 28 srp_id='SRP_1', storage_group_id='example-sg', 29 service_level='Diamond', num_vols=1, 30 vol_size=REQUESTED_CAPACITY, cap_unit='GB', _async=True)) 31 32 # We will wait this time on the results of the storage group create 33 # request 34 conn.common.wait_for_job(operation='Create SG with volume', 35 job=storage_group_async_job) 36 print('Storage Group created successfully...') 37 38 # Get information on our new storage group 39 storage_group_info = conn.provisioning.get_storage_group( 40 storage_group_name='Example-SG') 41 print('Storage Group details: {details}'.format( 42 details=storage_group_info)) 43 44 # Create a Host using supplied initiator IDs, these can be also be 45 # retrieved via the call conn.provisioning.get_available_initiator() 46 initiator_list = ['iqn:2020-test1', 'iqn:2020-test1'] 47 host_info = conn.provisioning.create_host( 48 host_name='Example-Host', initiator_list=initiator_list) 49 print('Host created successfully...') 50 print('New Host details: {details}'.format(details=host_info)) 51 52 # Create a Port Group using supplied ports, these could be also be 53 # retrieved via the call conn.provisioning.get_port_list() 54 port_group_info = conn.provisioning.create_port_group( 55 port_group_id='Example-PG', director_id='SE-01', port_id='1') 56 print('Port Group created successfully...') 57 print('Port Group details: {details}'.format(details=port_group_info)) 58 59 # Create a Masking View and tie all the elements we have created 60 # together 61 masking_view_info = ( 62 conn.provisioning.create_masking_view_existing_components( 63 port_group_name='Example-PG', masking_view_name='Example-MV', 64 storage_group_name='Example-SG', host_name='Example-Host')) 65 print('Masking View created...') 66 print('Masking View details: {details}'.format( 67 details=masking_view_info)) 68 69# Close the session 70conn.close_session() ``` -------------------------------- ### GET /{UNI_VERSION}/settings/registration/cloudiq Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/settings.html Lists the CloudIQ Data Collection Configuration. ```APIDOC ## GET /{UNI_VERSION}/settings/registration/cloudiq ### Description Lists the CloudIQ Data Collection Configuration. ### Method GET ### Endpoint /{UNI_VERSION}/settings/registration/cloudiq ### Parameters #### Query Parameters - **array_id** (string) - Optional - The storage array ID ### Response #### Success Response (200) - **response** (object) - The response from the get request. #### Response Example { "response": {} } ``` -------------------------------- ### Manage performance thresholds with PyU4V Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/performance.html Demonstrates retrieving all performance threshold settings, writing them to a CSV file, reading the CSV into a dictionary, updating threshold values, and uploading the updated settings to Unisphere. The example also shows directly updating a threshold metric without a CSV using update_threshold_settings. Requires an active PyU4V connection to Unisphere. ```python 1"""docs/source/programmers_guide_src/code/performance-thresholds.py.""" 2 3import os 4import PyU4V 5 6# Initialise PyU4V connection to Unisphere 7conn = PyU4V.U4VConn() 8 9# Set the CSV file name and path 10current_directory = os.getcwd() 11output_csv_name = 'thresholds-test.csv' 12output_csv_path = os.path.join(current_directory, output_csv_name) 13 14# Generate a CSV file with all of the thresholds and corresponding values 15conn.performance.generate_threshold_settings_csv( 16 output_csv_path=output_csv_path) 17 18# Read the CSV values into a dictionary, cast all string booleans and 19# numbers to their proper types 20threshold_dict = PyU4V.utils.file_handler.read_csv_values(output_csv_path, 21 convert=True) 22 23# Increase all of the first threshold values by 5 and second threshold 24# values by 10, alert only on the KPIs 25for i in range(0, len(threshold_dict.get('metric'))): 26 threshold_dict['firstThreshold'][i] += 5 27 threshold_dict['secondThreshold'][i] += 5 28 if threshold_dict['kpi'][i] is True: 29 threshold_dict['alertError'][i] = True 30 31# Process the CSV file and update the thresholds with their corresponding 32# values, we are only going to set the threshold value if it is a KPI 33conn.performance.set_thresholds_from_csv(csv_file_path=output_csv_path, 34 kpi_only=True) 35 36# It is also possible to set a threshold value without editing the values 37# in a CSV, the threshold metric and be edited directly 38threshold_settings = conn.performance.update_threshold_settings( 39 category='Array', metric='PercentCacheWP', first_threshold=60, 40 first_threshold_occurrences=2, first_threshold_samples=6, 41 first_threshold_severity='INFORMATION', second_threshold=90, 42 second_threshold_occurrences=1, second_threshold_samples=3, 43 second_threshold_severity='CRITICAL', alert=True) 44 45# Close the session 46conn.close_session() ``` -------------------------------- ### PyU4V.common URI building and GET request methods Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/common.html These methods form the URI-building and GET-request workflow in PyU4V.common. _build_uri assembles a versioned or versionless URI from keyword components, while _build_uri_get_version resolves the version flag and logs a debug message when both version and no_version are supplied. get_request sends the GET and raises ResourceNotFoundException on failure; get_resource builds the URI and delegates to get_request, deriving resource_type from args or kwargs. ```python def _build_uri(self, **kwargs): """Build the target URI. :key version: Unisphere version -- int :key no_version: if versionless uri -- bool :key category: resource category e.g. sloprovisioning -- str :key resource_level: resource level e.g. storagegroup -- str :key resource_level_id: resource level id -- str :key resource_type: optional name of resource -- str :key resource_type_id: optional name of resource -- str :key resource: optional name of resource -- str :key resource_id: optional name of resource -- str :key object_type: optional name of resource -- str :key object_type_id: optional name of resource -- str :returns: target URI -- str """ target_uri, version = str(), None if kwargs.get('category') not in ['performance', 'common']: version = self._build_uri_get_version(kwargs.get('version'), kwargs.get('no_version')) if version: target_uri += '/{version}'.format(version=version) target_uri += '/{category}'.format( category=kwargs.get('category')) if kwargs.get('resource_level'): target_uri += '/{resource_level}'.format( resource_level=kwargs.get('resource_level')) if kwargs.get('resource_level_id'): target_uri += '/{resource_level_id}'.format( resource_level_id=kwargs.get('resource_level_id')) if kwargs.get('resource_type'): target_uri += '/{resource_type}'.format( resource_type=kwargs.get('resource_type')) if kwargs.get('resource_type_id'): target_uri += '/{resource_type_id}'.format( resource_type_id=kwargs.get('resource_type_id')) if kwargs.get('resource'): target_uri += '/{resource}'.format( resource=kwargs.get('resource')) if kwargs.get('resource_id'): target_uri += '/{resource_id}'.format( resource_id=kwargs.get('resource_id')) if kwargs.get('object_type'): target_uri += '/{object_type}'.format( object_type=kwargs.get('object_type')) if kwargs.get('object_type_id'): target_uri += '/{object_type_id}'.format( object_type_id=kwargs.get('object_type_id')) return target_uri ``` ```python def _build_uri_get_version(self, version=None, no_version=False): """Get the Unisphere version for the target URI. :param version: version to use from kwargs -- str :param no_version: if URI should be versionless -- bool :returns: version -- str """ if not version and no_version: version = None elif not version and not no_version: version = self.UNI_VERSION elif version and no_version: LOG.debug( 'Version has been specified along with no_version flag, ' 'ignoring no_version flag and using version {ver}'.format( ver=version)) return version ``` ```python def get_request(self, target_uri, resource_type, params=None): """Send a GET request to the array. :param target_uri: target uri -- str :param resource_type: the resource type, e.g. maskingview -- str :param params: optional filter params -- dict :returns: resource_object -- dict :raises: ResourceNotFoundException """ message, sc = self.request(target_uri, GET, params=params) operation = 'GET {resource_type}'.format(resource_type=resource_type) self.check_status_code_success(operation, sc, message) return message ``` ```python def get_resource(self, *args, **kwargs): """Get resource details from the array. :key version: Unisphere version -- int :key no_version: if versionless uri -- bool :key category: resource category e.g. sloprovisioning -- str :key resource_level: resource level e.g. storagegroup -- str :key resource_level_id: resource level id -- str :key resource_type: optional name of resource -- str :key resource_type_id: optional name of resource -- str :key resource: optional name of resource -- str :key resource_id: optional name of resource -- str :key object_type: optional name of resource -- str :key object_type_id: optional name of resource -- str :key params: query parameters -- dict :returns: resource object -- dict """ target_uri = self._build_uri(**kwargs) resource_type = None if args: resource_type = args[2] elif not args and kwargs: resource_type = kwargs.get('resource_level') return self.get_request( target_uri, resource_type, kwargs.get('params')) ``` -------------------------------- ### Test connection with get_unisphere_version and get_array_list Source: https://pyu4v.readthedocs.io/en/latest/_sources/quick_start.rst.txt Runs test queries to validate the connection to Unisphere. The first call returns the Unisphere version and the second returns a list of arrays. The output shown is an example of what you can expect. ```python conn.common.get_unisphere_version() ('V10.2.0.0', '101') conn.common.get_array_list() ["000197900123", "000197900124", "000197900125", "000197900126"] ``` -------------------------------- ### GET /{UNI_VERSION}/settings/alert/performance_threshold Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/settings.html Lists Global Thresholds and Alerts. ```APIDOC ## GET /{UNI_VERSION}/settings/alert/performance_threshold ### Description Lists Global Thresholds and Alerts. ### Method GET ### Endpoint /{UNI_VERSION}/settings/alert/performance_threshold ### Parameters #### Query Parameters - **enabled_alerts** (bool) - Optional - Whether to include enabled alerts - **category** (string) - Optional - Category filter ### Response #### Success Response (200) - **response** (object) - The response from the get request. #### Response Example { "response": {} } ``` -------------------------------- ### get_storage_group_list method (partial) Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/migration.html Begins the method to get a list of all storage groups or migrating storage groups. The source is truncated at this point. ```python [docs] def get_storage_group_list(self, include_migrations=False): """Get list of all storage groups or migrating storage groups. ``` -------------------------------- ### Create SRDF/Metro protection for a storage group with PyU4V Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/replication.html Creates a storage group on the PowerMax array and protects it to a remote array using SRDF/Metro (Active mode). The example runs asynchronously and waits for the SRDF pairing job to complete before retrieving the RDFG number and SRDF details. Requires an active PyU4V connection to Unisphere. ```python 1"""docs/source/programmers_guide_src/code/replication-srdf_protection.py""" 2 3import PyU4V 4 5# Initialise PyU4V connection to Unisphere 6conn = PyU4V.U4VConn() 7 8# Create storage Group with one volume using settings specified for 9# service level and capacity 10storage_group = conn.provisioning.create_non_empty_storage_group( 11 srp_id='SRP_1', storage_group_id='PyU4V_SG', service_level='Diamond', 12 workload=None, num_vols=1, vol_size=1, cap_unit='GB') 13 14# Make a call to setup the remote replication, this will automatically 15# create a storage group with the same name on the remote array with the 16# correct volume count and size, the example here is executed 17# asynchronously and a wait is added to poll the async job id until 18# complete 19srdf_job_id = conn.replication.create_storage_group_srdf_pairings( 20 storage_group_id=storage_group['storageGroupId'], 21 remote_sid=conn.remote_array, srdf_mode="Active", _async=True) 22 23# Wait until the previous create SRDF pairing job has completed before 24# proceeding 25conn.common.wait_for_job_complete(job=srdf_job_id) 26 27# The now protected storage group will have an RDFG associated with it, 28# using the function conn.replication.get_storage_group_rdfg() function we 29# can retrieve a list of RDFGs associated with the storage group, in this 30# case there will only be one 31rdfg_list = conn.replication.get_storage_group_srdf_group_list( 32 storage_group_id=storage_group['storageGroupId']) 33 34# Extract the (only) RDFG number from the retrieved list 35rdfg_number = rdfg_list[0] 36 37# Finally the details of the protected storage group can be output to the 38# user. 39storage_group_srdf_info = conn.replication.get_storage_group_srdf_details( 40 storage_group_id=storage_group['storageGroupId'], 41 rdfg_num=rdfg_number) 42 43# Close the session 44conn.close_session() ``` -------------------------------- ### Custom REST call with PyU4V.common.create_resource() to create a storage group clone Source: https://pyu4v.readthedocs.io/en/latest/quick_start.html Custom function that uses PyU4V.common.create_resource() to create a clone copy of a storage group via the Unisphere REST API. It builds a payload with optional parameters (consistent, establish_terminate, force, star, skip) and sends a POST to the /100/replication/symmetrix/{array_id}/storagegroup/{storage_group_id}/clone/storagegroup endpoint. The example initializes a U4VConn connection and closes the session at the end. ```python 1"""docs/source/programmers_guide_src/code/custom_call.py.""" 2import PyU4V 3 4# Initialise PyU4V connection to Unisphere 5conn = PyU4V.U4VConn() 6 7# Create a Clone Copy of a Stoage group, The Target Storage Group will be 8# created with all required volumes for the clone copy if it does not exist 9# already. 10 11 12def my_custom_api_Call( 13 storage_group_id, target_storage_group_id, 14 consistent=True, establish_terminate=False, 15 array_id=None, force=False, star=False, skip=False): 16 """Create Clone. 17 :param storage_group_id: The Storage Group ID -- string 18 :param consistent: creates the clone crash consistent using ECA 19 technology -- bool 20 :param establish_terminate: creates the clone and immediately 21 terminates, very useful if you want to 22 make an independent copy available 23 immediately but don't intend to use for 24 restore purposes -- bool 25 :param array_id: The storage array ID -- string 26 :param target_storage_group_id: name of storage group to contain 27 clone devices -- string 28 :param force: Attempts to force the operation even though one or more 29 volumes may not be in the normal, expected state(s) for 30 the specified operation -- bool 31 :param star: Acknowledge the volumes are in an SRDF/Star 32 configuration -- bool 33 :param skip: Skips the source locks action -- bool 34 35 """ 36 array_id = array_id if array_id else conn.array_id 37 payload = { 38 "target_storage_group_name": target_storage_group_id, 39 "establish_terminate": establish_terminate, 40 "consistent": consistent, 41 "force": force, 42 "star": star, 43 "skip": skip 44 } 45 return conn.common.create_resource( 46 target_uri=f"/100/replication/symmetrix" 47 f"/{array_id}/storagegroup/{storage_group_id}" 48 f"/clone/storagegroup", payload=payload) 49 50 51# Close the session 52conn.close_session() ``` -------------------------------- ### Copy sample PyU4V.conf to home directory Source: https://pyu4v.readthedocs.io/en/latest/_sources/configuration.rst.txt Creates the ~/.PyU4V directory and copies the sample configuration file to it, renaming it to PyU4V.conf. The ~ symbol represents the user's home directory; on Windows use the full path like C:\Users\{user}\.PyU4V. ```bash $ mkdir ~/.PyU4V $ cp PyU4V/PyU4V.conf.sample ~/.PyU4V/PyU4V.conf ``` -------------------------------- ### Get CloudIQ Data Collection Configuration Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/settings.html Retrieves the CloudIQ data collection configuration by sending a GET request to the settings registration cloudiq endpoint. No query parameters are passed. ```python query_params = {} return self.common.get_request( target_uri=f"/{self.common.UNI_VERSION}/settings/" f"registration/cloudiq", resource_type=None, params=query_params) ``` -------------------------------- ### Connect to Unisphere with PyU4V and retrieve version and arrays Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/basic_operations.html Initialises a PyU4V connection to Unisphere using U4VConn with server IP, port, certificate path, and credentials. Retrieves the Unisphere version and array list, filters for local arrays, prints results, and closes the session. Requires a valid Unisphere instance and the specified certificate file. ```python 1"""docs/source/programmers_guide_src/code/basic-unisphere_connect.py.""" 2 3import PyU4V 4 5# Initialise PyU4V connection to Unisphere 6conn = PyU4V.U4VConn( 7 server_ip='10.0.0.75', port=8443, verify='~/.PyU4V/Unisphere91.pem', 8 username='pyu4v-user', password='secret-pass') 9 10# Get the Unisphere version 11version = conn.common.get_uni_version() 12 13# Retrieve a list of arrays managed by your instance of Unisphere 14array_list = conn.common.get_array_list() 15 16# Output results to screen 17print('Congratulations on your first connection to Unisphere, your ' 18 'version is: {ver}'.format(ver=version[0])) 19print('This instance of Unisphere instance manages the following arrays: ' 20 '{arr_list}'.format(arr_list=array_list)) 21 22# GET those arrays which are local to this instance of Unisphere 23local_array_list = list() 24for array_id in array_list: 25 array_details = conn.common.get_array(array_id) 26 if array_details['local']: 27 local_array_list.append(array_id) 28 29# Output results to screen 30print('The following arrays are local to this Unisphere instance: ' 31 '{arr_list}'.format(arr_list=local_array_list)) 32 33# Close the session 34conn.close_session() ``` -------------------------------- ### get_health_check_details Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/system.html Gets details of an individual health check for a specified array. ```APIDOC ## get_health_check_details ### Description Gets details of an individual health check. ### Method GET ### Endpoint /system/symmetrix/{array_id}/health/health_check/{health_check_id} ### Parameters #### Path Parameters - **array_id** (string) - Optional - The array ID. If not provided, the default array ID is used. - **health_check_id** (string) - Required - The health check ID. ### Response #### Success Response (200) - **health check details** (dict) - The details of the health check. #### Response Example { "health_check_details": {} } ``` -------------------------------- ### Initialise PyU4V connection with U4VConn Source: https://pyu4v.readthedocs.io/en/latest/_sources/quick_start.rst.txt Initialises a connection to Unisphere using the configuration provided in PyU4V.conf. The U4VConn constructor loads the settings from the configuration file. ```python import PyU4V conn = PyU4V.U4VConn() ``` -------------------------------- ### Get snapshot policy compliance for last week Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/snapshot_policy.html Calls get_snapshot_policy_compliance with last_week=True to retrieve compliance for the last week. ```python def get_snapshot_policy_compliance_last_week( self, storage_group_name): """Get compliance attributes on a storage group for the last week. :param storage_group_name: storage group name :returns: resource -- dict """ return self.get_snapshot_policy_compliance( storage_group_name, last_week=True) ``` -------------------------------- ### Create a storage group snapshot with SnapVX Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/replication.html Creates a storage group with a 1GB volume, generates a snapshot name using the current time, creates a snapshot of the storage group, and verifies the snapshot exists in the snapshot list. Requires a configured PyU4V connection. ```python """docs/source/programmers_guide_src/code/replication-snapshot_create.py""" import PyU4V import time # Initialise PyU4V connection to Unisphere conn = PyU4V.U4VConn() # Create storage Group with one volume using settings specified for # service level and capacity storage_group = conn.provisioning.create_non_empty_storage_group( srp_id='SRP_1', storage_group_id='PyU4V_SG', service_level='Diamond', workload=None, num_vols=1, vol_size=1, cap_unit='GB') # Define a Name for the Snapshot, in this case the name auto appends # the host # time for when it was taken for ease of identification snap_name = 'PyU4V_Snap_' + time.strftime('%d%m%Y%H%M%S') # Create the snapshot of the storage group containing the volume and # storage group created in the previous step snapshot = conn.replication.create_storage_group_snapshot( storage_group_id=storage_group['storageGroupId'], snap_name=snap_name) # Confirm the snapshot was created successfully, get a list of storage # group snapshots snap_list = conn.replication.get_storage_group_snapshot_list( storage_group_id=storage_group['storageGroupId']) # Assert the snapshot name is in the list of storage group snapshots assert snapshot['name'] in snap_list # Close the session conn.close_session() ``` -------------------------------- ### get_audit_log_list Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/system.html Retrieves a list of audit logs from Unisphere between a start and end date. Supports filtering through various input parameters. ```APIDOC ## get_audit_log_list ### Description Get a list of audit logs from Unisphere between start and end date. It is possible to filter this list through the input parameters. ### Method GET ### Endpoint /system/audit_logs ### Parameters #### Query Parameters - **start_time** (string) - Required - Start time for audit log retrieval. - **end_time** (string) - Required - End time for audit log retrieval. - **array_id** (string) - Optional - Array ID. - **user_name** (string) - Optional - User name. - **host_name** (string) - Optional - Host name. - **client_host** (string) - Optional - Client host. - **message** (string) - Optional - Message. - **record_id** (string) - Optional - Record ID. - **activity_id** (string) - Optional - Activity ID. - **application_id** (string) - Optional - Application ID. - **application_version** (string) - Optional - Application version. - **task_id** (string) - Optional - Task ID. - **process_id** (string) - Optional - Process ID. - **vendor_id** (string) - Optional - Vendor ID. - **os_type** (string) - Optional - OS type. - **os_revision** (string) - Optional - OS revision. - **api_library** (string) - Optional - API library. - **api_version** (string) - Optional - API version. - **audit_class** (string) - Optional - Audit class. - **action_code** (string) - Optional - Action code. - **function_class** (string) - Optional - Function class. ### Request Example GET /system/audit_logs?start_time=2023-01-01T00:00:00Z&end_time=2023-01-02T00:00:00Z ### Response #### Success Response (200) - **audit_logs** (array) - List of audit log entries. #### Response Example [ { "id": "log1", "message": "Example log" } ] ``` -------------------------------- ### Create SRDF/Metro Smart DR environment with PyU4V Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/replication.html Creates a storage group and then an SRDF/Metro Smart DR environment using PyU4V. Requires PowerMaxOS 5978.669.669 or higher and three arrays (MetroR1, MetroR2, DR). The environment creation is asynchronous and can take many minutes; wait for the job to complete before querying environment details. After Metro DR is set up, the environment is controlled exclusively by the environment name, and standard replication calls cannot be used until the Metro DR environment is deleted. ```python 1"""docs/source/programmers_guide_src/code/replication-metro_dr.py""" 2 3import PyU4V 4 5# Initialise PyU4V connection to Unisphere 6conn = PyU4V.U4VConn() 7 8metro_r1_array_id = '000297600111' 9metro_r2_array_id = '000297600112' 10dr_array_id = '000297600113' 11 12sg_name = 'PyU4V_Test_MetroDR' 13environment_name = 'PyU4VMetro' 14 15# Create a storage Group with some volumes 16sg_details = conn.provisioning.create_non_empty_storage_group( 17 storage_group_id=sg_name, service_level='Diamond', 18 num_vols=5, vol_size=6, cap_unit='GB', srp_id='SRP_1', 19 workload=None) 20 21""" 22The next call section creates the metro dr environment, this includes all 23Necessary SRDF setup creating the SRDF metro pairings and remote devices 24and storage group at the Metro R2 array. 25 26The API also creates all the necessary SRDF groups between R11, R21 and R2 27for the DR leg, this includes a recovery SRDF group. The example is 28using async execution of the REST calls, this task can take many 29minutes to complete and depending on the number of devices. 30""" 31job = conn.metro_dr.create_metrodr_environment( 32 storage_group_name=sg_name, environment_name=environment_name, 33 metro_r1_array_id=metro_r1_array_id, metro_r2_array_id=metro_r2_array_id, 34 dr_array_id=dr_array_id, dr_replication_mode='adaptivecopydisk') 35 36conn.common.wait_for_job_complete(job=job) 37 38metro_dr_env_details = conn.metro_dr.get_metrodr_environment_details( 39 environment_name, array_id=metro_r1_array_id) 40 41# Close the session 42conn.close_session() ``` -------------------------------- ### Initialize REST clients and feature modules in PyU4V.univmax_conn Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/univmax_conn.html Initializes REST clients and feature modules in the PyU4V connection class. Handles verification from config, requires either username/password or api_key, and sets up both standard and enhanced REST clients. Assigns array_id to all feature modules and persists u4v_version on clients. ```python # Set verification if verify is None: try: verify = config.get(SETUP, VERIFY) if verify.lower() == 'false': verify = False elif verify.lower() == 'true': verify = True except Exception: verify = True if None in [username, password, server_ip, port] and api_key is None: # Keep legacy behavior: either username+password or api_key # must be provided raise exception.MissingConfigurationException # Initialise REST sessions base_url = f'https://{server_ip}:{port}/univmax/restapi' enhanced_api_url = f'https://{server_ip}:{port}/univmax/rest' if username and password: self.rest_client = RestRequests( username, password, verify, base_url, interval, retries, application_type, proxies=proxies, timeout=self.timeout, ) else: # API-key auth path self.rest_client = RestRequests( verify, base_url, interval, retries, application_type, proxies=proxies, timeout=self.timeout, api_key=api_key) # Enhanced client self.enhanced_rest_client = RestRequests( username, password, verify, enhanced_api_url, interval, retries, application_type, proxies=proxies, timeout=self.timeout) # Persist version on clients so any downstream CommonFunctions can pick it up self.rest_client.u4v_version = self.u4v_version self.enhanced_rest_client.u4v_version = self.u4v_version # Request shortcut self.request = self.rest_client.rest_request # CommonFunctions honors the passed version (or the rest_client attribute) self.common = CommonFunctions(self.rest_client, self.u4v_version) # Validate Unisphere (skip minimum check if custom version specified) self.validate_unisphere() # Initialize feature modules (no need to pass u4v_version; they will # use CommonFunctions internally, which reads rest_client.u4v_version) self.clone = CloneFunctions(self.array_id, self.rest_client) self.provisioning = ProvisioningFunctions(self.array_id, self.rest_client) self.performance = PerformanceFunctions(self.array_id, self.rest_client) self.replication = ReplicationFunctions(self.array_id, self.rest_client) self.metro_dr = MetroDRFunctions(self.array_id, self.rest_client) self.migration = MigrationFunctions(self.array_id, self.rest_client) self.wlp = WLPFunctions(self.array_id, self.rest_client) self.snapshot_policy = SnapshotPolicyFunctions(self.array_id, self.rest_client) self.system = SystemFunctions(self.array_id, self.rest_client) self.serviceability = ServiceabilityFunctions(self.array_id, self.rest_client) self.performance_enhanced = EnhancedPerformanceFunctions( self.array_id, self.enhanced_rest_client) self.volumes = VolumesFunctions( self.array_id, self.enhanced_rest_client) self.storage_groups = StorageGroupsFunctions( self.array_id, self.enhanced_rest_client) self.enhanced_api = EnhancedAPIFunctions( self.array_id, self.enhanced_rest_client) self.settings = SettingsFunctions(self.array_id, self.rest_client) ``` -------------------------------- ### Create a Clone Copy with PyU4V Source: https://pyu4v.readthedocs.io/en/latest/programmers_guide_src/clone.html Initializes a PyU4V connection and creates a clone copy of a storage group. The establish_terminate=True parameter removes the clone session, leaving source and target independent; default false allows differential copies. ```python 1"""docs/source/programmers_guide_src/code/clone.py.""" 2 3import PyU4V 4 5# Initialise PyU4V connection to Unisphere 6conn = PyU4V.U4VConn() 7 8# Create a Clone Copy of a Storage group, The Target Storage Group will be 9# created with all required volumes for the clone copy if it does not exist 10# already. 11# Using the Establish & Terminate Boolean value in the example removes the 12# clone session leaving the source and target devices indepenent of 13# each other, the default value of false will allow you to create 14# differential copies, updating the data on target storage group volumes. 15 16conn.clone.create_clone( 17 storage_group_id="clonesrc", target_storage_group_id="clonetgt", 18 establish_terminate=True) 19 20# Close the session 21conn.close_session() ``` -------------------------------- ### Get snapshot policy compliance Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/snapshot_policy.html Retrieves compliance attributes for a storage group. Validates input parameters and raises InvalidInputException on invalid input. ```python def get_snapshot_policy_compliance( self, storage_group_name, last_week=False, last_four_weeks=False, from_epoch=None, to_epoch=None, from_time_string=None, to_time_string=None): """Get compliance attributes on a storage group. :param storage_group_name: storage group name :param last_week: compliance in last week -- bool :param last_four_weeks: compliance in last four weeks -- bool :param from_epoch: timestamp since epoch -- str e.g 1606820929 (seconds) :param to_epoch: timestamp since epoch -- str e.g 1606820929 (seconds) :param from_time_string: human readable date -- str e.g 2020-12-01 15:00 :param to_time_string: human readable date -- str e.g 2020-12-01 15:00 :returns: resource -- dict """ if not storage_group_name: msg = 'Storage group name cannot be None.' LOG.exception(msg) raise exception.InvalidInputException(data=msg) msg, query_params = self.verify_input_params( last_week, last_four_weeks, from_epoch, to_epoch, from_time_string, to_time_string) if msg: LOG.exception(msg) raise exception.InvalidInputException(data=msg) return self.get_resource( category=REPLICATION, resource_level=SYMMETRIX, resource_level_id=self.array_id, resource_type=STORAGEGROUP, resource_type_id=storage_group_name, resource=COMPLIANCE, object_type=SNAPSHOT, params=query_params) ``` -------------------------------- ### Get LDAP configuration (deprecated) Source: https://pyu4v.readthedocs.io/en/latest/_modules/PyU4V/system.html Retrieves the current LDAP configuration for the Unisphere server. Deprecated and will be removed in version 10.4; use the new settings module API calls instead. ```python def get_ldap_configuration(self): """Get Current LDAP configuration for Unisphere Server. :returns: dict """ LOG.warning("Warning: This function is deprecated and will be removed " "in future version 10.4. Please explore API Calls in new " "settings module") return self.common.get_request( target_uri=f"/{self.common.UNI_VERSION}/system/authorization/ldap", resource_type=None) ```