Portal Example¶
This example demonstrates how to set up a hub/spoke portal between two Qumulo clusters using Cloud Data Fabric.
Overview¶
Only one portal relationship can exist between any two clusters. Within that relationship, you can add multiple root directory pairs using qumulo_portal_root.
The setup follows a two-resource flow:
- Portal relationship (
qumulo_portal) creates the spoke and accepts on the hub - Portal root mapping (
qumulo_portal_root) proposes and authorizes a root pair
Architecture¶
SPOKE CLUSTER HUB CLUSTER
============= ===========
Step 1: qumulo_portal Creates spoke + accepts hub
Step 2: qumulo_portal_root Proposes + authorizes root pair
Prerequisites¶
- Two Qumulo clusters with REST API access
- Network connectivity between clusters on the portal port (default:
3713) - Admin credentials for both clusters
Usage¶
-
Create a
terraform.tfvarsfile:hub_cluster = { endpoint = "https://hub-cluster:8000" username = "admin" password = "hub-password" address = "hub-cluster.example.com" } spoke_cluster = { endpoint = "https://spoke-cluster:8000" username = "admin" password = "spoke-password" address = "spoke-cluster.example.com" } hub_root = "/data" spoke_root = "/cache" portal_type = "PORTAL_READ_ONLY" portal_port = 3713 insecure_skip_verify = true -
Initialize and apply:
-
Verify output values:
spoke_stateandhub_stateshould beACCEPTEDspoke_statusandhub_statusshould beACTIVE(orDEGRADEDif connectivity is limited)
Variables¶
| Name | Description | Type | Default |
|---|---|---|---|
hub_cluster |
Hub cluster connection details | object |
- |
spoke_cluster |
Spoke cluster connection details | object |
- |
hub_root |
Filesystem path on the hub containing source data | string |
/data |
spoke_root |
Filesystem path on the spoke where portal data appears | string |
/cache |
portal_type |
Portal access mode (PORTAL_READ_ONLY or PORTAL_READ_WRITE) |
string |
PORTAL_READ_ONLY |
portal_port |
TCP port for portal communication | number |
3713 |
insecure_skip_verify |
Skip TLS certificate verification for self-signed certs | bool |
true |
Outputs¶
| Name | Description |
|---|---|
spoke_portal_id |
ID of the spoke portal |
spoke_cluster_uuid |
UUID of the spoke cluster |
spoke_state |
State of the spoke portal |
spoke_status |
Health status of the spoke portal |
hub_portal_id |
ID of the hub portal |
hub_state |
State of the hub portal |
hub_status |
Health status of the hub portal |
authorized_hub_root_id |
Authorized hub-side root file ID |
Multi-Root Setup¶
To add multiple root directory pairs within the same portal:
resource "qumulo_portal_root" "data" {
portal_id = qumulo_portal.main.id
spoke_root_path = "/cache/data"
hub_root_path = "/data"
}
resource "qumulo_portal_root" "exports" {
portal_id = qumulo_portal.main.id
spoke_root_path = "/cache/exports"
hub_root_path = "/exports"
}
Troubleshooting¶
Spoke shows PENDING state¶
The hub has not accepted the relationship yet. Check qumulo_portal creation diagnostics.
Root exists but data is not accessible¶
The root pair must be authorized on the hub. Ensure qumulo_portal_root applied successfully and check authorized_hub_root_id.
Connection refused errors¶
Verify network connectivity between clusters on the portal port (default: 3713, configurable via portal_port).
Full Configuration¶
# Example: Setting Up a Portal Between Qumulo Clusters
#
# This example demonstrates how to set up a hub/spoke portal relationship
# between two Qumulo clusters using the Cloud Data Fabric.
#
# Flow:
# 1. Create portal relationship (spoke create + hub accept)
# 2. Add portal root mapping (spoke propose + hub authorize)
terraform {
required_providers {
qumulo = {
source = "qumulo-terraform-registry.s3.us-east-1.amazonaws.com/qumulo/qumulo"
version = "~> 1.0"
}
}
}
variable "hub_cluster" {
description = "Hub cluster connection details"
type = object({
endpoint = string
username = string
password = string
address = string # Hostname/IP reachable from spoke
})
sensitive = true
}
variable "spoke_cluster" {
description = "Spoke cluster connection details"
type = object({
endpoint = string
username = string
password = string
address = string # Hostname/IP reachable from hub
})
sensitive = true
}
variable "hub_root" {
description = "Filesystem path on the hub cluster containing source data"
type = string
default = "/data"
validation {
condition = startswith(var.hub_root, "/")
error_message = "hub_root must be an absolute path (must start with '/')."
}
}
variable "spoke_root" {
description = "Filesystem path on the spoke cluster where portal data appears"
type = string
default = "/cache"
validation {
condition = startswith(var.spoke_root, "/")
error_message = "spoke_root must be an absolute path (must start with '/')."
}
}
variable "portal_type" {
description = "Portal access mode. Use PORTAL_READ_ONLY for cache-only access, PORTAL_READ_WRITE for two-way writes."
type = string
default = "PORTAL_READ_ONLY"
validation {
condition = contains(["PORTAL_READ_ONLY", "PORTAL_READ_WRITE"], var.portal_type)
error_message = "portal_type must be PORTAL_READ_ONLY or PORTAL_READ_WRITE."
}
}
variable "portal_port" {
description = "TCP port used for portal communication between clusters."
type = number
default = 3713
validation {
condition = var.portal_port >= 1 && var.portal_port <= 65535
error_message = "portal_port must be between 1 and 65535."
}
}
variable "insecure_skip_verify" {
description = "Set true when clusters use self-signed certificates."
type = bool
default = true
}
provider "qumulo" {
connection_profiles = [
{
name = "hub"
endpoint = var.hub_cluster.endpoint
username = var.hub_cluster.username
password = var.hub_cluster.password
insecure_skip_verify = var.insecure_skip_verify
},
{
name = "spoke"
endpoint = var.spoke_cluster.endpoint
username = var.spoke_cluster.username
password = var.spoke_cluster.password
insecure_skip_verify = var.insecure_skip_verify
}
]
}
# Step 1: Create the portal relationship.
# One qumulo_portal per hub/spoke cluster pair. Multiple qumulo_portal_root
# resources can share the same portal to replicate different directory trees.
resource "qumulo_portal" "main" {
spoke_connection_profile = "spoke"
hub_connection_profile = "hub"
type = var.portal_type
hub_hosts = [var.hub_cluster.address]
hub_port = var.portal_port
spoke_hosts = [var.spoke_cluster.address]
spoke_port = var.portal_port
}
# Step 2: Add and authorize a root directory pair.
# You can add multiple qumulo_portal_root resources on the same portal to
# replicate different directory trees between the hub and spoke clusters.
resource "qumulo_portal_root" "data" {
portal_id = qumulo_portal.main.id
spoke_root_path = var.spoke_root
hub_root_path = var.hub_root
}
# Outputs
output "spoke_portal_id" {
description = "ID of the spoke portal"
value = qumulo_portal.main.spoke_id
}
output "spoke_cluster_uuid" {
description = "UUID of the spoke cluster"
value = qumulo_portal.main.spoke_cluster_uuid
}
output "spoke_state" {
description = "State of the spoke portal"
value = qumulo_portal.main.spoke_state
}
output "spoke_status" {
description = "Health status of the spoke portal"
value = qumulo_portal.main.spoke_status
}
output "hub_portal_id" {
description = "ID of the hub portal"
value = qumulo_portal.main.hub_id
}
output "hub_state" {
description = "State of the hub portal"
value = qumulo_portal.main.hub_state
}
output "hub_status" {
description = "Health status of the hub portal"
value = qumulo_portal.main.hub_status
}
output "authorized_hub_root_id" {
description = "Hub file ID authorized for spoke access"
value = qumulo_portal_root.data.remote_root
}