Skip to main content

Registration Port Check

PE ↔ PC Ports

Source: Nutanix "Port Details List" export (sections Prism Central + AOS), as of 2026-09-28

Port (TCP)DirectionLevelPurpose
9440bothrequiredPrism / API – registration and all communication
9300, 9301bothrequiredMercury Fanout gRPC – preferred PE↔PC channel
ICMPbothrequiredPC pings PE VIP / CVM IPs
123 UDPPC → PE VIPrequiredNTP
2106PC → PE (bi)opsLCM
3205, 3260PC → PEopsiSCSI data/control path for PCVM disks
9361, 9362, 9463PE → PC (bi)opsIkat proxy L4/L7/reverse (pc.2024.3+)
2060PE → PC (bi)opsAtlas – network provisioning
2123 (2120 fallback)PE → PCopsMetropolis – VM recovery points
2100, 22PE → PCspecialGenesis / SSH – PC deployment only
9441PC → PEspecialCAC client authentication only
2200PC → PE (bi)specialGenesis Gateway – cluster creation from PC

Port Check Script

Checks ping, all ports above for the given direction, Prism API response and clock skew. Plain bash (/dev/tcp) — no nc/nmap needed. Run on both sides.

bash pc_port_check.sh pe <PC_IP>                                  # on a PE CVM
bash pc_port_check.sh pc <CVM1> <CVM2> <CVM3> <CLUSTER_VIP>     # on the PCVM

required = red if closed, ops = yellow, special = grey. If all ports are open and registration still fails: check DNS/NTP on the PCVM and ncli multicluster get-cluster-state on the CVM (still registered elsewhere?).

Script: pc_port_check.sh

#!/usr/bin/env bash
# pc_port_check.sh – checks whether Prism Element (PE) and Prism Central (PC) can reach each other.
# Port list: Nutanix "Port Details List" (sections "Prism Central" and "AOS"), as of 2026-09-28.
#
# Usage:
#   On a PE CVM:  bash pc_port_check.sh pe <PC_IP>
#   On the PCVM:  bash pc_port_check.sh pc <CVM_IP> [<CVM_IP> ... <CLUSTER_VIP>]
#
# Test both directions – many ports must be open both ways.

TIMEOUT=3   # seconds per test

# Format: "port|level|service – purpose"
#   level: required = registration/communication fails without it
#          ops      = features after registration (LCM, backup, networking, ...)
#          special  = deployment/special cases only, failure usually irrelevant
PORTS_PE_TO_PC=(
  "9440|required|Prism/API – registration and all communication"
  "9300|required|Mercury Fanout (gRPC) – preferred channel PE<->PC"
  "9301|required|Mercury Fanout (gRPC) – preferred channel PE<->PC"
  "9361|ops|Ikat-Proxy L4 (pc.2024.3+)"
  "9362|ops|Ikat-Proxy L7 (pc.2024.3+)"
  "9463|ops|Ikat-Proxy Reverse (pc.2024.3+)"
  "2060|ops|Atlas – network provisioning"
  "2123|ops|Metropolis – VM recovery points (mTLS)"
  "2120|special|Metropolis – fallback if 2123 unavailable"
  "2100|special|Genesis – PC deployment only"
  "22|special|SSH – PC deployment only (password reset)"
)
PORTS_PC_TO_PE=(
  "9440|required|Prism/API – registration and all communication"
  "9300|required|Mercury Fanout (gRPC) – preferred channel PE<->PC"
  "9301|required|Mercury Fanout (gRPC) – preferred channel PE<->PC"
  "2106|ops|LCM – upgrades via Prism Central"
  "3205|ops|iSCSI data path (PCVM disks on cluster)"
  "3260|ops|iSCSI control path (PCVM disks on cluster)"
  "9441|special|CAC client authentication only"
  "2200|special|Genesis Gateway – cluster creation from PC only"
)
# Not testable via TCP: UDP 123 (NTP PC -> PE VIP) – covered by the clock comparison below.

GREEN='\033[32m'; RED='\033[31m'; YELLOW='\033[33m'; GREY='\033[90m'; NC='\033[0m'
FAILED=0; WARN=0

DIRECTION=$1; shift 2>/dev/null
case "$DIRECTION" in
  pe) PORTS=("${PORTS_PE_TO_PC[@]}"); LABEL="PE -> PC" ;;
  pc) PORTS=("${PORTS_PC_TO_PE[@]}"); LABEL="PC -> PE" ;;
  *)  DIRECTION="" ;;
esac
if [ -z "$DIRECTION" ] || [ $# -lt 1 ]; then
  echo "Usage:"
  echo "  on a PE CVM:  bash $0 pe <PC_IP>"
  echo "  on the PCVM:  bash $0 pc <CVM_IP> [<CVM_IP> ... <CLUSTER_VIP>]"
  exit 1
fi

# Tests a TCP port with plain bash – works without nc/nmap
check_port() {
  local host=$1 port=$2 level=$3 info=$4
  if timeout "$TIMEOUT" bash -c "echo > /dev/tcp/$host/$port" 2>/dev/null; then
    printf "  ${GREEN}%-9s${NC} TCP %-5s %-9s %s\n" "OPEN" "$port" "$level" "$info"
  else
    case "$level" in
      required)  printf "  ${RED}%-9s${NC} TCP %-5s %-9s %s  <-- check firewall!\n" "CLOSED" "$port" "$level" "$info"; FAILED=1 ;;
      ops)  printf "  ${YELLOW}%-9s${NC} TCP %-5s %-9s %s\n" "CLOSED" "$port" "$level" "$info"; WARN=1 ;;
      *)        printf "  ${GREY}%-9s${NC} TCP %-5s %-9s %s\n" "CLOSED" "$port" "$level" "$info" ;;
    esac
  fi
}

for TARGET in "$@"; do
  echo "=========================================================================="
  echo " $LABEL   Target: $TARGET   (from $(hostname))"
  echo "=========================================================================="

  # 1) Ping – required per Nutanix: PC pings the PE addresses
  if ping -c 2 -W 2 "$TARGET" >/dev/null 2>&1; then
    printf "  ${GREEN}%-9s${NC} ICMP  %-9s %s\n" "OK" "required" "Ping"
  else
    printf "  ${RED}%-9s${NC} ICMP  %-9s %s\n" "NONE" "required" "Ping – PC must be able to ping PE (VIP/CVMs)"; FAILED=1
  fi

  # 2) Ports
  for entry in "${PORTS[@]}"; do
    IFS='|' read -r port level info <<< "$entry"
    check_port "$TARGET" "$port" "$level" "$info"
  done

  # 3) Is it really Prism on 9440? (HTTP 401 = login required = good)
  CODE=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 5 -X POST -H 'Content-Type: application/json' -d '{}' \
         "https://$TARGET:9440/api/nutanix/v3/clusters/list")
  case "$CODE" in
    401|200) printf "  ${GREEN}%-9s${NC} %s\n" "OK" "Prism API responds (HTTP $CODE)" ;;
    000)     printf "  ${RED}%-9s${NC} %s\n" "FAILED" "Prism API no response (timeout/TLS)"; FAILED=1 ;;
    *)       printf "  ${YELLOW}%-9s${NC} %s\n" "NOTE" "Prism API responds with HTTP $CODE" ;;
  esac

  # 4) Clock comparison – skew of more than a few minutes makes registration fail
  REMOTE_DATE=$(curl -sk -I --max-time 5 "https://$TARGET:9440/" | grep -i '^date:' | cut -d' ' -f2- | tr -d '\r')
  if [ -n "$REMOTE_DATE" ]; then
    DIFF=$(( $(date -u +%s) - $(date -u -d "$REMOTE_DATE" +%s) )); DIFF=${DIFF#-}
    if [ "$DIFF" -le 60 ]; then
      printf "  ${GREEN}%-9s${NC} %s\n" "OK" "time skew ${DIFF}s"
    else
      printf "  ${RED}%-9s${NC} %s\n" "FAILED" "time skew ${DIFF}s – check NTP on both sides!"; FAILED=1
    fi
  else
    printf "  ${YELLOW}%-9s${NC} %s\n" "?" "could not compare clocks"
  fi
  echo
done

echo "Legend: required = red if closed | ops = yellow if closed | special = grey, usually irrelevant"
if [ $FAILED -eq 1 ]; then
  echo -e "${RED}At least one required check failed${NC} – see red lines above."
elif [ $WARN -eq 1 ]; then
  echo -e "${YELLOW}Registration should work,${NC} but some features (yellow) will not."
else
  echo -e "${GREEN}All required checks passed.${NC} Now test the opposite direction, if not done yet."
fi
exit $FAILED