|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# Test script for Resource Availability Calendar feature |
| 4 | +# |
| 5 | +# Usage: |
| 6 | +# ./test_calendar.sh --reports-url https://beta-7.fabric-testbed.net:8443/reports \ |
| 7 | +# --orchestrator-url https://beta-7.fabric-testbed.net \ |
| 8 | +# --token <reports_bearer_token> \ |
| 9 | +# [--fabric-token <fabric_id_token>] |
| 10 | +# |
| 11 | +# The --token is the static bearer token from reports config (bearer_tokens list). |
| 12 | +# The --fabric-token is optional; needed only to test the orchestrator proxy endpoint. |
| 13 | + |
| 14 | +set -euo pipefail |
| 15 | + |
| 16 | +RED='\033[0;31m' |
| 17 | +GREEN='\033[0;32m' |
| 18 | +YELLOW='\033[1;33m' |
| 19 | +NC='\033[0m' |
| 20 | + |
| 21 | +REPORTS_URL="https://alpha-5.fabric-testbed.net/reports" |
| 22 | +ORCH_URL="https://beta-7.fabric-testbed.net/" |
| 23 | +TOKEN="eyJhbGciOiJSUzI1NiIsImtpZCI6InJ0M1FQeGVwRTk4QjVKdUEyMFIyMFdiblVLRlFDbTkzNW1MZTJMU2syVjAiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJodHRwOi8vY2lsb2dvbi5vcmcvc2VydmVyQS91c2Vycy8xMTkwNDEwMSIsImlzcyI6Imh0dHBzOi8vY2lsb2dvbi5vcmciLCJnaXZlbl9uYW1lIjoiS29tYWwiLCJhdWQiOiJjaWxvZ29uOi9jbGllbnRfaWQvNGQ3ZWE1ODNkZDA2MTljMjkwZWRhN2FiNjZmMmNlZmEiLCJhY3IiOiJodHRwczovL3JlZmVkcy5vcmcvcHJvZmlsZS9tZmEiLCJhenAiOiJjaWxvZ29uOi9jbGllbnRfaWQvNGQ3ZWE1ODNkZDA2MTljMjkwZWRhN2FiNjZmMmNlZmEiLCJhdXRoX3RpbWUiOjE3NzQyNzYxNDMsIm5hbWUiOiJLb21hbCBUaGFyZWphIiwiZXhwIjoxNzc0Mjk3NTA1LCJpYXQiOjE3NzQyODMxMDUsImZhbWlseV9uYW1lIjoiVGhhcmVqYSIsImp0aSI6Imh0dHBzOi8vY2lsb2dvbi5vcmcvb2F1dGgyL2lkVG9rZW4vNWYwZGNlMzU1YTNkYjY3NmZiYzcwZjU1NjY0MDNlZi8xNzc0MjgzMDk5MTQxIiwiZW1haWwiOiJrdGhhcmUxMEBlbWFpbC51bmMuZWR1IiwicHJvamVjdHMiOlt7Im5hbWUiOiJGQUJSSUMgU3RhZmYiLCJ1dWlkIjoiYjk4NDdmYTEtMTNlZi00OWY5LTllMDctYWU2YWQwNmNkYTNmIiwidGFncyI6WyJOZXQuRmFjaWxpdHlQb3J0LlJFTkMtQ2hhbWVsZW9uIiwiQ29tcG9uZW50LlN0b3JhZ2UiLCJOZXQuQWxsRmFjaWxpdHlQb3J0cyIsIk5ldC5GQUJOZXR2NEV4dCIsIk5ldC5GQUJOZXR2NkV4dCIsIk5ldC5GYWNpbGl0eVBvcnQuUkVOQy1HU1UiLCJOZXQuTm9MaW1pdEJXIiwiU2xpY2UuTWVhc3VyZW1lbnRzIiwiU2xpY2UuTXVsdGlzaXRlIiwiU2xpY2UuTm9MaW1pdExpZmV0aW1lIiwiVk0uTm9MaW1pdCIsIlZNLk5vTGltaXRDUFUiLCJWTS5Ob0xpbWl0RGlzayIsIlZNLk5vTGltaXRSQU0iLCJOZXQuUG9ydE1pcnJvcmluZyIsIlN3aXRjaC5QNCIsIkNvbXBvbmVudC5GUEdBX1hpbGlueF9TTjEwMjIiLCJDb21wb25lbnQuRlBHQV9YaWxpbnhfVTI4MCIsIkNvbXBvbmVudC5HUFVfQTMwIiwiQ29tcG9uZW50LkdQVV9BNDAiLCJDb21wb25lbnQuR1BVX1JUWDYwMDAiLCJDb21wb25lbnQuR1BVX1Rlc2xhX1Q0IiwiQ29tcG9uZW50Lk5WTUVfUDQ1MTAiLCJDb21wb25lbnQuU21hcnROSUNfQmx1ZUZpZWxkXzNfQ29ubmVjdFhfNiIsIkNvbXBvbmVudC5TbWFydE5JQ19Db25uZWN0WF81IiwiQ29tcG9uZW50LlNtYXJ0TklDX0Nvbm5lY3RYXzYiXSwibWVtYmVyc2hpcHMiOnsiaXNfY3JlYXRvciI6ZmFsc2UsImlzX2xlYWQiOnRydWUsImlzX21lbWJlciI6dHJ1ZSwiaXNfb3duZXIiOnRydWUsImlzX3Rva2VuX2hvbGRlciI6dHJ1ZX19XSwicm9sZXMiOlt7Im5hbWUiOiJmYWJyaWMtYWN0aXZlLXVzZXJzIn0seyJuYW1lIjoiZmFjaWxpdHktb3BlcmF0b3JzIn0seyJuYW1lIjoiSnVweXRlcmh1YiJ9LHsibmFtZSI6InByb2plY3QtbGVhZHMifV0sInNjb3BlIjoiYWxsIiwidXVpZCI6ImRkYjVmMzc0LTdlODItNDVhZi04MmQwLTMzMGQwYjk2MWVhYSJ9.X7HkD8NpUesYfsOeoLPDDehCdRcXwsuYJpyQ1RBlV3XpERw5rfUEDgGThu1cLKhK2JrMlSelrSsDBNmyoYl7hAPx6PPe4NVNB5876KE4eBuR7QB53mwDWrO70h_Fje-dESOBQON3PzWNhCv40sKi0CsCcTVVpcOumFkMhHIyGWkyyryFsO0s_yf8pWtueA_RQJtssUC08CW421M1uDKoIAAA4Dx8DHnnVqhCO3tj_p5blOviLKOReLuShjXH7V4vFEF2HsuAzmCJC80KhWKElMab-TrMywZ2v6c6T9IWQLFbnOEhSo1O6lIVLxDWAejWNK4v9vTyC-rxjfhzVwar7b_7hKg6I8ECNlq5IzgCjsKtI0OK9eKUnvyd0grVS07uSQKOj_Y64k3V4l7nYupxAIZSd86RwNtwRaQqcll2FQwnAFqNMi3FCEECu1mttnJ35EoyydioeGOpna0q_I6cGhPOnt-X58HIn7B2I6M9_yAT6_ubgwEqPo94yZZ7eWnxtp5SpKkLCUXpBFpPDgfFiQo4AF1uyXZxyunePwQ2QKcpFBcNJ7elMNNnfoO-8pwRGtACv_qtSXQb264_FRBYX553UBFKpn_6S0JUfTd8Z14ZWxHo6RK4-RJ1OJ9625GaNUmln1nMXnmM7BKtvL2E5DCRtQrPB4mfqMVvZaALLoM" |
| 24 | +FABRIC_TOKEN="eyJhbGciOiJSUzI1NiIsImtpZCI6InJ0M1FQeGVwRTk4QjVKdUEyMFIyMFdiblVLRlFDbTkzNW1MZTJMU2syVjAiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJodHRwOi8vY2lsb2dvbi5vcmcvc2VydmVyQS91c2Vycy8xMTkwNDEwMSIsImlzcyI6Imh0dHBzOi8vY2lsb2dvbi5vcmciLCJnaXZlbl9uYW1lIjoiS29tYWwiLCJhdWQiOiJjaWxvZ29uOi9jbGllbnRfaWQvNGQ3ZWE1ODNkZDA2MTljMjkwZWRhN2FiNjZmMmNlZmEiLCJhY3IiOiJodHRwczovL3JlZmVkcy5vcmcvcHJvZmlsZS9tZmEiLCJhenAiOiJjaWxvZ29uOi9jbGllbnRfaWQvNGQ3ZWE1ODNkZDA2MTljMjkwZWRhN2FiNjZmMmNlZmEiLCJhdXRoX3RpbWUiOjE3NzQyNzYxNDMsIm5hbWUiOiJLb21hbCBUaGFyZWphIiwiZXhwIjoxNzc0Mjk3NTA1LCJpYXQiOjE3NzQyODMxMDUsImZhbWlseV9uYW1lIjoiVGhhcmVqYSIsImp0aSI6Imh0dHBzOi8vY2lsb2dvbi5vcmcvb2F1dGgyL2lkVG9rZW4vNWYwZGNlMzU1YTNkYjY3NmZiYzcwZjU1NjY0MDNlZi8xNzc0MjgzMDk5MTQxIiwiZW1haWwiOiJrdGhhcmUxMEBlbWFpbC51bmMuZWR1IiwicHJvamVjdHMiOlt7Im5hbWUiOiJGQUJSSUMgU3RhZmYiLCJ1dWlkIjoiYjk4NDdmYTEtMTNlZi00OWY5LTllMDctYWU2YWQwNmNkYTNmIiwidGFncyI6WyJOZXQuRmFjaWxpdHlQb3J0LlJFTkMtQ2hhbWVsZW9uIiwiQ29tcG9uZW50LlN0b3JhZ2UiLCJOZXQuQWxsRmFjaWxpdHlQb3J0cyIsIk5ldC5GQUJOZXR2NEV4dCIsIk5ldC5GQUJOZXR2NkV4dCIsIk5ldC5GYWNpbGl0eVBvcnQuUkVOQy1HU1UiLCJOZXQuTm9MaW1pdEJXIiwiU2xpY2UuTWVhc3VyZW1lbnRzIiwiU2xpY2UuTXVsdGlzaXRlIiwiU2xpY2UuTm9MaW1pdExpZmV0aW1lIiwiVk0uTm9MaW1pdCIsIlZNLk5vTGltaXRDUFUiLCJWTS5Ob0xpbWl0RGlzayIsIlZNLk5vTGltaXRSQU0iLCJOZXQuUG9ydE1pcnJvcmluZyIsIlN3aXRjaC5QNCIsIkNvbXBvbmVudC5GUEdBX1hpbGlueF9TTjEwMjIiLCJDb21wb25lbnQuRlBHQV9YaWxpbnhfVTI4MCIsIkNvbXBvbmVudC5HUFVfQTMwIiwiQ29tcG9uZW50LkdQVV9BNDAiLCJDb21wb25lbnQuR1BVX1JUWDYwMDAiLCJDb21wb25lbnQuR1BVX1Rlc2xhX1Q0IiwiQ29tcG9uZW50Lk5WTUVfUDQ1MTAiLCJDb21wb25lbnQuU21hcnROSUNfQmx1ZUZpZWxkXzNfQ29ubmVjdFhfNiIsIkNvbXBvbmVudC5TbWFydE5JQ19Db25uZWN0WF81IiwiQ29tcG9uZW50LlNtYXJ0TklDX0Nvbm5lY3RYXzYiXSwibWVtYmVyc2hpcHMiOnsiaXNfY3JlYXRvciI6ZmFsc2UsImlzX2xlYWQiOnRydWUsImlzX21lbWJlciI6dHJ1ZSwiaXNfb3duZXIiOnRydWUsImlzX3Rva2VuX2hvbGRlciI6dHJ1ZX19XSwicm9sZXMiOlt7Im5hbWUiOiJmYWJyaWMtYWN0aXZlLXVzZXJzIn0seyJuYW1lIjoiZmFjaWxpdHktb3BlcmF0b3JzIn0seyJuYW1lIjoiSnVweXRlcmh1YiJ9LHsibmFtZSI6InByb2plY3QtbGVhZHMifV0sInNjb3BlIjoiYWxsIiwidXVpZCI6ImRkYjVmMzc0LTdlODItNDVhZi04MmQwLTMzMGQwYjk2MWVhYSJ9.X7HkD8NpUesYfsOeoLPDDehCdRcXwsuYJpyQ1RBlV3XpERw5rfUEDgGThu1cLKhK2JrMlSelrSsDBNmyoYl7hAPx6PPe4NVNB5876KE4eBuR7QB53mwDWrO70h_Fje-dESOBQON3PzWNhCv40sKi0CsCcTVVpcOumFkMhHIyGWkyyryFsO0s_yf8pWtueA_RQJtssUC08CW421M1uDKoIAAA4Dx8DHnnVqhCO3tj_p5blOviLKOReLuShjXH7V4vFEF2HsuAzmCJC80KhWKElMab-TrMywZ2v6c6T9IWQLFbnOEhSo1O6lIVLxDWAejWNK4v9vTyC-rxjfhzVwar7b_7hKg6I8ECNlq5IzgCjsKtI0OK9eKUnvyd0grVS07uSQKOj_Y64k3V4l7nYupxAIZSd86RwNtwRaQqcll2FQwnAFqNMi3FCEECu1mttnJ35EoyydioeGOpna0q_I6cGhPOnt-X58HIn7B2I6M9_yAT6_ubgwEqPo94yZZ7eWnxtp5SpKkLCUXpBFpPDgfFiQo4AF1uyXZxyunePwQ2QKcpFBcNJ7elMNNnfoO-8pwRGtACv_qtSXQb264_FRBYX553UBFKpn_6S0JUfTd8Z14ZWxHo6RK4-RJ1OJ9625GaNUmln1nMXnmM7BKtvL2E5DCRtQrPB4mfqMVvZaALLoM" |
| 25 | + |
| 26 | +usage() { |
| 27 | + echo "Usage: $0 --reports-url <url> --orchestrator-url <url> --token <token> [--fabric-token <token>]" |
| 28 | + exit 1 |
| 29 | +} |
| 30 | + |
| 31 | +while [[ $# -gt 0 ]]; do |
| 32 | + case $1 in |
| 33 | + --reports-url) REPORTS_URL="$2"; shift 2 ;; |
| 34 | + --orchestrator-url) ORCH_URL="$2"; shift 2 ;; |
| 35 | + --token) TOKEN="$2"; shift 2 ;; |
| 36 | + --fabric-token) FABRIC_TOKEN="$2"; shift 2 ;; |
| 37 | + -h|--help) usage ;; |
| 38 | + *) echo "Unknown option: $1"; usage ;; |
| 39 | + esac |
| 40 | +done |
| 41 | + |
| 42 | +if [[ -z "$REPORTS_URL" || -z "$ORCH_URL" || -z "$TOKEN" ]]; then |
| 43 | + usage |
| 44 | +fi |
| 45 | + |
| 46 | +REPORTS_URL="${REPORTS_URL%/}" |
| 47 | +ORCH_URL="${ORCH_URL%/}" |
| 48 | +PASS=0 |
| 49 | +FAIL=0 |
| 50 | +SKIP=0 |
| 51 | + |
| 52 | +log_pass() { echo -e "${GREEN}[PASS]${NC} $1"; PASS=$((PASS + 1)); } |
| 53 | +log_fail() { echo -e "${RED}[FAIL]${NC} $1"; FAIL=$((FAIL + 1)); } |
| 54 | +log_skip() { echo -e "${YELLOW}[SKIP]${NC} $1"; SKIP=$((SKIP + 1)); } |
| 55 | +log_info() { echo -e " $1"; } |
| 56 | + |
| 57 | +check_status() { |
| 58 | + local desc="$1" status="$2" body="$3" expected="${4:-200}" |
| 59 | + if [[ "$status" == "$expected" ]]; then |
| 60 | + log_pass "$desc (HTTP $status)" |
| 61 | + else |
| 62 | + log_fail "$desc (HTTP $status, expected $expected)" |
| 63 | + log_info "Response: $(echo "$body" | head -5)" |
| 64 | + fi |
| 65 | +} |
| 66 | + |
| 67 | +# ───────────────────────────────────────────────────────── |
| 68 | +echo "" |
| 69 | +echo "==========================================" |
| 70 | +echo " Resource Calendar Test Suite" |
| 71 | +echo "==========================================" |
| 72 | +echo " Reports: $REPORTS_URL" |
| 73 | +echo " Orchestrator: $ORCH_URL" |
| 74 | +echo "==========================================" |
| 75 | +echo "" |
| 76 | + |
| 77 | +# ───────────────────────────────────────────────────────── |
| 78 | +echo "--- Step 1: Fetch host data from orchestrator ---" |
| 79 | +echo "" |
| 80 | + |
| 81 | +SUMMARY_RESP=$(curl -sk -w "\n%{http_code}" "$ORCH_URL/portalresources/summary?type=hosts") |
| 82 | +SUMMARY_STATUS=$(echo "$SUMMARY_RESP" | tail -1) |
| 83 | +SUMMARY_BODY=$(echo "$SUMMARY_RESP" | sed '$d') |
| 84 | + |
| 85 | +check_status "GET /portalresources/summary?type=hosts" "$SUMMARY_STATUS" "$SUMMARY_BODY" |
| 86 | + |
| 87 | +# Extract hosts from summary |
| 88 | +HOSTS_JSON=$(echo "$SUMMARY_BODY" | python3 -c " |
| 89 | +import sys, json |
| 90 | +data = json.load(sys.stdin) |
| 91 | +hosts = [] |
| 92 | +for item in data.get('data', []): |
| 93 | + if isinstance(item, dict) and 'hosts' in item: |
| 94 | + hosts = item['hosts'] |
| 95 | + break |
| 96 | +print(json.dumps(hosts)) |
| 97 | +" 2>/dev/null || echo "[]") |
| 98 | + |
| 99 | +HOST_COUNT=$(echo "$HOSTS_JSON" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))") |
| 100 | +log_info "Found $HOST_COUNT hosts in orchestrator summary" |
| 101 | + |
| 102 | +# Grab first host/site for later queries |
| 103 | +FIRST_HOST=$(echo "$HOSTS_JSON" | python3 -c "import sys,json; h=json.load(sys.stdin); print(h[0]['name'] if h else '')" 2>/dev/null) |
| 104 | +FIRST_SITE=$(echo "$HOSTS_JSON" | python3 -c "import sys,json; h=json.load(sys.stdin); print(h[0]['site'] if h else '')" 2>/dev/null) |
| 105 | +echo "" |
| 106 | + |
| 107 | +# ───────────────────────────────────────────────────────── |
| 108 | +echo "--- Step 2: Query calendar - basic (3 days, daily) ---" |
| 109 | +echo "" |
| 110 | + |
| 111 | +START="2025-06-01T00:00:00%2B00:00" |
| 112 | +END="2025-06-04T00:00:00%2B00:00" |
| 113 | + |
| 114 | +RESP=$(curl -sk -w "\n%{http_code}" \ |
| 115 | + "$REPORTS_URL/calendar?start_time=$START&end_time=$END&interval=day" \ |
| 116 | + -H "Authorization: Bearer $TOKEN") |
| 117 | +STATUS=$(echo "$RESP" | tail -1) |
| 118 | +BODY=$(echo "$RESP" | sed '$d') |
| 119 | + |
| 120 | +check_status "GET /calendar (3 days, daily)" "$STATUS" "$BODY" |
| 121 | + |
| 122 | +# Validate response structure |
| 123 | +echo "$BODY" | python3 -c " |
| 124 | +import sys, json |
| 125 | +data = json.load(sys.stdin) |
| 126 | +slots = data.get('data', []) |
| 127 | +total = data.get('total', 0) |
| 128 | +interval = data.get('interval', '') |
| 129 | +errors = [] |
| 130 | +if total != 3: |
| 131 | + errors.append(f'Expected 3 slots, got {total}') |
| 132 | +if interval != 'day': |
| 133 | + errors.append(f'Expected interval=day, got {interval}') |
| 134 | +for slot in slots: |
| 135 | + if 'hosts' not in slot or 'sites' not in slot: |
| 136 | + errors.append('Slot missing hosts or sites key') |
| 137 | + break |
| 138 | + for h in slot['hosts']: |
| 139 | + for key in ['cores_capacity', 'cores_allocated', 'cores_available', 'components']: |
| 140 | + if key not in h: |
| 141 | + errors.append(f'Host missing key: {key}') |
| 142 | + break |
| 143 | +if errors: |
| 144 | + print('VALIDATION ERRORS: ' + '; '.join(errors)) |
| 145 | + sys.exit(1) |
| 146 | +else: |
| 147 | + print(f'OK: {total} slots, {len(slots[0][\"hosts\"])} hosts, {len(slots[0][\"sites\"])} sites per slot') |
| 148 | +" 2>/dev/null && log_pass "Response structure validation" || log_fail "Response structure validation" |
| 149 | +echo "" |
| 150 | + |
| 151 | +# ───────────────────────────────────────────────────────── |
| 152 | +echo "--- Step 3: Query calendar - weekly interval ---" |
| 153 | +echo "" |
| 154 | + |
| 155 | +RESP=$(curl -sk -w "\n%{http_code}" \ |
| 156 | + "$REPORTS_URL/calendar?start_time=2025-06-01T00:00:00%2B00:00&end_time=2025-06-30T00:00:00%2B00:00&interval=week" \ |
| 157 | + -H "Authorization: Bearer $TOKEN") |
| 158 | +STATUS=$(echo "$RESP" | tail -1) |
| 159 | +BODY=$(echo "$RESP" | sed '$d') |
| 160 | + |
| 161 | +check_status "GET /calendar (weekly, June)" "$STATUS" "$BODY" |
| 162 | + |
| 163 | +SLOT_COUNT=$(echo "$BODY" | python3 -c "import sys,json; print(json.load(sys.stdin).get('total',0))" 2>/dev/null) |
| 164 | +log_info "Got $SLOT_COUNT weekly slots" |
| 165 | +echo "" |
| 166 | + |
| 167 | +# ───────────────────────────────────────────────────────── |
| 168 | +echo "--- Step 4: Query calendar - site filter ---" |
| 169 | +echo "" |
| 170 | + |
| 171 | +if [[ -n "$FIRST_SITE" ]]; then |
| 172 | + RESP=$(curl -sk -w "\n%{http_code}" \ |
| 173 | + "$REPORTS_URL/calendar?start_time=$START&end_time=$END&interval=day&site=$FIRST_SITE" \ |
| 174 | + -H "Authorization: Bearer $TOKEN") |
| 175 | + STATUS=$(echo "$RESP" | tail -1) |
| 176 | + BODY=$(echo "$RESP" | sed '$d') |
| 177 | + |
| 178 | + check_status "GET /calendar (site=$FIRST_SITE)" "$STATUS" "$BODY" |
| 179 | + |
| 180 | + echo "$BODY" | python3 -c " |
| 181 | +import sys, json |
| 182 | +data = json.load(sys.stdin) |
| 183 | +slots = data.get('data', []) |
| 184 | +if slots: |
| 185 | + sites = set() |
| 186 | + for s in slots: |
| 187 | + for h in s['hosts']: |
| 188 | + sites.add(h['site']) |
| 189 | + print(f'Sites in response: {sites}') |
| 190 | + if len(sites) == 1 and '$FIRST_SITE' in sites: |
| 191 | + print('OK: Only $FIRST_SITE hosts returned') |
| 192 | + else: |
| 193 | + print(f'WARNING: Expected only $FIRST_SITE, got {sites}') |
| 194 | +" 2>/dev/null |
| 195 | +else |
| 196 | + log_skip "Site filter test (no hosts found)" |
| 197 | +fi |
| 198 | +echo "" |
| 199 | + |
| 200 | +# ───────────────────────────────────────────────────────── |
| 201 | +echo "--- Step 5: Query calendar - exclude_site filter ---" |
| 202 | +echo "" |
| 203 | + |
| 204 | +if [[ -n "$FIRST_SITE" ]]; then |
| 205 | + RESP=$(curl -sk -w "\n%{http_code}" \ |
| 206 | + "$REPORTS_URL/calendar?start_time=$START&end_time=$END&interval=day&exclude_site=$FIRST_SITE" \ |
| 207 | + -H "Authorization: Bearer $TOKEN") |
| 208 | + STATUS=$(echo "$RESP" | tail -1) |
| 209 | + BODY=$(echo "$RESP" | sed '$d') |
| 210 | + |
| 211 | + check_status "GET /calendar (exclude_site=$FIRST_SITE)" "$STATUS" "$BODY" |
| 212 | + |
| 213 | + echo "$BODY" | python3 -c " |
| 214 | +import sys, json |
| 215 | +data = json.load(sys.stdin) |
| 216 | +slots = data.get('data', []) |
| 217 | +if slots: |
| 218 | + sites = set() |
| 219 | + for s in slots: |
| 220 | + for h in s['hosts']: |
| 221 | + sites.add(h['site']) |
| 222 | + if '$FIRST_SITE' not in sites: |
| 223 | + print(f'OK: $FIRST_SITE excluded, got sites: {sites}') |
| 224 | + else: |
| 225 | + print(f'WARNING: $FIRST_SITE still present in response') |
| 226 | +" 2>/dev/null |
| 227 | +else |
| 228 | + log_skip "Exclude site filter test (no hosts found)" |
| 229 | +fi |
| 230 | +echo "" |
| 231 | + |
| 232 | +# ───────────────────────────────────────────────────────── |
| 233 | +echo "--- Step 6: Query calendar - host filter ---" |
| 234 | +echo "" |
| 235 | + |
| 236 | +if [[ -n "$FIRST_HOST" ]]; then |
| 237 | + RESP=$(curl -sk -w "\n%{http_code}" \ |
| 238 | + "$REPORTS_URL/calendar?start_time=$START&end_time=$END&interval=day&host=$FIRST_HOST" \ |
| 239 | + -H "Authorization: Bearer $TOKEN") |
| 240 | + STATUS=$(echo "$RESP" | tail -1) |
| 241 | + BODY=$(echo "$RESP" | sed '$d') |
| 242 | + |
| 243 | + check_status "GET /calendar (host=$FIRST_HOST)" "$STATUS" "$BODY" |
| 244 | + |
| 245 | + HOST_IN_RESP=$(echo "$BODY" | python3 -c " |
| 246 | +import sys, json |
| 247 | +data = json.load(sys.stdin) |
| 248 | +slots = data.get('data', []) |
| 249 | +if slots: |
| 250 | + names = [h['name'] for h in slots[0]['hosts']] |
| 251 | + print(f'{len(names)} host(s): {names}') |
| 252 | +" 2>/dev/null) |
| 253 | + log_info "$HOST_IN_RESP" |
| 254 | +else |
| 255 | + log_skip "Host filter test (no hosts found)" |
| 256 | +fi |
| 257 | +echo "" |
| 258 | + |
| 259 | +# ───────────────────────────────────────────────────────── |
| 260 | +echo "--- Step 7: Query calendar - with active slivers (current time range) ---" |
| 261 | +echo "" |
| 262 | + |
| 263 | +NOW=$(date -u +%Y-%m-%dT%H:%M:%S%2B00:00) |
| 264 | +NEXT_WEEK=$(python3 -c "from datetime import datetime,timedelta,timezone; print((datetime.now(timezone.utc)+timedelta(days=7)).strftime('%Y-%m-%dT%H:%M:%S%2B00:00'))") |
| 265 | + |
| 266 | +RESP=$(curl -sk -w "\n%{http_code}" \ |
| 267 | + "$REPORTS_URL/calendar?start_time=$NOW&end_time=$NEXT_WEEK&interval=day" \ |
| 268 | + -H "Authorization: Bearer $TOKEN") |
| 269 | +STATUS=$(echo "$RESP" | tail -1) |
| 270 | +BODY=$(echo "$RESP" | sed '$d') |
| 271 | + |
| 272 | +check_status "GET /calendar (current week)" "$STATUS" "$BODY" |
| 273 | + |
| 274 | +echo "$BODY" | python3 -c " |
| 275 | +import sys, json |
| 276 | +data = json.load(sys.stdin) |
| 277 | +slots = data.get('data', []) |
| 278 | +if not slots: |
| 279 | + print(' No slots returned') |
| 280 | +else: |
| 281 | + total_alloc = 0 |
| 282 | + for slot in slots: |
| 283 | + for h in slot.get('hosts', []): |
| 284 | + total_alloc += h.get('cores_allocated', 0) |
| 285 | + if total_alloc > 0: |
| 286 | + print(f' Allocations detected: {total_alloc} total cores allocated across all slots') |
| 287 | + # Show first slot details |
| 288 | + s = slots[0] |
| 289 | + for h in s['hosts']: |
| 290 | + if h['cores_allocated'] > 0: |
| 291 | + print(f' {h[\"name\"]} ({h[\"site\"]}): {h[\"cores_allocated\"]}/{h[\"cores_capacity\"]} cores, {h[\"ram_allocated\"]}/{h[\"ram_capacity\"]} RAM') |
| 292 | + else: |
| 293 | + print(' No active allocations found (expected if no slivers overlap this time range)') |
| 294 | +" 2>/dev/null |
| 295 | +echo "" |
| 296 | + |
| 297 | +# ───────────────────────────────────────────────────────── |
| 298 | +echo "--- Step 8: Validation - bad requests ---" |
| 299 | +echo "" |
| 300 | + |
| 301 | +# Missing end_time |
| 302 | +RESP=$(curl -sk -w "\n%{http_code}" \ |
| 303 | + "$REPORTS_URL/calendar?start_time=$START" \ |
| 304 | + -H "Authorization: Bearer $TOKEN") |
| 305 | +STATUS=$(echo "$RESP" | tail -1) |
| 306 | +check_status "GET /calendar missing end_time -> 400" "$STATUS" "" "400" |
| 307 | + |
| 308 | +# start > end |
| 309 | +RESP=$(curl -sk -w "\n%{http_code}" \ |
| 310 | + "$REPORTS_URL/calendar?start_time=2025-06-04T00:00:00%2B00:00&end_time=2025-06-01T00:00:00%2B00:00" \ |
| 311 | + -H "Authorization: Bearer $TOKEN") |
| 312 | +STATUS=$(echo "$RESP" | tail -1) |
| 313 | +check_status "GET /calendar start > end -> 400" "$STATUS" "" "400" |
| 314 | + |
| 315 | +# Invalid interval |
| 316 | +RESP=$(curl -sk -w "\n%{http_code}" \ |
| 317 | + "$REPORTS_URL/calendar?start_time=$START&end_time=$END&interval=month" \ |
| 318 | + -H "Authorization: Bearer $TOKEN") |
| 319 | +STATUS=$(echo "$RESP" | tail -1) |
| 320 | +check_status "GET /calendar invalid interval -> 400" "$STATUS" "" "400" |
| 321 | + |
| 322 | +# No auth |
| 323 | +RESP=$(curl -sk -w "\n%{http_code}" \ |
| 324 | + "$REPORTS_URL/calendar?start_time=$START&end_time=$END") |
| 325 | +STATUS=$(echo "$RESP" | tail -1) |
| 326 | +check_status "GET /calendar no auth -> 401" "$STATUS" "" "401" |
| 327 | +echo "" |
| 328 | + |
| 329 | +# ───────────────────────────────────────────────────────── |
| 330 | +echo "--- Step 9: Orchestrator proxy ---" |
| 331 | +echo "" |
| 332 | + |
| 333 | +if [[ -n "$FABRIC_TOKEN" ]]; then |
| 334 | + RESP=$(curl -sk -w "\n%{http_code}" \ |
| 335 | + "$ORCH_URL/resources/calendar?start_date=$START&end_date=$END&interval=day" \ |
| 336 | + -H "Authorization: Bearer $FABRIC_TOKEN") |
| 337 | + STATUS=$(echo "$RESP" | tail -1) |
| 338 | + BODY=$(echo "$RESP" | sed '$d') |
| 339 | + |
| 340 | + check_status "GET /resources/calendar via orchestrator" "$STATUS" "$BODY" |
| 341 | + |
| 342 | + SLOT_COUNT=$(echo "$BODY" | python3 -c " |
| 343 | +import sys, json |
| 344 | +data = json.load(sys.stdin) |
| 345 | +# Orchestrator wraps in Resources: {data: [{...calendar...}]} |
| 346 | +for item in data.get('data', []): |
| 347 | + if isinstance(item, dict) and 'total' in item: |
| 348 | + print(item['total']) |
| 349 | + break |
| 350 | +" 2>/dev/null || echo "?") |
| 351 | + log_info "Calendar returned $SLOT_COUNT slots via orchestrator proxy" |
| 352 | +else |
| 353 | + log_skip "Orchestrator proxy test (no --fabric-token provided)" |
| 354 | +fi |
| 355 | +echo "" |
| 356 | + |
| 357 | +# ───────────────────────────────────────────────────────── |
| 358 | +echo "==========================================" |
| 359 | +echo -e " Results: ${GREEN}${PASS} passed${NC}, ${RED}${FAIL} failed${NC}, ${YELLOW}${SKIP} skipped${NC}" |
| 360 | +echo "==========================================" |
| 361 | + |
| 362 | +if [[ $FAIL -gt 0 ]]; then |
| 363 | + exit 1 |
| 364 | +fi |
0 commit comments