Week 2: Integration Testing¶
Test Matrix Week 2¶
┌────┬──────────────────────────────────────────────────────┬──────────┐
│ ID │ Test │ Pass │
├────┼──────────────────────────────────────────────────────┼──────────┤
│2.1 │ GCP→Azure cross-cloud routing │ <50ms │
│2.2 │ Branch→Azure SQL (end-to-end data path) │ Port open│
│2.3 │ Branch→GCP Vertex AI API call │ HTTP 200 │
│2.4 │ Teams voice quality (MOS via ThousandEyes) │ MOS >4.0 │
│2.5 │ O365 Exchange via ExpressRoute (not internet) │ ER path │
│2.6 │ Salesforce via best-probe path │ <100ms │
│2.7 │ Branch Guest VPN isolation │ No leak │
│2.8 │ SD-Access VN → SD-WAN VPN routing │ Routes OK│
│2.9 │ AppDynamics traces: branch→Azure SQL transaction │ Traced │
│2.10│ Splunk: all telemetry sources flowing │ All 17 │
└────┴──────────────────────────────────────────────────────┴──────────┘
TEST 2.1: GCP to Azure Cross-Cloud Routing¶
Objective: Verify that a GCP VM (Vertex AI subnet) can reach Azure resources via the hybrid path: GCP → Cloud Interconnect → Mumbai hub → ExpressRoute → Azure VNet
# Run from GCP-AI-TEST VM (10.128.0.10 in GCP asia-south1)
echo "=== TEST 2.1: GCP→AZURE CROSS-CLOUD ROUTING ==="
## Step 1: Check routing table on GCP VM
ip route show | grep "10.100"
## Expected: 10.100.0.0/16 via GCP internal gateway (next-hop toward Cloud Interconnect)
## Step 2: Traceroute from GCP to Azure app server
echo ""
echo "--- Traceroute: GCP asia-south1 → Azure app server (10.100.2.10) ---"
traceroute -n -q 1 10.100.2.10
## Expected path:
## 1. GCP internal gateway (10.128.x.1)
## 2. Cloud Interconnect hop (169.254.0.1 — Google side)
## 3. Mumbai hub (169.254.0.2 — C8500 side)
## 4. ExpressRoute (169.254.200.1 — Microsoft side)
## 5. Azure VNet gateway (10.200.x.x)
## 6. Target (10.100.2.10)
## Hops MUST include Cloud Interconnect (169.254.0.x) and ExpressRoute (169.254.200.x)
## If traffic goes via public internet: FAIL
## Step 3: Latency measurement
echo ""
echo "--- Latency: GCP → Azure (via hybrid path) ---"
ping -c 10 10.100.2.10 | tail -3
## Expected: <50ms (Mumbai → ER → Azure Central India)
## If >50ms: May be routing via internet — check hub routing
## Step 4: Verify no public internet transit
echo ""
echo "--- Confirm no internet hops ---"
traceroute -n 10.100.2.10 | grep -v "10\." | grep -v "172\." | grep -v "169\." | grep -v "*"
## Expected: No public IP hops (no 203.x.x.x, no 8.x.x.x etc.)
Pass Criteria: - Traceroute includes 169.254.0.x (Cloud Interconnect) AND 169.254.200.x (ExpressRoute) hops - Latency < 50ms GCP asia-south1 → Azure Central India - Zero public internet hops
TEST 2.2: Branch to Azure SQL End-to-End¶
#!/bin/bash
## branch_azure_sql_test.sh
echo "=== TEST 2.2: BRANCH→AZURE SQL END-TO-END ==="
## Test from multiple branch sites
BRANCHES=("blr-user-01:192.168.50.100" "del-user-01:192.168.51.100" "hyd-user-01:192.168.52.100")
SQL_PRIVATE_IP="10.100.10.10"
SQL_PORT="1433"
for BRANCH_ENTRY in "${BRANCHES[@]}"; do
BRANCH_HOST=$(echo $BRANCH_ENTRY | cut -d: -f1)
BRANCH_IP=$(echo $BRANCH_ENTRY | cut -d: -f2)
echo ""
echo "--- Testing from: $BRANCH_HOST ($BRANCH_IP) ---"
## Test 1: Port connectivity
RESULT=$(ssh $BRANCH_HOST "timeout 5 bash -c \"echo '' > /dev/tcp/$SQL_PRIVATE_IP/$SQL_PORT\" && echo 'OPEN' || echo 'CLOSED'" 2>/dev/null)
[[ "$RESULT" == "OPEN" ]] && echo " ✅ Port $SQL_PORT OPEN (private endpoint reachable)" \
|| echo " ❌ Port $SQL_PORT CLOSED"
## Test 2: DNS resolves to private IP
DNS_RESULT=$(ssh $BRANCH_HOST "nslookup abhavtech-sql.database.windows.net 10.252.1.100 2>/dev/null | grep 'Address:' | tail -1 | awk '{print \$2}'" 2>/dev/null)
[[ "$DNS_RESULT" == "10.100.10."* ]] && echo " ✅ DNS resolves to private: $DNS_RESULT" \
|| echo " ❌ DNS returned: $DNS_RESULT (expected 10.100.10.x)"
## Test 3: Traceroute (confirm via SD-WAN hub, not internet)
HOP_COUNT=$(ssh $BRANCH_HOST "traceroute -n -q 1 -w 2 10.100.10.10 2>/dev/null | grep -c '10\.'")
echo " Path: $HOP_COUNT private hops (expected: branch→hub→azure = ~4-6 hops)"
## Test 4: SQL Server connection test (if sqlcmd available)
if ssh $BRANCH_HOST "which sqlcmd" > /dev/null 2>&1; then
SQL_RESULT=$(ssh $BRANCH_HOST "sqlcmd -S 10.100.10.10 -U testuser -P 'TestPassword123!' -Q 'SELECT @@VERSION' 2>&1 | head -1")
[[ "$SQL_RESULT" == *"Microsoft SQL"* ]] && echo " ✅ SQL query successful" \
|| echo " ⚠️ SQL connection result: $SQL_RESULT"
fi
done
TEST 2.3: Branch to GCP Vertex AI API¶
#!/bin/bash
## branch_gcp_vertex_test.sh
echo "=== TEST 2.3: BRANCH→GCP VERTEX AI API ==="
## Test from Bangalore branch via hub backhaul to GCP
## Path: Branch → SD-WAN MPLS → Mumbai hub → Cloud Interconnect → GCP
echo ""
echo "--- From MUM-TEST-01 (Mumbai Hub → GCP) ---"
ssh mum-test-01 "
## Fetch a GCP access token
TOKEN=\$(curl -s 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' \
-H 'Metadata-Flavor: Google' 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin)[\"access_token\"])' 2>/dev/null)
if [[ -z \"\$TOKEN\" ]]; then
## Not on GCP — use service account key
TOKEN=\$(gcloud auth print-access-token 2>/dev/null)
fi
echo 'Testing Vertex AI API from Mumbai hub...'
START=\$(date +%s%3N)
HTTP_STATUS=\$(curl -s -o /dev/null -w '%{http_code}' \
-H \"Authorization: Bearer \$TOKEN\" \
-H 'Content-Type: application/json' \
'https://asia-south1-aiplatform.googleapis.com/v1/projects/abhavtech-ai/locations/asia-south1/models')
END=\$(date +%s%3N)
LATENCY=\$((END - START))
echo \"HTTP Status: \$HTTP_STATUS\"
echo \"Latency: \${LATENCY}ms\"
[[ \"\$HTTP_STATUS\" =~ ^[234] ]] && echo '✅ PASS: API reachable' || echo '⚠️ Check status'
[[ \$LATENCY -lt 30 ]] && echo '✅ PASS: Latency OK (<30ms via interconnect)' \
|| echo '⚠️ High latency (\${LATENCY}ms) — may be routing via internet'
"
echo ""
echo "--- Cross-region: GCP asia-south1 → Azure Central India ---"
ssh gcp-ai-test "
curl -s -o /dev/null -w 'Azure app server latency: %{time_total}s\nHTTP: %{http_code}\n' \
http://10.100.2.10:8080/health
"
TEST 2.4: Microsoft Teams Voice Quality (MOS via ThousandEyes)¶
Objective: Validate Teams MOS score ≥ 4.0 from all hub sites using ThousandEyes RTP Stream test
#!/bin/bash
## teams_mos_test.sh
echo "=== TEST 2.4: TEAMS VOICE QUALITY VIA THOUSANDEYES ==="
TE_TOKEN="<TE_BEARER_TOKEN>"
TE_BASE="https://api.thousandeyes.com/v7"
## Get test results for Teams voice tests
curl -s -X GET "$TE_BASE/test-results?testType=rtp-stream&appName=teams" \
-H "Authorization: Bearer $TE_TOKEN" | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
print('Agent | MOS | Loss% | Jitter | Latency | Status')
print('-' * 70)
for result in data.get('results', []):
agent = result.get('agentName', 'Unknown')[:20]
mos = result.get('mos', 0)
loss = result.get('loss', 0)
jitter = result.get('jitter', 0)
latency = result.get('latency', 0)
if mos >= 4.0 and loss <= 0.5 and jitter <= 10:
status = '✅ PASS'
elif mos >= 3.5:
status = '⚠️ WARN'
else:
status = '❌ FAIL'
print(f'{agent:20} | {mos:5.2f} | {loss:5.1f}% | {jitter:4.0f}ms | {latency:5.0f}ms | {status}')
"
echo ""
echo "Pass Criteria: MOS ≥ 4.0, Loss ≤ 0.5%, Jitter ≤ 10ms from all 6 hub agents"
Manual Verification — Check DSCP marking on ExpressRoute:
! On MUM-HUB-01 — confirm Teams marked EF and using ExpressRoute
show sdwan app-fwd cflowd flows | grep -i teams | head -5
! Expected: ms-teams-media flows with dscp=46 (EF), color=azure-expressroute
! Verify QoS EF class is counting packets
show policy-map interface TenGigabitEthernet0/0/2.4001 class QOS-EF
! Expected: Packet count incrementing, no drops
TEST 2.5: O365 Exchange Path Verification (ExpressRoute, Not Internet)¶
#!/bin/bash
## o365_path_test.sh
echo "=== TEST 2.5: O365 EXCHANGE VIA EXPRESSROUTE ==="
for HUB in mum-hub-01 lon-hub-01 nj-hub-01; do
echo ""
echo "--- $HUB ---"
## Check routing for Exchange Online prefix
ROUTE=$(ssh admin@$HUB "show ip route 40.107.0.0" 2>/dev/null)
if echo "$ROUTE" | grep -q "169.254.20"; then
echo " ✅ Exchange (40.107.0.0/17): Routes via ExpressRoute peer"
else
echo " ❌ Exchange: NOT via ExpressRoute"
echo " Route: $ROUTE"
fi
## Verify not using internet DIA for O365
DIA_FLOW=$(ssh admin@$HUB "show sdwan app-fwd cflowd flows | grep office365-exchange | grep dia" 2>/dev/null | wc -l)
ER_FLOW=$(ssh admin@$HUB "show sdwan app-fwd cflowd flows | grep office365-exchange | grep expressroute" 2>/dev/null | wc -l)
echo " Exchange flows: ExpressRoute=$ER_FLOW, DIA=$DIA_FLOW"
[[ $ER_FLOW -gt 0 && $DIA_FLOW -eq 0 ]] && echo " ✅ All Exchange traffic via ExpressRoute" \
|| [[ $ER_FLOW -gt 0 ]] && echo " ⚠️ Some Exchange via DIA (check AAR policy)" \
|| echo " ⚠️ No active Exchange flows (test during business hours)"
done
## ThousandEyes: Compare O365 latency across paths
echo ""
echo "--- ThousandEyes O365 Latency (2-day average) ---"
curl -s "$TE_BASE/test-results?testType=http-server&url=outlook.office365.com" \
-H "Authorization: Bearer $TE_TOKEN" | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
for r in data.get('results', []):
lat = r.get('responseTime', 0)
agent = r.get('agentName', '?')[:20]
status = '✅' if lat < 20 else ('⚠️' if lat < 50 else '❌')
print(f'{status} {agent:20} {lat:5}ms (threshold: <20ms via ExpressRoute)')
"
TEST 2.6–2.7: SaaS Probe and Guest Isolation¶
## TEST 2.6: Salesforce Cloud OnRamp Best-Path Selection
echo "=== TEST 2.6: SALESFORCE CLOUD ONRAMP ==="
## Get SaaS probe results from vManage
curl -k -s -b /tmp/cookies.txt \
"https://vmanage.abhavtech.com/dataservice/cloudonramp/saas/gateway/apps" | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
for entry in data.get('data', []):
if 'salesforce' in entry.get('appName','').lower():
print(f'Site: {entry.get(\"deviceName\",\"?\")} | Latency: {entry.get(\"latency\",0)}ms | Path: {entry.get(\"bestPath\",\"?\")}')
"
## Expected: All sites <100ms latency to Salesforce via best-probe path
echo ""
echo "=== TEST 2.7: GUEST VPN ISOLATION ==="
## Run from a simulated guest host on VPN 3
## Test 1: Guest cannot reach corporate
CORP_RESULT=$(ssh admin@mum-hub-01 "ping vrf 3 10.252.1.100 repeat 3 timeout 1 | tail -1" 2>/dev/null)
echo "$CORP_RESULT" | grep -q "0 success" && echo " ✅ Corporate unreachable from Guest VPN 3" \
|| echo " ❌ CRITICAL: Corporate reachable from Guest VPN!"
## Test 2: Guest cannot reach Azure VNet
AZURE_RESULT=$(ssh admin@mum-hub-01 "ping vrf 3 10.100.10.10 repeat 3 timeout 1 | tail -1" 2>/dev/null)
echo "$AZURE_RESULT" | grep -q "0 success" && echo " ✅ Azure VNet unreachable from Guest VPN 3" \
|| echo " ❌ CRITICAL: Azure VNet reachable from Guest VPN!"
## Test 3: Guest CAN reach internet (via DIA)
INET_RESULT=$(ssh admin@mum-hub-01 "ping vrf 3 8.8.8.8 repeat 3 | tail -1" 2>/dev/null)
echo "$INET_RESULT" | grep -qv "0 success" && echo " ✅ Internet reachable from Guest VPN 3" \
|| echo " ❌ Internet not reachable from Guest (DIA issue)"
TEST 2.8–2.10: SD-Access Handoff, AppDynamics, Splunk¶
## TEST 2.8: SD-Access VN → SD-WAN VPN Routing
echo "=== TEST 2.8: SD-ACCESS HANDOFF VERIFICATION ==="
## On MUM-HUB-01: BGP from SD-Access border
ssh admin@mum-hub-01 "show bgp vpnv4 unicast vrf 1 summary | grep 65001"
## Expected: 10.240.1.1 in ESTABLISHED state with active prefix count
ssh admin@mum-hub-01 "show bgp vpnv4 unicast vrf 1 | include 10.100"
## Expected: All Mumbai SD-Access subnets (10.100.1.0/24 etc.) present in VRF 1
## From London hub, can we reach Mumbai corporate LAN via OMP?
ssh admin@lon-hub-01 "ping vrf 1 10.100.1.50 repeat 5" 2>/dev/null | tail -2
## Expected: 5/5 success (London hub → SD-WAN fabric → Mumbai hub VPN 1 → SD-Access fabric)
## TEST 2.9: AppDynamics Transaction Tracing
echo ""
echo "=== TEST 2.9: APPDYNAMICS TRANSACTION TRACE ==="
## Trigger a test transaction: Bangalore branch user → Azure SQL
## This should appear as a traced business transaction in AppDynamics
## Check AppDynamics API for recent transactions
curl -s "https://abhavtech.saas.appdynamics.com/controller/rest/applications/MultiCloud-App/events" \
-u "admin@abhavtech:<PASSWORD>" \
-G --data-urlencode "time-range-type=BEFORE_NOW" \
--data-urlencode "duration-in-mins=30" \
-H "Accept: application/json" | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
events = data if isinstance(data, list) else data.get('events', [])
print(f'Events in last 30 min: {len(events)}')
for e in events[:5]:
print(f' {e.get(\"eventTime\",\"?\")} | {e.get(\"type\",\"?\")} | {e.get(\"message\",\"?\")[:60]}')
"
## TEST 2.10: Splunk Telemetry Sources
echo ""
echo "=== TEST 2.10: SPLUNK TELEMETRY COVERAGE ==="
SPLUNK_HOST="splunk.abhavtech.com"
SPLUNK_TOKEN="<SPLUNK_HEC_TOKEN>"
SOURCES=(
"index=dnac sourcetype=dnac:assurance" "DNAC Assurance"
"index=sdwan sourcetype=vmanage" "vManage SD-WAN"
"index=thousandeyes sourcetype=thousandeyes:test:result" "ThousandEyes"
"index=appdynamics sourcetype=appdynamics:apm" "AppDynamics"
"index=ise sourcetype=cisco:ise:syslog" "ISE"
"index=umbrella sourcetype=cisco:umbrella:dns" "Umbrella DNS"
)
i=0
while [[ $i -lt ${#SOURCES[@]} ]]; do
SEARCH="${SOURCES[$i]}"
LABEL="${SOURCES[$((i+1))]}"
COUNT=$(curl -s -k -u "admin:<PASSWORD>" \
"https://$SPLUNK_HOST:8089/services/search/jobs/oneshot" \
--data-urlencode "search=$SEARCH earliest=-15m | stats count" \
-d "output_mode=json" | \
python3 -c "import json,sys; d=json.load(sys.stdin); print(d['results'][0]['count'] if d.get('results') else '0')" 2>/dev/null)
[[ "$COUNT" -gt 0 ]] && echo " ✅ $LABEL: $COUNT events (last 15 min)" \
|| echo " ❌ $LABEL: No events — check source integration"
i=$((i+2))
done