User Access Management

The User Access Management page allows administrator role users to manage REST API keys and additional privileges, such as partition management, for all users. The page lists all users along with their role, any additional privilege granted, and the number of active and expired REST API keys.

IBM Storage Insights allows administrator and monitor users to perform partition migration and management. IBM Storage Insights controls the access of partition management and enhances security by using the concept of additional privileges. The administrator users can use the User Access Management page to configure the user's additional privilege by using the Partition Management option. The page allows administrator users to manage users' additional privileges and generate, revoke, and rotate/renew API keys. The page lists all the users along with the status of additional privilege, role, number of active and expired API keys, and other details.

Manage additional privileges

Only IBM Storage Insights administrators have access to the Manage additional privileges option. An admin user can assign it to themselves or to other admin and monitor users. Only admin and monitor users can use the Partition Management feature. Logged in admin and monitor users can also check the Partition Management access status of their account.

The Manage additional privileges option enables the admin to manage users' privileges and to allow or restrict users to use the Partition Management feature. The steps to allow or restrict Partition Management privileges for a user are listed as follows:
  1. Login to IBM Storage Insights by using administrator credentials.
  2. Go to Configuration > Users Access Management.
  3. Click the overflow menu with three vertical dots, and select the Manage additional privileges option from the menu for a user.
  4. On the Manage additional privileges page, select, or deselect the Partition Management check-box.
    Note: If the role of a user is reduced from administrator to monitor, the Partition Management privilege is restricted for the user. If the role of a user is upgraded from monitor to administrator, the Partition Management privilege remains unchanged.
  5. Click Save.

Manage REST APIs

The page allows access to REST APIs, facilitating the consumption of storage systems configuration and performance data for integration with customers operational dashboards. IBM Storage Insights REST APIs offer a smooth integration pathway for external applications, including integrated operation managers, by adhering to REST principles. These APIs use predictable resource-oriented URLs, accept form-encoded request bodies, generate JSON-encoded responses, and adhere to standard HTTP response codes, authentication methods, and verbs. Also, they incorporate safeguards to manage bursts of incoming requests, enhancing overall system stability. IBM Storage Insights administrators and monitoring persona can efficiently use the REST API to retrieve relevant data within their designated tenant.

The IBM Storage Insights REST APIs can be used for various integrations. Some of the use cases are:
  • Integrate storage systems health, capacity, and performance statistics with Integrated Operations dashboards
  • Integrate alerts and notifications that are generated from storage systems with Incident Management systems
  • Integrate security alerts with SOAR tools (Security Orchestration, Automation, and Response) for cyber resiliency workflow automations
The usage of IBM Storage Insights REST APIs involve three simple steps:
  1. Create an API Key.
  2. Generate a token by using an API Key.
  3. Access the REST API by using a token.
Learn about manage users' additional privileges, generate a new API key, and revoke and rotate/renew API keys:

Generating a REST API key

When you access the Users Access Management dashboard, the list of already created REST API keys is displayed.

A REST API can be generated only by the Admin. The steps for generating the REST API are as follows:
  1. Login to IBM Storage Insights using administrator credentials.
  2. Navigate to Configuration > Users Access Management.
  3. On the Users Access Management page, click the overflow menu with three vertical dots for a user you want to generate the key, and select Create REST API Key.
  4. Enter the details in the following fields:
    • Unit of time: Select the time limit until when you want to provide the API key to the user. The options include, Day(s), 1 year, and 2 year.
    • Duration/Days: Mention the number of days until when you want to provide the API key to the user if you selected the Days(s) option in Unit of time field.
  5. Click Generate API Key button.
  6. Copy the API key from the Copy button on the right corner of the API key field.
  7. Share the API key with the user through email or other suitable ways.
Note: The process to generate the API key remains the same if it is generated for the first time or if there is a list of already generated API keys.

Revoke REST API key

The Revoke key access option enables the administrator to remove access of API key from the user who already has access to it. Complete the following steps to revoke access:
  1. Login to IBM Storage Insights using administrator credentials.
  2. Navigate to Configuration > Users Access Management.
  3. Select the user from the list whose access you want to revoke.
  4. Click Revoke key access at the top of the list or click overflow menu with three vertical dots and select Revoke access on the selected user's row.
    Note: You can select multiple users at a time to revoke access.
  5. Click Revoke key access on the pop-up window.
Note: You can also revoke API key access for a user by clicking the API key count under the REST API Keys column. When you click the API key count, the REST API Keys window appears with details of all the keys that are associated with the user. Select the keys and click Revoke to revoke API keys.

Rotate/Renew REST API key

Rotate option enables the administrator to provide a new REST API key to the user if it gets lost and Renew in case the duration of the key is expired. Complete the following steps to Rotate/Renew key access:
  1. Login to IBM Storage Insights using administrator credentials.
  2. Navigate to Configuration > Users Access Management.
  3. Under the REST API Keys column, click the REST API key count for a user. The REST API Keys window appears with the details of all API keys for the user. The REST API Keys window provides the Revoke, Rotate/Renew Key, and Copy Key options.
    Note: The green icon with count specifies the number of active API keys for the user and the red icon with count specifies the expired API keys for the user.
  4. Select an API key and click Rotate/Renew Key option to rotate or renew the API key for the user.
    Note: If the user selects multiple API keys, the Rotate/Renew Key and Copy Key options get disabled.
  5. Select the Unit of time in days or years.
  6. Click Create + button to generate a new key. The existing API key is not valid and a new key is generated.
  7. Click the Copy button to copy the new key.

Generate an API Token

After generating an API key in the IBM Storage Insights GUI, you need to run the IBM Storage Insights Token API to create an API token. An API token is required for running the remaining IBM Storage Insights REST APIs. Running the Token API requires setting the generated API key in an x-api-key header. For more information about the token APIs, see Swagger documentation.

Important: The IBM Storage Insights REST API Swagger documentation is not functional, meaning that you cannot use it to run the REST APIs. The Swagger documentation is only informational. You need to use a separate REST Client application to run the IBM Storage Insights REST APIs.

After generating an API token, you can run the remaining IBM Storage Insights REST APIs. You must set the API token in an x-api-token header when executing the other REST APIs.

Important: The IBM Storage Insights Token API creates an API token that expires in 15 minutes. This expiration time is not configurable.

Building REST API string

Depending on your REST API client, you might build the REST API string yourself. A REST API string is made up of REST API path and query parameters. The query parameters are used to add additional information and filtering to API calls. You can add query parameters as key-value pairs to REST API path by using the '?' (question mark) character. Multiple parameters can be added by using the '&' (ampersand) character. A REST API string format is shown as follows:
https://<REST API PATH>?<parameter1>=<value1>&<parameter2>=<value2>&<parameter3>=<value3>&....
For example:
https://insights.ibm.com/restapi/v1/tenants/01eb8cb2-d32d-1e60-88ae-c35e5b3b9751/storage-systems/3b82a330-b90e-11eb-8322-49470b9950db/metrics?types=used_capacity&duration=7d
In the preceding example:
  • insights.ibm.com/restapi/v1/tenants/01eb8cb2-d32d-1e60-88ae-c35e5b3b9751/storage-systems/3b82a330-b90e-11eb-8322-49470b9950db/metrics is the API path
  • types=used_capacity and duration=7d are the two query parameters

In the preceding example, the REST API call filters metrics data for a particular storage system based on the specified parameters, which are type and duration. See Swagger documentation for more REST API details.

Using Python to access IBM Storage Insights REST APIs

The Python application allows to interact with the IBM Storage Insights REST API service. The CLI commands are using the IBM Storage Insights functions through the service's REST API.

Prerequisites
  • The sample application requires a Python 3.6+ runtime environment.
  • Download the ZIP file to review the sample code in Python that demonstrates the process to call the IBM Storage Insights REST APIs. The ZIP file also contains the Python installation prerequisites.
  • Install the required dependencies:
    pip install -r requirements.txt
  • Find your IBM Storage Insights Tenant ID.

    The Tenant ID of your IBM Storage Insights instance, for example, if your IBM Storage Insights URL looks like https://insights.ibm.com/gui/abcd-efgh-ijkl-mnop-xyz, then the last part of the URL, abcd-efgh-ijkl-mnop-xyz is your Tenant ID.

  • Get your IBM Storage Insights REST API Key.
  • Update the configuration file config.ini with the values obtained before running the Python scripts.
Sample Python code:
import argparse
import json
import os
from datetime import datetime
import click

from ibm_si_rest import si_rest

TOKEN_FILE = 'token.json' # store API token till expiry

def parse_arguments():
    parser = argparse.ArgumentParser(description='IBM Storage Insights REST API.')

    subparsers = parser.add_subparsers(dest='command', help='Choose a REST API to invoke')

    # list of storage systems
    parser_list_storage_systems = subparsers.add_parser('list_storage_systems', help='List storage systems')
    parser_list_storage_systems.add_argument('--system-type', help='Type of the storage system, e.g., block, filer, '
                                                                   'object')
    # list of volumes
    parser_list_storage_system_vol = subparsers.add_parser('list_storage_system_vol', help='List volumes of storage '
                                                                                           'systems')
    parser_list_storage_system_vol.add_argument('--system-uuid', help='UUID of the storage system')

    # list of alerts
    parser_list_alerts = subparsers.add_parser('list_alerts', help='List alerts for a tenant')
    parser_list_alerts.add_argument('--duration', help='Alerts requested for particular duration')
    parser_list_alerts.add_argument('--severity', help='Alerts requested for particular severity')

    # list of notifications
    parser_list_notifications = subparsers.add_parser('list_notifications', help='List notifications for a tenant')
    parser_list_notifications.add_argument('--duration', help='Notifications requested for particular duration')
    parser_list_notifications.add_argument('--severity', help='Notifications requested for particular severity')

    return parser, parser.parse_args()

def get_token(rest_api_host, api_key, tenant_id):
    """Fetch API token and save it locally till it expires"""
    try:
        if os.path.exists(TOKEN_FILE):
            with open(TOKEN_FILE, 'r') as token_file:
                stored_token = json.load(token_file)
                token_expiry = datetime.utcfromtimestamp(stored_token['result']['expiration']/1000.0)
                if datetime.utcnow() < token_expiry:
                    click.secho('Token is valid, re-using it', fg='green')
                    return stored_token['result']['token']
                else:
                    # Token expired, get a fresh token
                    click.secho('Token expired, getting a fresh token', fg='red')
                    token_response = si_rest.get_token(rest_api_host, api_key, tenant_id)
                    if token_response:
                        with open(TOKEN_FILE, 'w') as token_file_w:
                            json.dump(token_response, token_file_w)
                        return token_response['result']['token']
                    else:
                        return None
        else:
            # fetch token for the first time
            click.secho('Token not found, fetching token', fg='blue')
            token_response = si_rest.get_token(rest_api_host, api_key, tenant_id)
            if token_response:
                with open(TOKEN_FILE, 'w') as token_file:
                    json.dump(token_response, token_file)
                return token_response['result']['token']
            else:
                return None
    except Exception as ex:
        click.secho("Unexpected error: %s" % str(ex), fg="bright_red")

def process_command():
    parser, args = parse_arguments()

    if args.command == 'list_storage_systems':
        try:
            rest_api_host, api_key, tenant_id = si_rest.setup()
            token = get_token(rest_api_host, api_key, tenant_id)
            if token is None:
                raise Exception('Token creation failed')
            storage_systems = si_rest.list_storage_systems(rest_api_host, token, tenant_id, args.system_type)
            click.secho('Found (%d) storage systems' % len(storage_systems['data']), fg='green')
        except Exception as ex:
            click.secho("Unexpected error: %s" % str(ex), fg="bright_red")

    elif args.command == 'list_storage_system_vol':
        try:
            if args.system_uuid:
                rest_api_host, api_key, tenant_id = si_rest.setup()
                token = get_token(rest_api_host, api_key, tenant_id)
                if token is None:
                    raise Exception('Token creation failed')
                system_vols = si_rest.list_storage_system_volumes(rest_api_host, token, tenant_id, args.system_uuid)
                system_vols_data = []
                if system_vols and system_vols['data'] and len(system_vols['data']) > 0:
                    system_vols_data = system_vols['data']
                while system_vols and system_vols['data']:
                    if 'links' in system_vols:
                        next_link = next((link for link in system_vols['links'] if link['params']['rel'] == "next"), None)
                        if next_link:
                            click.secho('Fetching next set of volumes (%s)' % next_link['uri'], fg="green")
                            system_vols = si_rest.list_storage_system_volumes(rest_api_host, token, tenant_id, args.system_uuid,
                                                                              next_link['uri'])
                            if system_vols is not None and system_vols['data'] is not None and len(system_vols['data']) > 0:
                                system_vols_data.extend(system_vols['data'])
                        else:
                            break
                    else:
                        break

                click.secho('Found (%d) volumes' % len(system_vols_data), fg='green')
            else:
                click.secho('Please provide system uuid', fg='red')
        except Exception as ex:
            click.secho("Unexpected error: %s" % str(ex), fg="bright_red")

    elif args.command == 'list_alerts':
        try:
            rest_api_host, api_key, tenant_id = si_rest.setup()
            token = get_token(rest_api_host, api_key, tenant_id)
            if token is None:
                raise Exception('Token creation failed')
            alerts = si_rest.list_tenant_alerts(rest_api_host, token, tenant_id, args.duration, args.severity)
            alerts_data = []
            if alerts and alerts['data'] and len(alerts['data']) > 0:
                alerts_data = alerts['data']
            while alerts and alerts['data']:
                if 'links' in alerts:
                    next_link = next((link for link in alerts['links'] if link['params']['rel'] == "next"), None)
                    if next_link:
                        click.secho('Fetching next set of alerts (%s)' % next_link['uri'], fg="green")
                        alerts = si_rest.list_tenant_alerts(rest_api_host, token, tenant_id, args.duration, args.severity,
                                                        next_link['uri'])
                        if alerts is not None and alerts['data'] is not None and len(alerts['data']) > 0:
                            alerts_data.extend(alerts['data'])
                    else:
                        break
                else:
                    break

            click.secho('Found (%d) alerts' % len(alerts_data), fg='green')
        except Exception as ex:
            click.secho("Unexpected error: %s" % str(ex), fg="bright_red")

    elif args.command == 'list_notifications':
        try:
            rest_api_host, api_key, tenant_id = si_rest.setup()
            token = get_token(rest_api_host, api_key, tenant_id)
            if token is None:
                raise Exception('Token creation failed')
            notifications = si_rest.list_tenant_notifications(rest_api_host, token, tenant_id, args.duration, args.severity)
            notifications_data = []
            if notifications and notifications['data'] and len(notifications['data']) > 0:
                notifications_data = notifications['data']
            while notifications and notifications['data']:
                if 'links' in notifications:
                    next_link = next((link for link in notifications['links'] if link['params']['rel'] == "next"), None)
                    if next_link:
                        click.secho('Fetching next set of notifications (%s)' % next_link['uri'], fg="green")
                        notifications = si_rest.list_tenant_notifications(rest_api_host, token, tenant_id, args.duration,
                                                                      args.severity,
                                                                      next_link['uri'])
                        if notifications and notifications['data'] and len(notifications['data']) > 0:
                            notifications_data.extend(notifications['data'])
                    else:
                        break
                else:
                    break

            click.secho('Found (%d) notifications' % len(notifications_data), fg='green')
        except Exception as ex:
            click.secho("Unexpected error: %s" % str(ex), fg="bright_red")

    else:
        parser.print_help()

if __name__ == '__main__':
    process_command()