conoha-dns-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conoha_dns_cli-0.1.0/PKG-INFO +205 -0
- conoha_dns_cli-0.1.0/README.md +193 -0
- conoha_dns_cli-0.1.0/pyproject.toml +27 -0
- conoha_dns_cli-0.1.0/setup.cfg +4 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli/__init__.py +0 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli/client.py +107 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli/domain.py +104 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli/id_converter.py +12 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli/main.py +118 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli/record.py +118 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli/utils.py +36 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli.egg-info/PKG-INFO +205 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli.egg-info/SOURCES.txt +15 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli.egg-info/dependency_links.txt +1 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli.egg-info/entry_points.txt +2 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli.egg-info/requires.txt +3 -0
- conoha_dns_cli-0.1.0/src/conoha_dns_cli.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: conoha-dns-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool to manage ConoHa DNS.
|
|
5
|
+
Author-email: haturatu <taro@eyes4you.org>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: python-dotenv
|
|
10
|
+
Requires-Dist: requests
|
|
11
|
+
Requires-Dist: xxhash
|
|
12
|
+
|
|
13
|
+
# ConoHa DNS CLI
|
|
14
|
+
|
|
15
|
+
ConoHa DNSをコマンドラインから操作するためのツールです。
|
|
16
|
+
|
|
17
|
+
デフォルトでは見やすいテーブル形式で一覧表示し、`--output csv` オプションを指定することでCSV形式で標準出力することもできます。
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
$ conoha-dns -h
|
|
21
|
+
usage: main.py [-h]
|
|
22
|
+
(--auth | -l [DOMAIN] | -ad NAME EMAIL | -dd DOMAIN | -ar DOMAIN NAME TYPE DATA | -ur DOMAIN RECORD_ID | -dr DOMAIN RECORD_ID)
|
|
23
|
+
[-t TTL] [-o {csv}] [--new-name NEW_NAME] [--new-type NEW_TYPE]
|
|
24
|
+
[--new-data NEW_DATA] [--new-ttl NEW_TTL]
|
|
25
|
+
|
|
26
|
+
ConoHa DNS API (v1) を操作するCLIツール
|
|
27
|
+
|
|
28
|
+
options:
|
|
29
|
+
-h, --help show this help message and exit
|
|
30
|
+
--auth APIトークンを認証・取得する
|
|
31
|
+
-l [DOMAIN], --list [DOMAIN]
|
|
32
|
+
ドメイン一覧または指定ドメインのレコード一覧表示
|
|
33
|
+
-ad NAME EMAIL, --add-domain NAME EMAIL
|
|
34
|
+
ドメイン追加
|
|
35
|
+
-dd DOMAIN, --delete-domain DOMAIN
|
|
36
|
+
ドメインを名前またはIDで削除
|
|
37
|
+
-ar DOMAIN NAME TYPE DATA, --add-record DOMAIN NAME TYPE DATA
|
|
38
|
+
レコード追加
|
|
39
|
+
-ur DOMAIN RECORD_ID, --update-record DOMAIN RECORD_ID
|
|
40
|
+
レコード更新
|
|
41
|
+
-dr DOMAIN RECORD_ID, --delete-record DOMAIN RECORD_ID
|
|
42
|
+
レコード削除
|
|
43
|
+
-t TTL, --ttl TTL レコード追加時のTTL(秒)。デフォルト: 300
|
|
44
|
+
-o {csv}, --output {csv}
|
|
45
|
+
出力形式をCSVにします。'-l'での一覧表示時のみ有効です。
|
|
46
|
+
--new-name NEW_NAME 更新後のレコード名
|
|
47
|
+
--new-type NEW_TYPE 更新後のレコードタイプ
|
|
48
|
+
--new-data NEW_DATA 更新後のレコードデータ
|
|
49
|
+
--new-ttl NEW_TTL 更新後のTTL
|
|
50
|
+
|
|
51
|
+
使用例:
|
|
52
|
+
# APIトークンを認証・取得
|
|
53
|
+
conoha-dns --auth
|
|
54
|
+
|
|
55
|
+
# ドメイン一覧表示
|
|
56
|
+
conoha-dns -l
|
|
57
|
+
|
|
58
|
+
# レコード一覧をCSV形式で標準出力
|
|
59
|
+
conoha-dns -l example.com --output csv > records.csv
|
|
60
|
+
|
|
61
|
+
# Aレコード追加 (サブドメインtestを補完してtest.example.comを追加)
|
|
62
|
+
conoha-dns -ar example.com @ A 192.0.2.1
|
|
63
|
+
conoha-dns -ar example.com test A 192.0.2.1
|
|
64
|
+
|
|
65
|
+
# レコード更新 (レコードIDを指定し、新しいIPアドレスを設定)
|
|
66
|
+
conoha-dns -ur example.com <record_id> --new-data 192.0.2.2
|
|
67
|
+
|
|
68
|
+
# レコード削除 (レコードIDは -l example.com で確認)
|
|
69
|
+
conoha-dns -dr example.com <record_id>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## インストール
|
|
73
|
+
|
|
74
|
+
はじめに、ConoHaの認証情報を記述した`~/.conoha-env`ファイルをホームディレクトリに作成します。
|
|
75
|
+
APIエンドポイントはConoHaのリージョンによって異なるため、適宜変更してください。
|
|
76
|
+
[ConoHaコントロールパネル](https://cp.conoha.jp/VPS/API/) の「API情報」から確認できます。
|
|
77
|
+
|
|
78
|
+
**~/.conoha-env**:
|
|
79
|
+
```
|
|
80
|
+
CONOHA_USER_ID="your_user_id" # API ユーザー: ユーザID
|
|
81
|
+
CONOHA_PASSWORD="your_password" # API ユーザー: パスワード
|
|
82
|
+
TENANT_ID="your_tenant_id" # テナント情報: テナントID
|
|
83
|
+
CONOHA_AUTH_URL="https://identity.sample.conoha.io" # エンドポイント: Identity Service URL
|
|
84
|
+
CONOHA_DNS_API_URL="https://dns-service.sample.conoha.io" # エンドポイント: DNS Service URL
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
次に、Makefileを使ってビルドとインストールを実行します。
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
make install
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
これにより、`conoha-dns`コマンドがインストールされます。
|
|
94
|
+
|
|
95
|
+
## 使い方
|
|
96
|
+
|
|
97
|
+
このコマンドは、実行したい操作をフラグで指定します。一度に指定できる操作は1つだけです。
|
|
98
|
+
ドメインを指定する引数 (DOMAIN) には、ドメイン名 (`example.com`) またはドメインID (`ba9b5b9d`など) の両方を使用できます。
|
|
99
|
+
|
|
100
|
+
### 一般的なコマンド
|
|
101
|
+
|
|
102
|
+
**ヘルプ表示**
|
|
103
|
+
```bash
|
|
104
|
+
conoha-dns -h
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**認証**
|
|
108
|
+
認証情報を使って新しいAPIトークンを取得し、`~/.conoha-env`ファイルに保存します。
|
|
109
|
+
なお、ConoHa APIトークンの有効期限は24時間です。
|
|
110
|
+
```bash
|
|
111
|
+
conoha-dns --auth
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### ドメイン・レコード管理
|
|
115
|
+
|
|
116
|
+
**ドメイン一覧・レコード一覧**
|
|
117
|
+
|
|
118
|
+
`-l` フラグを使用すると、一覧がテーブル形式で表示されます。
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# ドメイン一覧
|
|
122
|
+
conoha-dns -l
|
|
123
|
+
|
|
124
|
+
# レコード一覧 (ドメイン名またはIDで指定)
|
|
125
|
+
conoha-dns -l <ドメイン名/ID>
|
|
126
|
+
```
|
|
127
|
+
*実行例:*
|
|
128
|
+
```bash
|
|
129
|
+
$ conoha-dns -l
|
|
130
|
+
ID Name
|
|
131
|
+
0e6f0695 soulminingrig.com.
|
|
132
|
+
4fb208de eyes4you.org.
|
|
133
|
+
...
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**CSV形式での標準出力**
|
|
137
|
+
|
|
138
|
+
`--output csv` オプションを追加すると、一覧をCSV形式で標準出力します。リダイレクト(`>`)を使えばファイルに保存できます。
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# ドメイン一覧をCSVで標準出力
|
|
142
|
+
conoha-dns -l --output csv
|
|
143
|
+
|
|
144
|
+
# レコード一覧をCSVファイルとして保存
|
|
145
|
+
conoha-dns -l <ドメイン名/ID> --output csv > records.csv
|
|
146
|
+
```
|
|
147
|
+
*実行例:*
|
|
148
|
+
```bash
|
|
149
|
+
$ conoha-dns -l example.com --output csv
|
|
150
|
+
ID,Name,Type,Data,TTL
|
|
151
|
+
5057adde,eye4u.org.,SOA,"a.conoha-dns.com. taro.eyes4you.org. 1757833524 3600 600 86400 3600",3600
|
|
152
|
+
2eff1245,eye4u.org.,NS,a.conoha-dns.com.,3600
|
|
153
|
+
...
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**ドメイン追加**
|
|
157
|
+
```bash
|
|
158
|
+
conoha-dns -ad <ドメイン名> <メールアドレス>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**ドメイン削除**
|
|
162
|
+
```bash
|
|
163
|
+
conoha-dns -dd <ドメイン名/ID>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### レコード管理
|
|
167
|
+
|
|
168
|
+
内部的に、ConoHa APIから取得したレコードIDは長いため、xxhashを用いて短いハッシュ値に変換して表示・利用しています。
|
|
169
|
+
|
|
170
|
+
**レコード追加**
|
|
171
|
+
```bash
|
|
172
|
+
conoha-dns -ar <ドメイン名/ID> <レコード名> <種別> <値> [--ttl <秒数>]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**レコード更新**
|
|
176
|
+
`record_id`(`conoha-dns -l <ドメイン名/ID>`で確認可能)と、少なくとも1つの`--new-*`オプションを指定する必要があります。
|
|
177
|
+
```bash
|
|
178
|
+
conoha-dns -ur <ドメイン名/ID> <record_id> [--new-name <名前>] [--new-type <種別>] [--new-data <値>] [--new-ttl <TTL>]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**レコード削除**
|
|
182
|
+
```bash
|
|
183
|
+
conoha-dns -dr <ドメイン名/ID> <record_id>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 応用例:xargsを使った一括操作
|
|
187
|
+
|
|
188
|
+
`xargs`と組み合わせることで、ファイルに記載されたドメインリストに対して一括で操作を実行できます。
|
|
189
|
+
|
|
190
|
+
例えば、以下のようなドメインリストが書かれた`domain-list.txt`があるとします。
|
|
191
|
+
|
|
192
|
+
**domain-list.txt**
|
|
193
|
+
```
|
|
194
|
+
example1.com
|
|
195
|
+
example2.com
|
|
196
|
+
example3.com
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
このファイル内の各ドメインに対して、一括でルートAレコードを追加するには、以下のコマンドを実行します。
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
cat domain-list.txt | xargs -I{} conoha-dns -ar {} @ A 192.0.2.1
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
これにより、`domain-list.txt`の各行が`{}`に代入され、ドメインごとにレコード追加コマンドが実行されます。
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# ConoHa DNS CLI
|
|
2
|
+
|
|
3
|
+
ConoHa DNSをコマンドラインから操作するためのツールです。
|
|
4
|
+
|
|
5
|
+
デフォルトでは見やすいテーブル形式で一覧表示し、`--output csv` オプションを指定することでCSV形式で標準出力することもできます。
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
$ conoha-dns -h
|
|
9
|
+
usage: main.py [-h]
|
|
10
|
+
(--auth | -l [DOMAIN] | -ad NAME EMAIL | -dd DOMAIN | -ar DOMAIN NAME TYPE DATA | -ur DOMAIN RECORD_ID | -dr DOMAIN RECORD_ID)
|
|
11
|
+
[-t TTL] [-o {csv}] [--new-name NEW_NAME] [--new-type NEW_TYPE]
|
|
12
|
+
[--new-data NEW_DATA] [--new-ttl NEW_TTL]
|
|
13
|
+
|
|
14
|
+
ConoHa DNS API (v1) を操作するCLIツール
|
|
15
|
+
|
|
16
|
+
options:
|
|
17
|
+
-h, --help show this help message and exit
|
|
18
|
+
--auth APIトークンを認証・取得する
|
|
19
|
+
-l [DOMAIN], --list [DOMAIN]
|
|
20
|
+
ドメイン一覧または指定ドメインのレコード一覧表示
|
|
21
|
+
-ad NAME EMAIL, --add-domain NAME EMAIL
|
|
22
|
+
ドメイン追加
|
|
23
|
+
-dd DOMAIN, --delete-domain DOMAIN
|
|
24
|
+
ドメインを名前またはIDで削除
|
|
25
|
+
-ar DOMAIN NAME TYPE DATA, --add-record DOMAIN NAME TYPE DATA
|
|
26
|
+
レコード追加
|
|
27
|
+
-ur DOMAIN RECORD_ID, --update-record DOMAIN RECORD_ID
|
|
28
|
+
レコード更新
|
|
29
|
+
-dr DOMAIN RECORD_ID, --delete-record DOMAIN RECORD_ID
|
|
30
|
+
レコード削除
|
|
31
|
+
-t TTL, --ttl TTL レコード追加時のTTL(秒)。デフォルト: 300
|
|
32
|
+
-o {csv}, --output {csv}
|
|
33
|
+
出力形式をCSVにします。'-l'での一覧表示時のみ有効です。
|
|
34
|
+
--new-name NEW_NAME 更新後のレコード名
|
|
35
|
+
--new-type NEW_TYPE 更新後のレコードタイプ
|
|
36
|
+
--new-data NEW_DATA 更新後のレコードデータ
|
|
37
|
+
--new-ttl NEW_TTL 更新後のTTL
|
|
38
|
+
|
|
39
|
+
使用例:
|
|
40
|
+
# APIトークンを認証・取得
|
|
41
|
+
conoha-dns --auth
|
|
42
|
+
|
|
43
|
+
# ドメイン一覧表示
|
|
44
|
+
conoha-dns -l
|
|
45
|
+
|
|
46
|
+
# レコード一覧をCSV形式で標準出力
|
|
47
|
+
conoha-dns -l example.com --output csv > records.csv
|
|
48
|
+
|
|
49
|
+
# Aレコード追加 (サブドメインtestを補完してtest.example.comを追加)
|
|
50
|
+
conoha-dns -ar example.com @ A 192.0.2.1
|
|
51
|
+
conoha-dns -ar example.com test A 192.0.2.1
|
|
52
|
+
|
|
53
|
+
# レコード更新 (レコードIDを指定し、新しいIPアドレスを設定)
|
|
54
|
+
conoha-dns -ur example.com <record_id> --new-data 192.0.2.2
|
|
55
|
+
|
|
56
|
+
# レコード削除 (レコードIDは -l example.com で確認)
|
|
57
|
+
conoha-dns -dr example.com <record_id>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## インストール
|
|
61
|
+
|
|
62
|
+
はじめに、ConoHaの認証情報を記述した`~/.conoha-env`ファイルをホームディレクトリに作成します。
|
|
63
|
+
APIエンドポイントはConoHaのリージョンによって異なるため、適宜変更してください。
|
|
64
|
+
[ConoHaコントロールパネル](https://cp.conoha.jp/VPS/API/) の「API情報」から確認できます。
|
|
65
|
+
|
|
66
|
+
**~/.conoha-env**:
|
|
67
|
+
```
|
|
68
|
+
CONOHA_USER_ID="your_user_id" # API ユーザー: ユーザID
|
|
69
|
+
CONOHA_PASSWORD="your_password" # API ユーザー: パスワード
|
|
70
|
+
TENANT_ID="your_tenant_id" # テナント情報: テナントID
|
|
71
|
+
CONOHA_AUTH_URL="https://identity.sample.conoha.io" # エンドポイント: Identity Service URL
|
|
72
|
+
CONOHA_DNS_API_URL="https://dns-service.sample.conoha.io" # エンドポイント: DNS Service URL
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
次に、Makefileを使ってビルドとインストールを実行します。
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
make install
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
これにより、`conoha-dns`コマンドがインストールされます。
|
|
82
|
+
|
|
83
|
+
## 使い方
|
|
84
|
+
|
|
85
|
+
このコマンドは、実行したい操作をフラグで指定します。一度に指定できる操作は1つだけです。
|
|
86
|
+
ドメインを指定する引数 (DOMAIN) には、ドメイン名 (`example.com`) またはドメインID (`ba9b5b9d`など) の両方を使用できます。
|
|
87
|
+
|
|
88
|
+
### 一般的なコマンド
|
|
89
|
+
|
|
90
|
+
**ヘルプ表示**
|
|
91
|
+
```bash
|
|
92
|
+
conoha-dns -h
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**認証**
|
|
96
|
+
認証情報を使って新しいAPIトークンを取得し、`~/.conoha-env`ファイルに保存します。
|
|
97
|
+
なお、ConoHa APIトークンの有効期限は24時間です。
|
|
98
|
+
```bash
|
|
99
|
+
conoha-dns --auth
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### ドメイン・レコード管理
|
|
103
|
+
|
|
104
|
+
**ドメイン一覧・レコード一覧**
|
|
105
|
+
|
|
106
|
+
`-l` フラグを使用すると、一覧がテーブル形式で表示されます。
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# ドメイン一覧
|
|
110
|
+
conoha-dns -l
|
|
111
|
+
|
|
112
|
+
# レコード一覧 (ドメイン名またはIDで指定)
|
|
113
|
+
conoha-dns -l <ドメイン名/ID>
|
|
114
|
+
```
|
|
115
|
+
*実行例:*
|
|
116
|
+
```bash
|
|
117
|
+
$ conoha-dns -l
|
|
118
|
+
ID Name
|
|
119
|
+
0e6f0695 soulminingrig.com.
|
|
120
|
+
4fb208de eyes4you.org.
|
|
121
|
+
...
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**CSV形式での標準出力**
|
|
125
|
+
|
|
126
|
+
`--output csv` オプションを追加すると、一覧をCSV形式で標準出力します。リダイレクト(`>`)を使えばファイルに保存できます。
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# ドメイン一覧をCSVで標準出力
|
|
130
|
+
conoha-dns -l --output csv
|
|
131
|
+
|
|
132
|
+
# レコード一覧をCSVファイルとして保存
|
|
133
|
+
conoha-dns -l <ドメイン名/ID> --output csv > records.csv
|
|
134
|
+
```
|
|
135
|
+
*実行例:*
|
|
136
|
+
```bash
|
|
137
|
+
$ conoha-dns -l example.com --output csv
|
|
138
|
+
ID,Name,Type,Data,TTL
|
|
139
|
+
5057adde,eye4u.org.,SOA,"a.conoha-dns.com. taro.eyes4you.org. 1757833524 3600 600 86400 3600",3600
|
|
140
|
+
2eff1245,eye4u.org.,NS,a.conoha-dns.com.,3600
|
|
141
|
+
...
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**ドメイン追加**
|
|
145
|
+
```bash
|
|
146
|
+
conoha-dns -ad <ドメイン名> <メールアドレス>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**ドメイン削除**
|
|
150
|
+
```bash
|
|
151
|
+
conoha-dns -dd <ドメイン名/ID>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### レコード管理
|
|
155
|
+
|
|
156
|
+
内部的に、ConoHa APIから取得したレコードIDは長いため、xxhashを用いて短いハッシュ値に変換して表示・利用しています。
|
|
157
|
+
|
|
158
|
+
**レコード追加**
|
|
159
|
+
```bash
|
|
160
|
+
conoha-dns -ar <ドメイン名/ID> <レコード名> <種別> <値> [--ttl <秒数>]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**レコード更新**
|
|
164
|
+
`record_id`(`conoha-dns -l <ドメイン名/ID>`で確認可能)と、少なくとも1つの`--new-*`オプションを指定する必要があります。
|
|
165
|
+
```bash
|
|
166
|
+
conoha-dns -ur <ドメイン名/ID> <record_id> [--new-name <名前>] [--new-type <種別>] [--new-data <値>] [--new-ttl <TTL>]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**レコード削除**
|
|
170
|
+
```bash
|
|
171
|
+
conoha-dns -dr <ドメイン名/ID> <record_id>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 応用例:xargsを使った一括操作
|
|
175
|
+
|
|
176
|
+
`xargs`と組み合わせることで、ファイルに記載されたドメインリストに対して一括で操作を実行できます。
|
|
177
|
+
|
|
178
|
+
例えば、以下のようなドメインリストが書かれた`domain-list.txt`があるとします。
|
|
179
|
+
|
|
180
|
+
**domain-list.txt**
|
|
181
|
+
```
|
|
182
|
+
example1.com
|
|
183
|
+
example2.com
|
|
184
|
+
example3.com
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
このファイル内の各ドメインに対して、一括でルートAレコードを追加するには、以下のコマンドを実行します。
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
cat domain-list.txt | xargs -I{} conoha-dns -ar {} @ A 192.0.2.1
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
これにより、`domain-list.txt`の各行が`{}`に代入され、ドメインごとにレコード追加コマンドが実行されます。
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "conoha-dns-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A CLI tool to manage ConoHa DNS."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "haturatu", email = "taro@eyes4you.org"},
|
|
13
|
+
]
|
|
14
|
+
license = "BSD-3-Clause"
|
|
15
|
+
|
|
16
|
+
dependencies = [
|
|
17
|
+
"python-dotenv",
|
|
18
|
+
"requests",
|
|
19
|
+
"xxhash",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
conoha-dns = "conoha_dns_cli.main:main"
|
|
24
|
+
|
|
25
|
+
[tool.setuptools]
|
|
26
|
+
package-dir = {"" = "src"}
|
|
27
|
+
packages = ["conoha_dns_cli"]
|
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
import json
|
|
4
|
+
from functools import lru_cache
|
|
5
|
+
from .utils import handle_api_error
|
|
6
|
+
|
|
7
|
+
class ConohaDNSClient:
|
|
8
|
+
def __init__(self, renew=False):
|
|
9
|
+
self.api_base_url = os.getenv("CONOHA_DNS_API_URL", "https://dns-service.c3j1.conoha.io")
|
|
10
|
+
self.token = self._get_api_token(renew)
|
|
11
|
+
|
|
12
|
+
def _get_api_token(self, renew=False):
|
|
13
|
+
if not renew:
|
|
14
|
+
token = os.getenv("CONOHA_TOKEN") or os.getenv("API_TOKEN")
|
|
15
|
+
if token:
|
|
16
|
+
return token
|
|
17
|
+
|
|
18
|
+
user_id = os.getenv("CONOHA_USER_ID")
|
|
19
|
+
password = os.getenv("CONOHA_PASSWORD")
|
|
20
|
+
project_id = os.getenv("TENANT_ID")
|
|
21
|
+
|
|
22
|
+
if not all([user_id, password, project_id]):
|
|
23
|
+
raise ValueError("APIトークンが見つからず、認証情報 (~/.conoha-envの CONOHA_USER_ID, CONOHA_PASSWORD, TENANT_ID) も不完全です。")
|
|
24
|
+
|
|
25
|
+
auth_base_url = os.getenv("CONOHA_AUTH_URL", "https://identity.c3j1.conoha.io")
|
|
26
|
+
auth_url = f"{auth_base_url.rstrip('/')}/v3/auth/tokens"
|
|
27
|
+
|
|
28
|
+
payload = {
|
|
29
|
+
"auth": {
|
|
30
|
+
"identity": {
|
|
31
|
+
"methods": ["password"],
|
|
32
|
+
"password": {
|
|
33
|
+
"user": {"id": user_id, "password": password}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"scope": {"project": {"id": project_id}}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
headers = {"Accept": "application/json", "Content-Type": "application/json"}
|
|
40
|
+
|
|
41
|
+
print("認証して新しいAPIトークンを取得します...")
|
|
42
|
+
try:
|
|
43
|
+
response = requests.post(auth_url, headers=headers, json=payload)
|
|
44
|
+
new_token = response.headers.get("X-Subject-Token")
|
|
45
|
+
|
|
46
|
+
if response.status_code == 201 and new_token:
|
|
47
|
+
print("新しいAPIトークンを取得しました。")
|
|
48
|
+
try:
|
|
49
|
+
env_path = os.path.expanduser("~/.conoha-env")
|
|
50
|
+
lines = []
|
|
51
|
+
if os.path.exists(env_path):
|
|
52
|
+
with open(env_path, "r") as f:
|
|
53
|
+
lines = f.readlines()
|
|
54
|
+
|
|
55
|
+
with open(env_path, "w") as f:
|
|
56
|
+
for line in lines:
|
|
57
|
+
if not line.startswith("CONOHA_TOKEN="):
|
|
58
|
+
f.write(line)
|
|
59
|
+
f.write(f"CONOHA_TOKEN={new_token}\n")
|
|
60
|
+
print(f"{env_path}ファイルにCONOHA_TOKENを更新しました。")
|
|
61
|
+
|
|
62
|
+
except IOError as e:
|
|
63
|
+
print(f"警告: {env_path}ファイルへの書き込みに失敗しました。手動でトークンを保存してください。エラー: {e}")
|
|
64
|
+
os.environ['CONOHA_TOKEN'] = new_token
|
|
65
|
+
return new_token
|
|
66
|
+
else:
|
|
67
|
+
error_message = f"APIトークンの取得に失敗しました。ステータスコード: {response.status_code}"
|
|
68
|
+
try:
|
|
69
|
+
error_details = response.json()
|
|
70
|
+
error_message += f"\nレスポンス: {json.dumps(error_details, indent=2, ensure_ascii=False)}"
|
|
71
|
+
except json.JSONDecodeError:
|
|
72
|
+
error_message += f"\nレスポンス: {response.text}"
|
|
73
|
+
raise ValueError(error_message)
|
|
74
|
+
except requests.exceptions.RequestException as e:
|
|
75
|
+
handle_api_error(e)
|
|
76
|
+
raise ValueError("APIトークン取得リクエスト中にエラーが発生しました。")
|
|
77
|
+
|
|
78
|
+
@lru_cache(maxsize=None)
|
|
79
|
+
def get_headers(self):
|
|
80
|
+
return {"Accept": "application/json", "X-Auth-Token": self.token}
|
|
81
|
+
|
|
82
|
+
def get(self, path):
|
|
83
|
+
url = f"{self.api_base_url}{path}"
|
|
84
|
+
response = requests.get(url, headers=self.get_headers())
|
|
85
|
+
response.raise_for_status()
|
|
86
|
+
return response.json()
|
|
87
|
+
|
|
88
|
+
def post(self, path, payload):
|
|
89
|
+
url = f"{self.api_base_url}{path}"
|
|
90
|
+
response = requests.post(url, headers=self.get_headers(), json=payload)
|
|
91
|
+
response.raise_for_status()
|
|
92
|
+
return response.json()
|
|
93
|
+
|
|
94
|
+
def delete(self, path):
|
|
95
|
+
url = f"{self.api_base_url}{path}"
|
|
96
|
+
response = requests.delete(url, headers=self.get_headers())
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
# DELETEはボディが空のことがある
|
|
99
|
+
if response.status_code == 204:
|
|
100
|
+
return None
|
|
101
|
+
return response.json()
|
|
102
|
+
|
|
103
|
+
def put(self, path, payload):
|
|
104
|
+
url = f"{self.api_base_url}{path}"
|
|
105
|
+
response = requests.put(url, headers=self.get_headers(), json=payload)
|
|
106
|
+
response.raise_for_status()
|
|
107
|
+
return response.json()
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from functools import lru_cache
|
|
2
|
+
from .client import ConohaDNSClient
|
|
3
|
+
from .utils import normalize_domain, handle_api_error
|
|
4
|
+
import requests
|
|
5
|
+
import sys
|
|
6
|
+
import csv
|
|
7
|
+
|
|
8
|
+
from .id_converter import get_short_id
|
|
9
|
+
|
|
10
|
+
def is_short_id(s: str) -> bool:
|
|
11
|
+
"""短いID(8文字の16進数文字列)かどうかを判定する"""
|
|
12
|
+
if len(s) != 8:
|
|
13
|
+
return False
|
|
14
|
+
try:
|
|
15
|
+
int(s, 16)
|
|
16
|
+
return True
|
|
17
|
+
except ValueError:
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
class DomainManager:
|
|
21
|
+
def __init__(self, client: ConohaDNSClient):
|
|
22
|
+
self.client = client
|
|
23
|
+
|
|
24
|
+
@lru_cache(maxsize=1)
|
|
25
|
+
def _fetch_all_domains(self):
|
|
26
|
+
"""全ドメインの情報をAPIから取得する(キャッシュ付き)"""
|
|
27
|
+
return self.client.get("/v1/domains").get("domains", [])
|
|
28
|
+
|
|
29
|
+
def get_domain_id(self, identifier: str) -> str:
|
|
30
|
+
"""ドメイン名またはIDからドメインID(uuid)を検索して返す"""
|
|
31
|
+
domains = self._fetch_all_domains()
|
|
32
|
+
|
|
33
|
+
# 1. IDとして一致するものを探す
|
|
34
|
+
if is_short_id(identifier):
|
|
35
|
+
for domain in domains:
|
|
36
|
+
if get_short_id(domain['uuid']) == identifier:
|
|
37
|
+
return domain['uuid']
|
|
38
|
+
|
|
39
|
+
# 2. ドメイン名として一致するものを探す
|
|
40
|
+
normalized_name = normalize_domain(identifier)
|
|
41
|
+
for domain in domains:
|
|
42
|
+
if domain['name'] == normalized_name:
|
|
43
|
+
return domain['uuid']
|
|
44
|
+
|
|
45
|
+
raise ValueError(f"ドメイン '{identifier}' が見つかりませんでした。")
|
|
46
|
+
|
|
47
|
+
def get_domain_name_from_id(self, domain_id: str) -> str:
|
|
48
|
+
"""ドメインID(uuid)からドメイン名を検索して返す"""
|
|
49
|
+
domains = self._fetch_all_domains()
|
|
50
|
+
for domain in domains:
|
|
51
|
+
if domain['uuid'] == domain_id:
|
|
52
|
+
return domain['name']
|
|
53
|
+
raise ValueError(f"ドメインID '{domain_id}' が見つかりませんでした。")
|
|
54
|
+
|
|
55
|
+
def list_domains(self, output_format: str = None):
|
|
56
|
+
try:
|
|
57
|
+
domains = self._fetch_all_domains()
|
|
58
|
+
|
|
59
|
+
if output_format == 'csv':
|
|
60
|
+
print("ドメイン一覧をCSV形式で出力します...", file=sys.stderr)
|
|
61
|
+
if not domains:
|
|
62
|
+
print(" (ドメインはありません)", file=sys.stderr)
|
|
63
|
+
return
|
|
64
|
+
writer = csv.writer(sys.stdout)
|
|
65
|
+
writer.writerow(['ID', 'Name'])
|
|
66
|
+
for domain in domains:
|
|
67
|
+
short_id = get_short_id(domain['uuid'])
|
|
68
|
+
writer.writerow([short_id, domain['name']])
|
|
69
|
+
else:
|
|
70
|
+
if not domains:
|
|
71
|
+
print(" (ドメインはありません)")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
header = f" {'ID':<10} {'Name':<30}"
|
|
75
|
+
print(header)
|
|
76
|
+
for domain in domains:
|
|
77
|
+
short_id = get_short_id(domain['uuid'])
|
|
78
|
+
print(f" {short_id:<10} {domain['name']:<30}")
|
|
79
|
+
|
|
80
|
+
except requests.exceptions.RequestException as e:
|
|
81
|
+
handle_api_error(e)
|
|
82
|
+
except IOError as e:
|
|
83
|
+
print(f"ファイル書き込みエラー: {e}", file=sys.stderr)
|
|
84
|
+
|
|
85
|
+
def add_domain(self, name: str, email: str):
|
|
86
|
+
normalized_name = normalize_domain(name)
|
|
87
|
+
print(f"ドメイン '{normalized_name}' を追加しています...")
|
|
88
|
+
payload = {"name": normalized_name, "email": email}
|
|
89
|
+
try:
|
|
90
|
+
domain = self.client.post("/v1/domains", payload)
|
|
91
|
+
print("ドメインが正常に追加されました。")
|
|
92
|
+
print(f" ID: {domain['uuid']}, Name: {domain['name']}")
|
|
93
|
+
except requests.exceptions.RequestException as e:
|
|
94
|
+
handle_api_error(e)
|
|
95
|
+
|
|
96
|
+
def delete_domain(self, identifier: str):
|
|
97
|
+
try:
|
|
98
|
+
domain_id = self.get_domain_id(identifier)
|
|
99
|
+
domain_name = self.get_domain_name_from_id(domain_id)
|
|
100
|
+
print(f"ドメイン '{domain_name}' (ID: {domain_id}) を削除しています...")
|
|
101
|
+
self.client.delete(f"/v1/domains/{domain_id}")
|
|
102
|
+
print("ドメインが正常に削除されました。")
|
|
103
|
+
except (ValueError, requests.exceptions.RequestException) as e:
|
|
104
|
+
handle_api_error(e) if isinstance(e, requests.exceptions.RequestException) else print(f"エラー: {e}")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import xxhash
|
|
2
|
+
|
|
3
|
+
def get_short_id(uuid: str) -> str:
|
|
4
|
+
"""UUIDから短いハッシュIDを生成する"""
|
|
5
|
+
return xxhash.xxh64(uuid).hexdigest()[:8] # 8文字に短縮
|
|
6
|
+
|
|
7
|
+
def find_full_uuid(items: list, short_id: str) -> str:
|
|
8
|
+
"""アイテムのリストからshort_idに一致する完全なUUIDを見つける"""
|
|
9
|
+
for item in items:
|
|
10
|
+
if get_short_id(item['uuid']) == short_id:
|
|
11
|
+
return item['uuid']
|
|
12
|
+
raise ValueError(f"指定されたID '{short_id}' に一致するアイテムが見つかりませんでした。")
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from dotenv import load_dotenv
|
|
5
|
+
from .client import ConohaDNSClient
|
|
6
|
+
from .domain import DomainManager
|
|
7
|
+
from .record import RecordManager
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
load_dotenv(dotenv_path=os.path.expanduser("~/.conoha-env"))
|
|
11
|
+
epilog_text = """
|
|
12
|
+
使用例:
|
|
13
|
+
# APIトークンを認証・取得
|
|
14
|
+
conoha-dns --auth
|
|
15
|
+
|
|
16
|
+
# APIトークンを強制的に再認証・更新
|
|
17
|
+
conoha-dns --renew
|
|
18
|
+
|
|
19
|
+
# ドメイン一覧表示
|
|
20
|
+
conoha-dns -l
|
|
21
|
+
|
|
22
|
+
# レコード一覧をCSV形式で標準出力
|
|
23
|
+
conoha-dns -l example.com --output csv > records.csv
|
|
24
|
+
|
|
25
|
+
# Aレコード追加 (サブドメインtestを補完してtest.example.comを追加)
|
|
26
|
+
conoha-dns -ar example.com @ A 192.0.2.1
|
|
27
|
+
conoha-dns -ar example.com test A 192.0.2.1
|
|
28
|
+
|
|
29
|
+
# レコード更新 (レコードIDを指定し、新しいIPアドレスを設定)
|
|
30
|
+
conoha-dns -ur example.com <record_id> --new-data 192.0.2.2
|
|
31
|
+
|
|
32
|
+
# レコード削除 (レコードIDは -l example.com で確認)
|
|
33
|
+
conoha-dns -dr example.com <record_id>
|
|
34
|
+
"""
|
|
35
|
+
parser = argparse.ArgumentParser(
|
|
36
|
+
description="ConoHa DNS API (v1) を操作するCLIツール",
|
|
37
|
+
epilog=epilog_text,
|
|
38
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
39
|
+
)
|
|
40
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
41
|
+
|
|
42
|
+
# Actions
|
|
43
|
+
group.add_argument("--auth", action="store_true", help="APIトークンを認証・取得する")
|
|
44
|
+
group.add_argument("--renew", action="store_true", help="APIトークンを強制的に再認証・更新する")
|
|
45
|
+
group.add_argument("-l", "--list", nargs='?', const=True, default=None, metavar="DOMAIN", help="ドメイン一覧または指定ドメインのレコード一覧表示")
|
|
46
|
+
group.add_argument("-ad", "--add-domain", nargs=2, metavar=("NAME", "EMAIL"), help="ドメイン追加")
|
|
47
|
+
group.add_argument("-dd", "--delete-domain", metavar="DOMAIN", help="ドメインを名前またはIDで削除")
|
|
48
|
+
group.add_argument("-ar", "--add-record", nargs=4, metavar=("DOMAIN", "NAME", "TYPE", "DATA"), help="レコード追加")
|
|
49
|
+
group.add_argument("-ur", "--update-record", nargs=2, metavar=("DOMAIN", "RECORD_ID"), help="レコード更新")
|
|
50
|
+
group.add_argument("-dr", "--delete-record", nargs=2, metavar=("DOMAIN", "RECORD_ID"), help="レコード削除")
|
|
51
|
+
|
|
52
|
+
# General options
|
|
53
|
+
parser.add_argument("-t", "--ttl", type=int, default=300, help="レコード追加時のTTL(秒)。デフォルト: 300")
|
|
54
|
+
parser.add_argument("-o", "--output", choices=['csv'], help="出力形式をCSVにします。'-l'での一覧表示時のみ有効です。")
|
|
55
|
+
|
|
56
|
+
# Options for --update-record
|
|
57
|
+
parser.add_argument("--new-name", help="更新後のレコード名")
|
|
58
|
+
parser.add_argument("--new-type", help="更新後のレコードタイプ")
|
|
59
|
+
parser.add_argument("--new-data", help="更新後のレコードデータ")
|
|
60
|
+
parser.add_argument("--new-ttl", type=int, help="更新後のTTL")
|
|
61
|
+
|
|
62
|
+
args = parser.parse_args()
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
if args.renew:
|
|
66
|
+
client = ConohaDNSClient(renew=True)
|
|
67
|
+
if client.token:
|
|
68
|
+
print("APIトークンが正常に更新されました。")
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
if args.auth:
|
|
72
|
+
client = ConohaDNSClient()
|
|
73
|
+
if client.token:
|
|
74
|
+
print("認証に成功し、APIトークンが利用可能です。")
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
client = ConohaDNSClient()
|
|
78
|
+
domain_manager = DomainManager(client)
|
|
79
|
+
record_manager = RecordManager(client, domain_manager)
|
|
80
|
+
|
|
81
|
+
if args.list is not None:
|
|
82
|
+
output_format = args.output
|
|
83
|
+
if output_format and output_format != 'csv':
|
|
84
|
+
parser.error("現在サポートしている出力形式は'csv'のみです。")
|
|
85
|
+
|
|
86
|
+
if args.list is True:
|
|
87
|
+
domain_manager.list_domains(output_format=output_format)
|
|
88
|
+
else:
|
|
89
|
+
record_manager.list_records(args.list, output_format=output_format)
|
|
90
|
+
elif args.add_domain:
|
|
91
|
+
domain_manager.add_domain(args.add_domain[0], args.add_domain[1])
|
|
92
|
+
elif args.delete_domain:
|
|
93
|
+
domain_manager.delete_domain(args.delete_domain)
|
|
94
|
+
elif args.add_record:
|
|
95
|
+
record_manager.add_record(args.add_record[0], args.add_record[1], args.add_record[2], args.add_record[3], args.ttl)
|
|
96
|
+
elif args.update_record:
|
|
97
|
+
if not any([args.new_name, args.new_type, args.new_data, args.new_ttl is not None]):
|
|
98
|
+
parser.error("--update-recordには、--new-name, --new-type, --new-data, --new-ttl のいずれか1つ以上の指定が必要です。")
|
|
99
|
+
record_manager.update_record(
|
|
100
|
+
args.update_record[0],
|
|
101
|
+
args.update_record[1],
|
|
102
|
+
new_name=args.new_name,
|
|
103
|
+
new_type=args.new_type,
|
|
104
|
+
new_data=args.new_data,
|
|
105
|
+
new_ttl=args.new_ttl
|
|
106
|
+
)
|
|
107
|
+
elif args.delete_record:
|
|
108
|
+
record_manager.delete_record(args.delete_record[0], args.delete_record[1])
|
|
109
|
+
|
|
110
|
+
except ValueError as e:
|
|
111
|
+
print(f"エラー: {e}", file=sys.stderr)
|
|
112
|
+
sys.exit(3)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f"予期せぬエラーが発生しました: {e}", file=sys.stderr)
|
|
115
|
+
sys.exit(1)
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
main()
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from .client import ConohaDNSClient
|
|
2
|
+
from .domain import DomainManager
|
|
3
|
+
from .utils import normalize_record_name, handle_api_error
|
|
4
|
+
from .id_converter import get_short_id, find_full_uuid
|
|
5
|
+
import requests
|
|
6
|
+
import sys
|
|
7
|
+
import csv
|
|
8
|
+
|
|
9
|
+
class RecordManager:
|
|
10
|
+
def __init__(self, client: ConohaDNSClient, domain_manager: DomainManager):
|
|
11
|
+
self.client = client
|
|
12
|
+
self.domain_manager = domain_manager
|
|
13
|
+
|
|
14
|
+
def _get_all_records(self, domain_id: str) -> list:
|
|
15
|
+
return self.client.get(f"/v1/domains/{domain_id}/records").get("records", [])
|
|
16
|
+
|
|
17
|
+
def list_records(self, identifier: str, output_format: str = None):
|
|
18
|
+
try:
|
|
19
|
+
domain_id = self.domain_manager.get_domain_id(identifier)
|
|
20
|
+
domain_name = self.domain_manager.get_domain_name_from_id(domain_id).rstrip('.')
|
|
21
|
+
records = self._get_all_records(domain_id)
|
|
22
|
+
|
|
23
|
+
if output_format == 'csv':
|
|
24
|
+
if not records:
|
|
25
|
+
print(" (レコードはありません)", file=sys.stderr)
|
|
26
|
+
return
|
|
27
|
+
writer = csv.writer(sys.stdout)
|
|
28
|
+
writer.writerow(['ID', 'Name', 'Type', 'Data', 'TTL'])
|
|
29
|
+
for record in records:
|
|
30
|
+
short_id = get_short_id(record['uuid'])
|
|
31
|
+
writer.writerow([short_id, record['name'], record['type'], record['data'], record['ttl']])
|
|
32
|
+
else:
|
|
33
|
+
if not records:
|
|
34
|
+
print(" (レコードはありません)")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
# カラムの最大幅を計算 (データは省略しない)
|
|
38
|
+
max_name = max(len(r['name']) for r in records) if records else 4
|
|
39
|
+
max_type = max(len(r['type']) for r in records) if records else 4
|
|
40
|
+
max_data = max(len(str(r['data'])) for r in records) if records else 4
|
|
41
|
+
|
|
42
|
+
header = f" {'ID':<10} {'Name':<{max_name}} {'Type':<{max_type}} {'TTL':<6} {'Data'}"
|
|
43
|
+
print(header)
|
|
44
|
+
|
|
45
|
+
for record in records:
|
|
46
|
+
short_id = get_short_id(record['uuid'])
|
|
47
|
+
print(f" {short_id:<10} {record['name']:<{max_name}} {record['type']:<{max_type}} {str(record['ttl']):<6} {record['data']}")
|
|
48
|
+
|
|
49
|
+
except (ValueError, requests.exceptions.RequestException) as e:
|
|
50
|
+
handle_api_error(e) if isinstance(e, requests.exceptions.RequestException) else print(f"エラー: {e}", file=sys.stderr)
|
|
51
|
+
except IOError as e:
|
|
52
|
+
print(f"ファイル書き込みエラー: {e}", file=sys.stderr)
|
|
53
|
+
|
|
54
|
+
def add_record(self, identifier: str, name: str, type: str, data: str, ttl: int):
|
|
55
|
+
try:
|
|
56
|
+
domain_id = self.domain_manager.get_domain_id(identifier)
|
|
57
|
+
domain_name = self.domain_manager.get_domain_name_from_id(domain_id)
|
|
58
|
+
normalized_name = normalize_record_name(domain_name, name)
|
|
59
|
+
|
|
60
|
+
print(f"ドメイン '{domain_name}' にレコード '{normalized_name}' を追加しています...")
|
|
61
|
+
payload = {"name": normalized_name, "type": type, "data": data, "ttl": ttl}
|
|
62
|
+
record = self.client.post(f"/v1/domains/{domain_id}/records", payload)
|
|
63
|
+
short_id = get_short_id(record['uuid'])
|
|
64
|
+
print("レコードが正常に追加されました。")
|
|
65
|
+
print(f" ID: {short_id}, Name: {record['name']}, Type: {record['type']}, Data: {record['data']}")
|
|
66
|
+
except (ValueError, requests.exceptions.RequestException) as e:
|
|
67
|
+
handle_api_error(e) if isinstance(e, requests.exceptions.RequestException) else print(f"エラー: {e}")
|
|
68
|
+
|
|
69
|
+
def delete_record(self, identifier: str, short_record_id: str):
|
|
70
|
+
try:
|
|
71
|
+
domain_id = self.domain_manager.get_domain_id(identifier)
|
|
72
|
+
domain_name = self.domain_manager.get_domain_name_from_id(domain_id)
|
|
73
|
+
all_records = self._get_all_records(domain_id)
|
|
74
|
+
full_record_id = find_full_uuid(all_records, short_record_id)
|
|
75
|
+
|
|
76
|
+
print(f"ドメイン '{domain_name}' からレコードID '{short_record_id}' (UUID: {full_record_id}) を削除しています...")
|
|
77
|
+
self.client.delete(f"/v1/domains/{domain_id}/records/{full_record_id}")
|
|
78
|
+
print("レコードが正常に削除されました。")
|
|
79
|
+
except (ValueError, requests.exceptions.RequestException) as e:
|
|
80
|
+
handle_api_error(e) if isinstance(e, requests.exceptions.RequestException) else print(f"エラー: {e}")
|
|
81
|
+
|
|
82
|
+
def get_record(self, domain_id: str, record_id: str):
|
|
83
|
+
return self.client.get(f"/v1/domains/{domain_id}/records/{record_id}")
|
|
84
|
+
|
|
85
|
+
def update_record(self, identifier: str, short_record_id: str, new_name: str = None, new_type: str = None, new_data: str = None, new_ttl: int = None):
|
|
86
|
+
try:
|
|
87
|
+
domain_id = self.domain_manager.get_domain_id(identifier)
|
|
88
|
+
domain_name = self.domain_manager.get_domain_name_from_id(domain_id)
|
|
89
|
+
all_records = self._get_all_records(domain_id)
|
|
90
|
+
full_record_id = find_full_uuid(all_records, short_record_id)
|
|
91
|
+
|
|
92
|
+
print(f"レコードID '{short_record_id}' (UUID: {full_record_id}) の現在の情報を取得しています...")
|
|
93
|
+
current_record = self.get_record(domain_id, full_record_id)
|
|
94
|
+
|
|
95
|
+
name_to_update = new_name if new_name is not None else current_record['name']
|
|
96
|
+
normalized_name = normalize_record_name(domain_name, name_to_update)
|
|
97
|
+
|
|
98
|
+
payload = {
|
|
99
|
+
"name": normalized_name,
|
|
100
|
+
"type": new_type if new_type is not None else current_record['type'],
|
|
101
|
+
"data": new_data if new_data is not None else current_record['data'],
|
|
102
|
+
"ttl": new_ttl if new_ttl is not None else current_record.get('ttl'),
|
|
103
|
+
"description": current_record.get('description'),
|
|
104
|
+
"priority": current_record.get('priority')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if payload['ttl'] is None:
|
|
108
|
+
del payload['ttl']
|
|
109
|
+
|
|
110
|
+
print(f"レコードID '{short_record_id}' を更新しています...")
|
|
111
|
+
updated_record = self.client.put(f"/v1/domains/{domain_id}/records/{full_record_id}", payload)
|
|
112
|
+
|
|
113
|
+
short_id = get_short_id(updated_record['uuid'])
|
|
114
|
+
print("レコードが正常に更新されました。")
|
|
115
|
+
print(f" ID: {short_id}, Name: {updated_record['name']}, Type: {updated_record['type']}, Data: {updated_record['data']}, TTL: {updated_record.get('ttl')}")
|
|
116
|
+
|
|
117
|
+
except (ValueError, requests.exceptions.RequestException) as e:
|
|
118
|
+
handle_api_error(e) if isinstance(e, requests.exceptions.RequestException) else print(f"エラー: {e}")
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
def normalize_domain(domain_name: str) -> str:
|
|
6
|
+
"""ドメイン名の末尾にドットを追加する"""
|
|
7
|
+
if not domain_name.endswith('.'):
|
|
8
|
+
return domain_name + '.'
|
|
9
|
+
return domain_name
|
|
10
|
+
|
|
11
|
+
def normalize_record_name(domain_name: str, record_name: str) -> str:
|
|
12
|
+
"""レコード名を正規化する"""
|
|
13
|
+
if record_name == '@':
|
|
14
|
+
return normalize_domain(domain_name)
|
|
15
|
+
|
|
16
|
+
clean_domain_name = domain_name.rstrip('.')
|
|
17
|
+
if not record_name.endswith(clean_domain_name):
|
|
18
|
+
record_name = f"{record_name}.{clean_domain_name}"
|
|
19
|
+
|
|
20
|
+
return normalize_domain(record_name)
|
|
21
|
+
|
|
22
|
+
def handle_api_error(e):
|
|
23
|
+
"""APIエラーを処理し、情報を出力する"""
|
|
24
|
+
print(f"エラー: 要求に失敗しました。", file=sys.stderr)
|
|
25
|
+
if isinstance(e, requests.exceptions.RequestException):
|
|
26
|
+
if hasattr(e, 'response') and e.response is not None:
|
|
27
|
+
print(f"ステータスコード: {e.response.status_code}", file=sys.stderr)
|
|
28
|
+
try:
|
|
29
|
+
print(f"レスポンス: {json.dumps(e.response.json(), indent=2, ensure_ascii=False)}", file=sys.stderr)
|
|
30
|
+
except json.JSONDecodeError:
|
|
31
|
+
print(f"レスポンス: {e.response.text}", file=sys.stderr)
|
|
32
|
+
else:
|
|
33
|
+
print(e, file=sys.stderr)
|
|
34
|
+
else:
|
|
35
|
+
print(e, file=sys.stderr)
|
|
36
|
+
sys.exit(4)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: conoha-dns-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool to manage ConoHa DNS.
|
|
5
|
+
Author-email: haturatu <taro@eyes4you.org>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: python-dotenv
|
|
10
|
+
Requires-Dist: requests
|
|
11
|
+
Requires-Dist: xxhash
|
|
12
|
+
|
|
13
|
+
# ConoHa DNS CLI
|
|
14
|
+
|
|
15
|
+
ConoHa DNSをコマンドラインから操作するためのツールです。
|
|
16
|
+
|
|
17
|
+
デフォルトでは見やすいテーブル形式で一覧表示し、`--output csv` オプションを指定することでCSV形式で標準出力することもできます。
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
$ conoha-dns -h
|
|
21
|
+
usage: main.py [-h]
|
|
22
|
+
(--auth | -l [DOMAIN] | -ad NAME EMAIL | -dd DOMAIN | -ar DOMAIN NAME TYPE DATA | -ur DOMAIN RECORD_ID | -dr DOMAIN RECORD_ID)
|
|
23
|
+
[-t TTL] [-o {csv}] [--new-name NEW_NAME] [--new-type NEW_TYPE]
|
|
24
|
+
[--new-data NEW_DATA] [--new-ttl NEW_TTL]
|
|
25
|
+
|
|
26
|
+
ConoHa DNS API (v1) を操作するCLIツール
|
|
27
|
+
|
|
28
|
+
options:
|
|
29
|
+
-h, --help show this help message and exit
|
|
30
|
+
--auth APIトークンを認証・取得する
|
|
31
|
+
-l [DOMAIN], --list [DOMAIN]
|
|
32
|
+
ドメイン一覧または指定ドメインのレコード一覧表示
|
|
33
|
+
-ad NAME EMAIL, --add-domain NAME EMAIL
|
|
34
|
+
ドメイン追加
|
|
35
|
+
-dd DOMAIN, --delete-domain DOMAIN
|
|
36
|
+
ドメインを名前またはIDで削除
|
|
37
|
+
-ar DOMAIN NAME TYPE DATA, --add-record DOMAIN NAME TYPE DATA
|
|
38
|
+
レコード追加
|
|
39
|
+
-ur DOMAIN RECORD_ID, --update-record DOMAIN RECORD_ID
|
|
40
|
+
レコード更新
|
|
41
|
+
-dr DOMAIN RECORD_ID, --delete-record DOMAIN RECORD_ID
|
|
42
|
+
レコード削除
|
|
43
|
+
-t TTL, --ttl TTL レコード追加時のTTL(秒)。デフォルト: 300
|
|
44
|
+
-o {csv}, --output {csv}
|
|
45
|
+
出力形式をCSVにします。'-l'での一覧表示時のみ有効です。
|
|
46
|
+
--new-name NEW_NAME 更新後のレコード名
|
|
47
|
+
--new-type NEW_TYPE 更新後のレコードタイプ
|
|
48
|
+
--new-data NEW_DATA 更新後のレコードデータ
|
|
49
|
+
--new-ttl NEW_TTL 更新後のTTL
|
|
50
|
+
|
|
51
|
+
使用例:
|
|
52
|
+
# APIトークンを認証・取得
|
|
53
|
+
conoha-dns --auth
|
|
54
|
+
|
|
55
|
+
# ドメイン一覧表示
|
|
56
|
+
conoha-dns -l
|
|
57
|
+
|
|
58
|
+
# レコード一覧をCSV形式で標準出力
|
|
59
|
+
conoha-dns -l example.com --output csv > records.csv
|
|
60
|
+
|
|
61
|
+
# Aレコード追加 (サブドメインtestを補完してtest.example.comを追加)
|
|
62
|
+
conoha-dns -ar example.com @ A 192.0.2.1
|
|
63
|
+
conoha-dns -ar example.com test A 192.0.2.1
|
|
64
|
+
|
|
65
|
+
# レコード更新 (レコードIDを指定し、新しいIPアドレスを設定)
|
|
66
|
+
conoha-dns -ur example.com <record_id> --new-data 192.0.2.2
|
|
67
|
+
|
|
68
|
+
# レコード削除 (レコードIDは -l example.com で確認)
|
|
69
|
+
conoha-dns -dr example.com <record_id>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## インストール
|
|
73
|
+
|
|
74
|
+
はじめに、ConoHaの認証情報を記述した`~/.conoha-env`ファイルをホームディレクトリに作成します。
|
|
75
|
+
APIエンドポイントはConoHaのリージョンによって異なるため、適宜変更してください。
|
|
76
|
+
[ConoHaコントロールパネル](https://cp.conoha.jp/VPS/API/) の「API情報」から確認できます。
|
|
77
|
+
|
|
78
|
+
**~/.conoha-env**:
|
|
79
|
+
```
|
|
80
|
+
CONOHA_USER_ID="your_user_id" # API ユーザー: ユーザID
|
|
81
|
+
CONOHA_PASSWORD="your_password" # API ユーザー: パスワード
|
|
82
|
+
TENANT_ID="your_tenant_id" # テナント情報: テナントID
|
|
83
|
+
CONOHA_AUTH_URL="https://identity.sample.conoha.io" # エンドポイント: Identity Service URL
|
|
84
|
+
CONOHA_DNS_API_URL="https://dns-service.sample.conoha.io" # エンドポイント: DNS Service URL
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
次に、Makefileを使ってビルドとインストールを実行します。
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
make install
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
これにより、`conoha-dns`コマンドがインストールされます。
|
|
94
|
+
|
|
95
|
+
## 使い方
|
|
96
|
+
|
|
97
|
+
このコマンドは、実行したい操作をフラグで指定します。一度に指定できる操作は1つだけです。
|
|
98
|
+
ドメインを指定する引数 (DOMAIN) には、ドメイン名 (`example.com`) またはドメインID (`ba9b5b9d`など) の両方を使用できます。
|
|
99
|
+
|
|
100
|
+
### 一般的なコマンド
|
|
101
|
+
|
|
102
|
+
**ヘルプ表示**
|
|
103
|
+
```bash
|
|
104
|
+
conoha-dns -h
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**認証**
|
|
108
|
+
認証情報を使って新しいAPIトークンを取得し、`~/.conoha-env`ファイルに保存します。
|
|
109
|
+
なお、ConoHa APIトークンの有効期限は24時間です。
|
|
110
|
+
```bash
|
|
111
|
+
conoha-dns --auth
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### ドメイン・レコード管理
|
|
115
|
+
|
|
116
|
+
**ドメイン一覧・レコード一覧**
|
|
117
|
+
|
|
118
|
+
`-l` フラグを使用すると、一覧がテーブル形式で表示されます。
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# ドメイン一覧
|
|
122
|
+
conoha-dns -l
|
|
123
|
+
|
|
124
|
+
# レコード一覧 (ドメイン名またはIDで指定)
|
|
125
|
+
conoha-dns -l <ドメイン名/ID>
|
|
126
|
+
```
|
|
127
|
+
*実行例:*
|
|
128
|
+
```bash
|
|
129
|
+
$ conoha-dns -l
|
|
130
|
+
ID Name
|
|
131
|
+
0e6f0695 soulminingrig.com.
|
|
132
|
+
4fb208de eyes4you.org.
|
|
133
|
+
...
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**CSV形式での標準出力**
|
|
137
|
+
|
|
138
|
+
`--output csv` オプションを追加すると、一覧をCSV形式で標準出力します。リダイレクト(`>`)を使えばファイルに保存できます。
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# ドメイン一覧をCSVで標準出力
|
|
142
|
+
conoha-dns -l --output csv
|
|
143
|
+
|
|
144
|
+
# レコード一覧をCSVファイルとして保存
|
|
145
|
+
conoha-dns -l <ドメイン名/ID> --output csv > records.csv
|
|
146
|
+
```
|
|
147
|
+
*実行例:*
|
|
148
|
+
```bash
|
|
149
|
+
$ conoha-dns -l example.com --output csv
|
|
150
|
+
ID,Name,Type,Data,TTL
|
|
151
|
+
5057adde,eye4u.org.,SOA,"a.conoha-dns.com. taro.eyes4you.org. 1757833524 3600 600 86400 3600",3600
|
|
152
|
+
2eff1245,eye4u.org.,NS,a.conoha-dns.com.,3600
|
|
153
|
+
...
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**ドメイン追加**
|
|
157
|
+
```bash
|
|
158
|
+
conoha-dns -ad <ドメイン名> <メールアドレス>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**ドメイン削除**
|
|
162
|
+
```bash
|
|
163
|
+
conoha-dns -dd <ドメイン名/ID>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### レコード管理
|
|
167
|
+
|
|
168
|
+
内部的に、ConoHa APIから取得したレコードIDは長いため、xxhashを用いて短いハッシュ値に変換して表示・利用しています。
|
|
169
|
+
|
|
170
|
+
**レコード追加**
|
|
171
|
+
```bash
|
|
172
|
+
conoha-dns -ar <ドメイン名/ID> <レコード名> <種別> <値> [--ttl <秒数>]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**レコード更新**
|
|
176
|
+
`record_id`(`conoha-dns -l <ドメイン名/ID>`で確認可能)と、少なくとも1つの`--new-*`オプションを指定する必要があります。
|
|
177
|
+
```bash
|
|
178
|
+
conoha-dns -ur <ドメイン名/ID> <record_id> [--new-name <名前>] [--new-type <種別>] [--new-data <値>] [--new-ttl <TTL>]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**レコード削除**
|
|
182
|
+
```bash
|
|
183
|
+
conoha-dns -dr <ドメイン名/ID> <record_id>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 応用例:xargsを使った一括操作
|
|
187
|
+
|
|
188
|
+
`xargs`と組み合わせることで、ファイルに記載されたドメインリストに対して一括で操作を実行できます。
|
|
189
|
+
|
|
190
|
+
例えば、以下のようなドメインリストが書かれた`domain-list.txt`があるとします。
|
|
191
|
+
|
|
192
|
+
**domain-list.txt**
|
|
193
|
+
```
|
|
194
|
+
example1.com
|
|
195
|
+
example2.com
|
|
196
|
+
example3.com
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
このファイル内の各ドメインに対して、一括でルートAレコードを追加するには、以下のコマンドを実行します。
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
cat domain-list.txt | xargs -I{} conoha-dns -ar {} @ A 192.0.2.1
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
これにより、`domain-list.txt`の各行が`{}`に代入され、ドメインごとにレコード追加コマンドが実行されます。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/conoha_dns_cli/__init__.py
|
|
4
|
+
src/conoha_dns_cli/client.py
|
|
5
|
+
src/conoha_dns_cli/domain.py
|
|
6
|
+
src/conoha_dns_cli/id_converter.py
|
|
7
|
+
src/conoha_dns_cli/main.py
|
|
8
|
+
src/conoha_dns_cli/record.py
|
|
9
|
+
src/conoha_dns_cli/utils.py
|
|
10
|
+
src/conoha_dns_cli.egg-info/PKG-INFO
|
|
11
|
+
src/conoha_dns_cli.egg-info/SOURCES.txt
|
|
12
|
+
src/conoha_dns_cli.egg-info/dependency_links.txt
|
|
13
|
+
src/conoha_dns_cli.egg-info/entry_points.txt
|
|
14
|
+
src/conoha_dns_cli.egg-info/requires.txt
|
|
15
|
+
src/conoha_dns_cli.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
conoha_dns_cli
|