Skip to content

Commit

Permalink
Merge pull request #3 from sakura-internet/feat/register_sim
Browse files Browse the repository at this point in the history
"SIM一括登録"コマンドを追加
  • Loading branch information
f-okuhara authored Apr 16, 2024
2 parents 7863e2e + 59646af commit 77e4599
Show file tree
Hide file tree
Showing 21 changed files with 1,058 additions and 7 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@

# コマンド一覧

- [モバイルゲートウェイ内のSIMに割り当て可能なIPアドレスを出力(get_unused_ip)](./get_unused_ip)
- [モバイルゲートウェイ内のSIMに割り当て可能なIPアドレスを出力(get_unused_ip)](./get_unused_ip)
- [CSVファイルからSIM一括登録(register_sim)](./register_sim)
295 changes: 289 additions & 6 deletions common/common.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package common

import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
Expand All @@ -22,17 +23,24 @@ type SimAPIResponse struct {
Count int `json:"Count"`
}

// GetUsedIPAddressesInMGW
// MGW 内での利用されている IP アドレス一覧を取得する
// 利用されている IPアドレス一覧を取得
func GetUsedIPAddressesInMGW(accessToken string, accessTokenSecret string, zone string, mgwID string) (map[string]struct{}, error) {
// ベーシック認証
auth := fmt.Sprintf("%s:%s", accessToken, accessTokenSecret)
// BASIC認証のAuthorizationヘッダが設定されたhttp.Header{}インスタンスを作成
func createHeadersWithBasicAuth(user string, password string) http.Header {
auth := fmt.Sprintf("%s:%s", user, password)
encodedAuth := base64.StdEncoding.EncodeToString([]byte(auth))
headers := http.Header{
"Authorization": []string{"Basic " + encodedAuth},
}

return headers
}

// GetUsedIPAddressesInMGW
// MGW 内での利用されている IP アドレス一覧を取得する
// 利用されている IPアドレス一覧を取得
func GetUsedIPAddressesInMGW(accessToken string, accessTokenSecret string, zone string, mgwID string) (map[string]struct{}, error) {
// BASIC認証
headers := createHeadersWithBasicAuth(accessToken, accessTokenSecret)

// モバイルゲートウェイ配下のSIMを全件取得するための URL の組み立て
baseURL := fmt.Sprintf("https://secure.sakura.ad.jp/cloud/zone/%s/api/cloud/1.1/appliance/%s/mobilegateway/sims", zone, mgwID)
queryParams := url.Values{}
Expand Down Expand Up @@ -152,3 +160,278 @@ func incrementIP(ip net.IP) {
}
}
}

type SimRegisterInfo struct {
ICCID string
PassCode string
}

// SIM作成APIのレスポンス
type SimCreateAPIResponse struct {
CommonServiceItem struct {
ID string `json:"ID"`
}
Success bool `json:"Success"`
IsOK bool `json:"is_ok"`
}

// APIのis_okレスポンス
type SimApiIsOkResponse struct {
IsOK bool `json:"is_ok"`
}

// APIのis_fatalレスポンス
type SimApiIsFatalResponse struct {
IsFatal bool `json:"is_fatal"`
Serial string `json:"serial"`
Status string `json:"status"`
ErrorCode string `json:"error_code"`
ErrorMsg string `json:"error_msg"`
}

// SIMの作成
func createSim(accessToken string, accessTokenSecret string, simID string, simPasscode string) (string, error) {
// BASIC認証
headers := createHeadersWithBasicAuth(accessToken, accessTokenSecret)

// SIM作成リクエストの組み立て
baseURL := "https://secure.sakura.ad.jp/cloud/zone/is1a/api/cloud/1.1/commonserviceitem"
body := fmt.Sprintf(`
{
"CommonServiceItem": {
"Name": "%s",
"Status": {
"ICCID": "%s"
},
"Remark": {
"PassCode": "%s"
},
"Provider": {
"Class": "sim"
}
}
}`, simID, simID, simPasscode)
bytesBody := []byte(body)
bufBody := bytes.NewBuffer(bytesBody)

// リクエスト送信
req, err := http.NewRequest("POST", baseURL, bufBody)
if err != nil {
return "", fmt.Errorf("HTTPクライアントの初期化に失敗しました...%s", err.Error())
}
req.Header = headers

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("HTTPクライアントの実行に失敗しました...%s", err.Error())
}
defer resp.Body.Close()

// レスポンスの確認
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("SIM作成のレスポンスの読み込みに失敗しました...%s", err.Error())
}
if resp.StatusCode != http.StatusCreated {
// 認証情報の間違い
if resp.StatusCode == http.StatusUnauthorized {
return "", fmt.Errorf("アクセストークン、アクセストークンシークレットを確認してください。SIM作成に失敗しました")
}

// 作成済み
if resp.StatusCode == http.StatusConflict {
// エラーにしないけどリソースIDは空
return "", nil
}

// 認証エラーじゃない場合
var apiFatalRes SimApiIsFatalResponse
err = json.Unmarshal(respBody, &apiFatalRes)
if err != nil {
return "", fmt.Errorf("SIM作成のレスポンスのパースに失敗しました...%s", err.Error())
}
// レスポンスに含まれているエラーメッセージを返す
return "", fmt.Errorf("%s: (%s)%s", apiFatalRes.Serial, apiFatalRes.Status, apiFatalRes.ErrorMsg)
}

var apiResponse SimCreateAPIResponse
err = json.Unmarshal(respBody, &apiResponse)
if err != nil {
return "", fmt.Errorf("SIM作成のレスポンスのパースに失敗しました...%s", err.Error())
}

// SIMのリソースIDを返す
return apiResponse.CommonServiceItem.ID, nil
}

// SIMのIPアドレス設定
func assignIPAddressToSim(accessToken string, accessTokenSecret string, simID string, ipAddress string) error {
// BASIC認証
headers := createHeadersWithBasicAuth(accessToken, accessTokenSecret)

// 『SIMのIPアドレス指定』のリクエストの組み立て
baseURL := fmt.Sprintf("https://secure.sakura.ad.jp/cloud/zone/is1a/api/cloud/1.1/commonserviceitem/%s/sim/ip", simID)
body := fmt.Sprintf(`{
"sim": {
"ip": "%s"
}}`, ipAddress)
bytesBody := []byte(body)
bufBody := bytes.NewBuffer(bytesBody)

// リクエスト送信
req, err := http.NewRequest("PUT", baseURL, bufBody)
if err != nil {
return fmt.Errorf("HTTPクライアントの初期化に失敗しました...%s", err.Error())
}
req.Header = headers

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("HTTPクライアントの実行に失敗しました...%s", err.Error())
}
defer resp.Body.Close()

// レスポンスの確認
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("SIMのIPアドレス設定のレスポンスの読み込みに失敗しました...%s", err.Error())
}
if resp.StatusCode != http.StatusOK {
// 認証情報の間違い
if resp.StatusCode == http.StatusUnauthorized {
return fmt.Errorf("アクセストークン、アクセストークンシークレットを確認してください。SIMのIPアドレス設定に失敗しました")
}
// 認証エラーじゃない場合
var apiFatalRes SimApiIsFatalResponse
err = json.Unmarshal(respBody, &apiFatalRes)
if err != nil {
return fmt.Errorf("SIMのIPアドレス設定のレスポンスのパースに失敗しました...%s", err.Error())
}
// レスポンスに含まれているエラーメッセージを返す
return fmt.Errorf("%s: (%s)%s", apiFatalRes.Serial, apiFatalRes.Status, apiFatalRes.ErrorMsg)
}

// 設定の成否を返す
var apiOkRes SimApiIsOkResponse
err = json.Unmarshal(respBody, &apiOkRes)
if err != nil {
return fmt.Errorf("SIMのIPアドレス設定のレスポンスのパースに失敗しました...%s", err.Error())
}

if !apiOkRes.IsOK {
return fmt.Errorf("SIMのIPアドレス設定が失敗しました")
}

return nil
}

// モバイルゲートウェイにSIMを登録
func assignSimToMgw(accessToken string, accessTokenSecret string, zone string, mgwID string, simID string) error {
// BASIC認証
headers := createHeadersWithBasicAuth(accessToken, accessTokenSecret)

// 『モバイルゲートウェイにSIMを登録』のリクエストの組み立て
baseURL := fmt.Sprintf("https://secure.sakura.ad.jp/cloud/zone/%s/api/cloud/1.1/appliance/%s/mobilegateway/sims", zone, mgwID)
body := fmt.Sprintf(`{
"sim": {
"resource_id": "%s"
}}`, simID)
bytesBody := []byte(body)
bufBody := bytes.NewBuffer(bytesBody)

// リクエスト送信
req, err := http.NewRequest("POST", baseURL, bufBody)
if err != nil {
return fmt.Errorf("HTTPクライアントの初期化に失敗しました...%s", err.Error())
}
req.Header = headers

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("HTTPクライアントの実行に失敗しました...%s", err.Error())
}
defer resp.Body.Close()

// レスポンスの確認
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("モバイルゲートウェイにSIMを登録のレスポンスの読み込みに失敗しました...%s", err.Error())
}

if resp.StatusCode != http.StatusOK {
// 認証情報の間違い
if resp.StatusCode == http.StatusUnauthorized {
return fmt.Errorf("認証に失敗しました...%s", err.Error())
}
// 認証エラーじゃない場合
var apiFatalRes SimApiIsFatalResponse
err = json.Unmarshal(respBody, &apiFatalRes)
if err != nil {
return fmt.Errorf("モバイルゲートウェイにSIMを登録のレスポンスのパースに失敗しました...%s", err.Error())
}
// レスポンスに含まれているエラーメッセージを返す
return fmt.Errorf("%s: (%s)%s", apiFatalRes.Serial, apiFatalRes.Status, apiFatalRes.ErrorMsg)
}

// 設定の成否を返す
var apiOkRes SimApiIsOkResponse
err = json.Unmarshal(respBody, &apiOkRes)
if err != nil {
return fmt.Errorf("モバイルゲートウェイにSIMを登録のレスポンスのパースに失敗しました...%s", err.Error())
}

if !apiOkRes.IsOK {
return fmt.Errorf("SIMのIPアドレス設定が失敗しました")
}

return nil
}

//  リスト内のSIMを登録する
func RegisterSimFromList(accessToken string, accessTokenSecret string, zone string, mgwID string, simList []SimRegisterInfo, ipList []string) error {

if len(simList) > len(ipList) {
return fmt.Errorf("登録対象のSIM %d 枚に対して割り当て可能なIPアドレスが %d 個しかありません", len(simList), len(ipList))
}

ipListIndex := 0
for _, sim := range simList {
// SIMを作成
fmt.Printf("SIM登録(ICCID: %s)", sim.ICCID)
simResourceId, err := createSim(accessToken, accessTokenSecret, sim.ICCID, sim.PassCode)
if err != nil {
fmt.Printf("[FAILED]\n")
return err
}
if simResourceId == "" {
//登録済みだからスキップ
fmt.Printf("[SKIP]\n")
continue
}
fmt.Printf("[OK]")

// MGWにSIMを登録
fmt.Printf(", モバイルゲートウェイに追加")
err = assignSimToMgw(accessToken, accessTokenSecret, zone, mgwID, simResourceId)
if err != nil {
fmt.Printf("[FAILED]\n")
return err
}
fmt.Printf("[OK]")

// SIMにIPアドレスを設定
fmt.Printf(", IPアドレスを設定(%s)", ipList[ipListIndex])
err = assignIPAddressToSim(accessToken, accessTokenSecret, simResourceId, ipList[ipListIndex])
if err != nil {
fmt.Printf("[FAILED]\n")
return err
}
fmt.Printf("[OK]\n")

ipListIndex++
}
return nil
}
39 changes: 39 additions & 0 deletions register_sim/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
APP_NAME := register_sim

VERSION ?= latest

BINARIES := \
bin/$(APP_NAME)-$(VERSION)-linux-amd64 \
bin/$(APP_NAME)-$(VERSION)-linux-arm64 \
bin/$(APP_NAME)-$(VERSION)-darwin-amd64 \
bin/$(APP_NAME)-$(VERSION)-darwin-arm64 \
bin/$(APP_NAME)-$(VERSION)-windows-amd64.exe \
bin/$(APP_NAME)-$(VERSION)-windows-arm64.exe

all: $(BINARIES)

bin/$(APP_NAME)-$(VERSION)-linux-amd64:
GOOS=linux GOARCH=amd64 go build -o $@

bin/$(APP_NAME)-$(VERSION)-linux-arm64:
GOOS=linux GOARCH=arm64 go build -o $@

bin/$(APP_NAME)-$(VERSION)-darwin-amd64:
GOOS=darwin GOARCH=amd64 go build -o $@

bin/$(APP_NAME)-$(VERSION)-darwin-arm64:
GOOS=darwin GOARCH=arm64 go build -o $@

bin/$(APP_NAME)-$(VERSION)-windows-amd64.exe:
GOOS=windows GOARCH=amd64 go build -o $@

bin/$(APP_NAME)-$(VERSION)-windows-arm64.exe:
GOOS=windows GOARCH=arm64 go build -o $@

zip: all
zip -j bin/$(APP_NAME)-$(VERSION)-all.zip $(BINARIES)

clean:
rm -r bin

.PHONY: all clean
Loading

0 comments on commit 77e4599

Please sign in to comment.