# 帳票設計書 14-Node Attributes Report

## 概要

本ドキュメントは、OpenSearchのCat API「/_cat/nodeattrs」エンドポイントが出力するNode Attributes Reportの設計仕様を定義する。本帳票はクラスタ内の各ノードに設定されたカスタム属性情報をテキストテーブル形式にて出力するものである。

### 本帳票の処理概要

本帳票は、OpenSearchクラスタの全ノードに設定されたカスタム属性（node.attr.* 設定）を一覧化する。ClusterStateからノード一覧を取得し、さらにNodesInfoからプロセス情報を取得する2段階の非同期処理で構成される。各ノードの各属性が1行として出力される。

**業務上の目的・背景**：ノード属性はシャードのアロケーション制御やAwareness設定に使用され、例えばラック配置やデータセンター配置に基づくシャード分散に不可欠である。本帳票により全ノードの属性設定を一覧確認でき、設定漏れや不整合を迅速に検出できる。

**帳票の利用シーン**：シャードアロケーション設定の確認、ノード属性ベースのフィルタリング検証、新規ノード追加時の属性設定確認、クラスタのトポロジー把握に利用される。

**主要な出力内容**：
1. ノード識別情報（ノード名、ノードID、PID、ホスト名、IPアドレス、ポート）
2. 属性名
3. 属性値

**帳票の出力タイミング**：ユーザーがREST APIエンドポイント `GET /_cat/nodeattrs` にHTTPリクエストを送信した際にリアルタイムで生成・出力される。

**帳票の利用者**：OpenSearchクラスタ管理者、インフラストラクチャエンジニア。

## 帳票種別

一覧表（ノード属性一覧）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | REST API | `GET /_cat/nodeattrs` | HTTPリクエスト送信 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | text/plain（テキストテーブル）またはJSON/YAML（format指定時） |
| 用紙サイズ | N/A（API応答） |
| 向き | N/A |
| ファイル名 | N/A（HTTPレスポンスボディ） |
| 出力方法 | HTTPレスポンス |
| 文字コード | UTF-8 |

### PDF固有設定

該当なし

### Excel固有設定

該当なし

## 帳票レイアウト

### レイアウト概要

テキストテーブル形式で、1行が1ノードの1属性に対応する一覧を出力する。

```
┌────────────────────────────────────────────────────────┐
│  ヘッダー部（カラム名: node, id*, pid*, host, ip,       │
│             port*, attr, value）  *=デフォルト非表示     │
├────────────────────────────────────────────────────────┤
│  明細部（ノード×属性ごとに1行）                           │
│  例: node1  host1  10.0.0.1  rack  rack1               │
│      node1  host1  10.0.0.1  zone  us-east-1           │
│      node2  host2  10.0.0.2  rack  rack2               │
└────────────────────────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | node | ノード名（デフォルト表示） | DiscoveryNode.getName() | 文字列（alias: name） |
| 2 | id | ノードID（デフォルト非表示） | DiscoveryNode.getId() | 文字列（alias: nodeId）。full_id=falseの場合4文字に短縮 |
| 3 | pid | プロセスID（デフォルト非表示） | ProcessInfo.getId() | 数値（alias: p） |
| 4 | host | ホスト名 | DiscoveryNode.getHostName() | 文字列（alias: h） |
| 5 | ip | IPアドレス | DiscoveryNode.getHostAddress() | 文字列（alias: i） |
| 6 | port | トランスポートポート（デフォルト非表示） | DiscoveryNode.getAddress().address().getPort() | 数値（alias: po） |
| 7 | attr | 属性名（デフォルト表示） | Map.Entry.getKey() | 文字列（alias: attr.name） |
| 8 | value | 属性値（デフォルト表示） | Map.Entry.getValue() | 文字列（alias: attr.value） |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | node | ノード名 | DiscoveryNode.getName() | 文字列 | 可変 |
| 2 | id | ノードID | DiscoveryNode.getId() | 文字列（短縮可） | 可変 |
| 3 | pid | プロセスID | ProcessInfo.getId() | 数値/null | 可変 |
| 4 | host | ホスト名 | DiscoveryNode.getHostName() | 文字列 | 可変 |
| 5 | ip | IPアドレス | DiscoveryNode.getHostAddress() | 文字列 | 可変 |
| 6 | port | ポート番号 | getAddress().address().getPort() | 数値 | 可変 |
| 7 | attr | 属性名 | attrEntry.getKey() | 文字列 | 可変 |
| 8 | value | 属性値 | attrEntry.getValue() | 文字列 | 可変 |

### フッター部

フッター部は存在しない。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| local | ローカルノードのクラスタステートのみ参照するか | No（デフォルト: false） |
| cluster_manager_timeout | クラスタマネージャへのタイムアウト | No |
| full_id | ノードIDをフル表示するか | No（デフォルト: false、4文字に短縮） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | ノード順 | DiscoveryNodesのイテレーション順 |
| 2 | 属性順 | getAttributes()のMapイテレーション順 |

### 改ページ条件

改ページなし。

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| ClusterState.nodes（内部API） | ノード一覧・属性取得 | ClusterStateRequest.clear().nodes(true) |
| NodesInfo.PROCESS（内部API） | プロセスID取得 | NodesInfoRequest.clear().addMetric(PROCESS) |

### テーブル別参照項目詳細

#### ClusterStateResponse

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| DiscoveryNode.getName() | node | - | ノード名 |
| DiscoveryNode.getId() | id | full_id=falseの場合4文字短縮 | ノードID |
| DiscoveryNode.getHostName() | host | - | ホスト名 |
| DiscoveryNode.getHostAddress() | ip | - | IPアドレス |
| DiscoveryNode.getAddress().address().getPort() | port | - | トランスポートポート |
| DiscoveryNode.getAttributes() | attr, value | Map.Entry形式 | カスタム属性 |

#### NodesInfoResponse

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| ProcessInfo.getId() | pid | info.getInfo(ProcessInfo.class) | infoがnullの場合はnull出力 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| id（短縮） | Strings.substring(node.getId(), 0, 4) | - | full_id=false時のID短縮 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[クライアントからGETリクエスト受信] --> B[RestNodeAttrsAction.doCatRequest]
    B --> C[ClusterStateRequest生成 - nodes=true]
    C --> D[NodeClient.admin.cluster.state実行]
    D --> E[ClusterStateResponse受信]
    E --> F[NodesInfoRequest生成 - PROCESS指定]
    F --> G[NodeClient.admin.cluster.nodesInfo実行]
    G --> H[NodesInfoResponse受信]
    H --> I[buildTable: DiscoveryNodesをループ]
    I --> J[各ノードのgetAttributes()をループ]
    J --> K[テーブル行追加（ノード情報+属性名+属性値）]
    K --> L[RestTable.buildResponse]
    L --> M[HTTPレスポンス返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ノード情報なし | NodesInfoResponseにエントリがない場合 | PIDがnullで出力（行136: info==null時null） | NullPointerException回避のガード処理 |
| 属性なし | ノードにカスタム属性がない場合 | 該当ノードの行が出力されない | 正常動作（getAttributes()が空Map） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | ノード数 x 属性数（典型的には数十行） |
| 目標出力時間 | クラスタステート + ノード情報取得（通常1秒以内） |
| 同時出力数上限 | REST APIスレッドプールに依存 |

## セキュリティ考慮事項

ノード属性にはインフラストラクチャ配置情報が含まれる可能性があるため、cluster:monitor/state および cluster:monitor/nodes/info 権限を持つユーザーのみアクセス可能とすべきである。

## 備考

- デフォルト表示カラムは node, attr, value の3つ。id, pid, port はデフォルト非表示
- `?full_id=true` でノードIDの全文字を表示可能
- `master_timeout` パラメータは非推奨

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DiscoveryNode.java | `server/src/main/java/org/opensearch/cluster/node/DiscoveryNode.java` | getAttributes()メソッドでMap<String,String>形式の属性取得 |
| 1-2 | ProcessInfo.java | `server/src/main/java/org/opensearch/monitor/process/ProcessInfo.java` | getId()でプロセスIDを取得 |

**読解のコツ**: DiscoveryNode.getAttributes()はnodeのyaml設定のnode.attr.*で定義されたカスタム属性を返す。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestNodeAttrsAction.java | `server/src/main/java/org/opensearch/rest/action/cat/RestNodeAttrsAction.java` | 帳票メインクラス。147行 |

**主要処理フロー**:
1. **行68-70**: routes()で `/_cat/nodeattrs` ルートを定義
2. **行83-106**: doCatRequest()で2段階非同期処理
3. **行84-85**: ClusterStateRequest で nodes=true
4. **行95-97**: NodesInfoRequest で PROCESS メトリック指定
5. **行109-122**: getTableWithHeader()で8カラム定義（3カラムがデフォルト非表示）
6. **行124-146**: buildTable()でノード x 属性のネストループ
7. **行125**: full_idパラメータの取得
8. **行135**: Strings.substring(node.getId(), 0, 4)でID短縮

### プログラム呼び出し階層図

```
HTTP GET /_cat/nodeattrs
    |
    +-- AbstractCatAction.prepareRequest()
        |
        +-- RestNodeAttrsAction.doCatRequest() [行83]
            |
            +-- ClusterStateRequest.clear().nodes(true) [行84-85]
            +-- NodeClient.admin().cluster().state() [行92]
                |
                +-- RestActionListener.processResponse() [行94]
                    |
                    +-- NodesInfoRequest.addMetric(PROCESS) [行97]
                    +-- NodeClient.admin().cluster().nodesInfo() [行98]
                        |
                        +-- RestResponseListener.buildResponse() [行100]
                            |
                            +-- buildTable() [行124]
                            |   +-- DiscoveryNodes loop [行130]
                            |       +-- getAttributes() loop [行132]
                            |           +-- table.addCell() x 8 [行134-141]
                            |
                            +-- RestTable.buildResponse() [行101]
```

### データフロー図

```
[入力]                    [処理]                           [出力]

HTTP GET Request   --> RestNodeAttrsAction              --> HTTP Response
  /_cat/nodeattrs       |                                   (text/plain
                        +-> ClusterStateRequest              or JSON)
                        |   (nodes only)
                        +-> ClusterStateResponse
                        |   (DiscoveryNodes with attributes)
                        +-> NodesInfoRequest
                        |   (PROCESS metric)
                        +-> NodesInfoResponse
                        |   (ProcessInfo for PID)
                        +-> buildTable()
                        +-> RestTable.buildResponse()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RestNodeAttrsAction.java | `server/src/main/java/org/opensearch/rest/action/cat/RestNodeAttrsAction.java` | ソース | 帳票メインクラス（147行） |
| AbstractCatAction.java | `server/src/main/java/org/opensearch/rest/action/cat/AbstractCatAction.java` | ソース | Cat API基底クラス |
| RestTable.java | `server/src/main/java/org/opensearch/rest/action/cat/RestTable.java` | ソース | テーブルレスポンス構築 |
| DiscoveryNode.java | `server/src/main/java/org/opensearch/cluster/node/DiscoveryNode.java` | ソース | ノード情報・属性 |
| ProcessInfo.java | `server/src/main/java/org/opensearch/monitor/process/ProcessInfo.java` | ソース | プロセス情報 |
| NodeInfo.java | `server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodeInfo.java` | ソース | ノード情報コンテナ |
| ActionModule.java | `server/src/main/java/org/opensearch/action/ActionModule.java` | ソース | アクション登録 |
