# Registration Port Check

#### PE ↔ PC Ports

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

<table id="bkmrk-port-%28tcp%29directionl"><thead><tr><td>**Port (TCP)**</td><td>**Direction**</td><td>**Level**</td><td>**Purpose**</td></tr></thead><tbody><tr><td>9440</td><td>both</td><td>required</td><td>Prism / API – registration and all communication</td></tr><tr><td>9300, 9301</td><td>both</td><td>required</td><td>Mercury Fanout gRPC – preferred PE↔PC channel</td></tr><tr><td>ICMP</td><td>both</td><td>required</td><td>PC pings PE VIP / CVM IPs</td></tr><tr><td>123 UDP</td><td>PC → PE VIP</td><td>required</td><td>NTP</td></tr><tr><td>2106</td><td>PC → PE (bi)</td><td>ops</td><td>LCM</td></tr><tr><td>3205, 3260</td><td>PC → PE</td><td>ops</td><td>iSCSI data/control path for PCVM disks</td></tr><tr><td>9361, 9362, 9463</td><td>PE → PC (bi)</td><td>ops</td><td>Ikat proxy L4/L7/reverse (pc.2024.3+)</td></tr><tr><td>2060</td><td>PE → PC (bi)</td><td>ops</td><td>Atlas – network provisioning</td></tr><tr><td>2123 (2120 fallback)</td><td>PE → PC</td><td>ops</td><td>Metropolis – VM recovery points</td></tr><tr><td>2100, 22</td><td>PE → PC</td><td>special</td><td>Genesis / SSH – PC deployment only</td></tr><tr><td>9441</td><td>PC → PE</td><td>special</td><td>CAC client authentication only</td></tr><tr><td>2200</td><td>PC → PE (bi)</td><td>special</td><td>Genesis Gateway – cluster creation from PC</td></tr></tbody></table>

#### 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
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

```bash
#!/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

```