MSP¶
The MSP module provides MSPBase, an extension of NewCentralBase designed for Managed Service Providers (MSPs). It handles MSP-level authentication and provides isolated, tenant-scoped API connections through a token-exchange flow — all without managing tokens manually.
Unified Credentials Only
MSP features are available starting in PyCentral SDK v2.0a19.
MSPBase exclusively uses "unified" credentials. Standalone "glp" or "new_central" entries are not supported.
Overview¶
MSPBase builds on NewCentralBase and adds:
- MSP-level GLP and New Central API calls using a single
"unified"credential - Per-tenant connections via
get_tenant_connection(), which exchanges the MSP token for a tenant-scoped token - Automatic token renewal — when a token expires (MSP or tenant), the SDK refreshes it transparently
- Tenant connection caching — repeated calls with the same
tenant_workspace_idreturn the same connection without re-running the token exchange
Authentication¶
All MSP connections use a single set of "unified" credentials. Provide a workspace_id for your MSP workspace, along with your client_id and client_secret.
To also enable New Central API calls (e.g. for monitoring or configuration), include a cluster_name or a base_url for the Central cluster your MSP account is provisioned on.
unified:
client_id: <client-id>
client_secret: <client-secret>
workspace_id: <workspace-id>
cluster_name: US-WEST-5 # Optional — include to enable New Central API calls
Basic Setup¶
MSP-Level API Calls¶
Once you have the msp_token.yaml file ready, you can make MSP-level API calls to both New Central and GLP.
from pycentral import MSPBase
with MSPBase(token_info="msp_token.yaml") as msp:
# New Central API call — list MSP tenants
response = msp.command(
api_method="GET",
api_path="network-msp/v1/list-tenants",
api_params={"limit": 100, "next": 1},
)
if response["code"] == 200:
tenants = response["msg"].get("items", [])
print(f"Total tenants: {len(tenants)}")
for tenant in tenants:
print(f'Central Tenant ID - {tenant.get("tenantId")}, Tenant Name - {tenant.get("tenantName")}')
else:
print(f"Error {response['code']}: {response['msg']}")
GLP-Only Mode
If you only need GLP API calls (e.g. listing tenants, managing subscriptions), omit cluster_name / base_url from your credentials. Only the GLP endpoint will be configured.
Tenant Connections¶
get_tenant_connection() exchanges the MSP token for a tenant-scoped token and returns a connection that behaves exactly like NewCentralBase. Use it for all tenant-scoped API calls.
The connection is cached — calling get_tenant_connection() again with the same tenant_workspace_id returns the same object without repeating the token exchange.
Tenant Workspace ID
tenant_workspace_id is the GLP workspace ID for the tenant. This is an ID from GLP for the tenant similar to the workspace_id in your MSP credentials, but scoped to the individual tenant. It can be found in the tenantId field of the GLP tenant list response. The SDK automatically strips dashes from the value (e.g. "abc-def-123" becomes "abcdef123") to match GLP API requirements. This normalization is applied consistently across get_tenant_connection() and close_tenant_connection().
Create a Tenant Connection¶
You can create a tenant connection by providing either a tenant_workspace_id or a tenant_name. When using tenant_name, the SDK resolves it to a tenant_workspace_id via a GLP API call.
from pycentral import MSPBase
with MSPBase(token_info="msp_token.yaml") as msp:
# Option 1: Connect by tenant workspace ID (from the GLP tenant list)
tenant_conn = msp.get_tenant_connection(tenant_workspace_id="<tenant-workspace-id>")
# Option 2: Connect by tenant name (resolves to workspace ID via GLP API)
# tenant_conn = msp.get_tenant_connection(tenant_name="<tenant-name>")
Tenant — New Central API Call¶
The tenant connection inherits the same Central base URL from the MSP credentials. Provide cluster_name or base_url in your MSP credentials to enable New Central calls.
from pycentral import MSPBase
with MSPBase(token_info="account_credentials_new.yaml") as msp:
tenant_conn = msp.get_tenant_connection(tenant_workspace_id="<tenant-workspace-id>")
# List APs in the tenant's Central environment
response = tenant_conn.command(
api_method="GET",
api_path="network-monitoring/v1/aps",
)
if response["code"] == 200:
aps = response["msg"].get("items", [])
print(f"Tenant APs: {len(aps)}")
for ap in aps:
print(ap.get("serialNumber"), ap.get("deviceName"), ap.get("status"))
else:
print(f"Error {response['code']}: {response['msg']}")
Tenant — GLP API Call¶
Use the tenant connection to make GLP calls scoped to that tenant's workspace.
from pycentral import MSPBase
with MSPBase(token_info="account_credentials_new.yaml") as msp:
tenant_conn = msp.get_tenant_connection(tenant_name="The Wandwright Academy")
# Specify app_name="glp" to make API call to GreenLake.
response = tenant_conn.command(
api_method="GET",
api_path="audit-log/v1/logs",
app_name="glp",
)
if response["code"] == 200:
logs = response["msg"].get("items", [])
print(f"Tenant audit logs: {len(logs)}")
for log in logs:
print(log.get("createdAt"), log.get("category"), log.get("user"))
else:
print(f"Error {response['code']}: {response['msg']}")
Token Renewal¶
Token renewal is handled automatically by the SDK. No manual intervention is required.
| Scenario | What Happens |
|---|---|
| MSP token expires during an API call | SDK refreshes the MSP token using client credentials and retries |
| Tenant token expires during an API call | SDK refreshes the MSP parent token first, then re-exchanges it for a fresh tenant-scoped token, and retries |
| MSP token expires during tenant token exchange | SDK renews the MSP token and retries the exchange (up to one retry) |
Managing Connections¶
Both MSPBase and tenant connections support Python's with statement, which ensures all HTTP clients are closed cleanly when the block exits.
from pycentral import MSPBase
with MSPBase(token_info="msp_token.yaml") as msp:
tenant_conn = msp.get_tenant_connection(tenant_workspace_id="<tenant-workspace-id>")
# Use tenant_conn for API calls...
# Optionally close a single tenant connection mid-session
msp.close_tenant_connection(tenant_workspace_id="<tenant-workspace-id>")
# msp.close() is called automatically — closes MSP and all remaining tenant connections
Manual Cleanup
If you're not using with, call msp.close() when done to release HTTP client resources for both the MSP connection and all cached tenant connections.
Next Steps¶
- Explore the Base Module Reference for
NewCentralBasedetails - See the Authentication Guide for credential setup
- Explore the full catalog of Central guides on the Developer Hub
API Reference¶
MSPBase(token_info, logger=None, log_level='INFO', enable_scope=False)
¶
Bases: NewCentralBase
NewCentralBase subclass with MSP tenant token-exchange support.
All constructor arguments are identical to NewCentralBase. MSP mode exclusively uses "unified" credentials so that both GLP and Central API calls share a single token, while per-tenant access is obtained via get_tenant_connection().
Tenant connections are cached internally: calling get_tenant_connection() with the same tenant_workspace_id multiple times always returns the same TenantBase instance without re-running the token exchange. Use close_tenant_connection() to explicitly close a single tenant connection, or close() (inherited) to close the MSP connection and all cached tenant connections.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token_info
|
dict or str
|
Token information dict or path to a YAML/JSON credentials file. See NewCentralBase for full details. |
required |
logger
|
Logger
|
External logger. Defaults to None. |
None
|
log_level
|
str
|
Log level string. Defaults to "INFO". |
'INFO'
|
enable_scope
|
bool
|
Whether to initialise scope management. Defaults to False. |
False
|
Source code in pycentral/msp/msp_base.py
get_tenant_connection(tenant_name=None, tenant_workspace_id=None) -> TenantBase
¶
Obtain a tenant-scoped connection for the given tenant.
Returns a cached TenantBase if one already exists for tenant_workspace_id. Otherwise performs the token exchange, creates a new connection, caches it, and returns it.
Token expiry is handled automatically: when a cached connection receives a 401, it renews the MSP parent token and re-exchanges it for a fresh tenant token without any additional caller action.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant_name
|
str
|
The display name of the MSP tenant. |
None
|
tenant_workspace_id
|
str
|
The tenant's GLP workspace ID,
used as the subject in the token exchange request. Find this in
the GLP tenant list response ( |
None
|
Returns:
| Type | Description |
|---|---|
TenantBase
|
A connection instance scoped to the specified tenant's Central environment. Use its command() method for all tenant-scoped API calls. The same object is returned on subsequent calls with the same tenant_workspace_id. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If neither tenant_name nor tenant_workspace_id is provided, or if Central is not configured on this MSP instance (no cluster_name or base_url under "unified", and no standalone "glp" entry). |
LoginError
|
If MSP token renewal or the token exchange fails. |
Source code in pycentral/msp/msp_base.py
close_tenant_connection(tenant_workspace_id: str) -> None
¶
Close and evict the cached connection for a specific tenant.
Releases the underlying HTTP client resources for the given tenant and removes it from the cache. The next call to get_tenant_connection() with the same tenant_workspace_id will perform a fresh token exchange.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant_workspace_id
|
str
|
The tenant's GLP workspace ID whose cached connection should be closed. |
required |
Raises:
| Type | Description |
|---|---|
KeyError
|
If no cached connection exists for tenant_workspace_id. |
Source code in pycentral/msp/msp_base.py
close() -> None
¶
Close the MSP connection and all cached tenant connections.
Closes every cached TenantBase first, then closes the MSP-level HTTP clients inherited from NewCentralBase.
Source code in pycentral/msp/msp_base.py
get_tenant_id(tenant_name)
¶
Resolve a tenant display name to its GLP workspace ID.
Queries the GLP MSP tenants API and returns the workspace ID for the given tenant name. This workspace ID is required for the token exchange performed by get_tenant_connection().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant_name
|
str
|
The display name of the tenant to resolve. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The tenant's GLP workspace ID. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the tenant_name cannot be resolved to a workspace ID. |
Source code in pycentral/msp/msp_base.py
TenantBase(token_info, msp_parent, tenant_workspace_id, logger)
¶
Bases: NewCentralBase
A tenant-scoped connection owned by MSPBase.
Not intended to be instantiated directly — obtain instances via MSPBase.get_tenant_connection().
On a 401 response, _renew_token() renews the parent MSP's shared token first, then re-exchanges it for a fresh tenant-scoped token, keeping both connections in sync.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token_info
|
dict
|
Pre-built token_info dict, already resolved — bypasses new_parse_input_args. |
required |
msp_parent
|
MSPBase
|
The parent MSP instance that owns this tenant connection. |
required |
tenant_workspace_id
|
str
|
The tenant's GLP workspace ID, used as the subject in the token exchange request. |
required |
logger
|
Logger
|
Shared logger inherited from the parent MSP instance. |
required |