Cisco IMC(CIMC)のエミュレータにGoでAPIを実行してみた

Cisco IMC(CIMC)[^1] のEmulatorをインストールしてpythonからログイン・ログアウトのAPIを実行してみたのですが、XMLや構造体(メソッド)などの勉強としてGoでも同じことをやってみたので備忘録として書いておきます。

# CIMC Emulatorインストールについて

CIMCやインストール方法については以下を参照ください。

https://sky-joker.tech/2017/10/08/cisco-imccimcのエミュレータを使ってみた

# CIMC API Document

https://www.cisco.com/c/ja_jp/td/docs/ucs/ucsc-integratedmgmtcntrller/ucsc-integratedmgmtcntrll/pug/001/b_cimc_api_book/b_cimc_api_book_preface_00.html

# CIMCのAPIを使いたかったきっかけ

CIMC経由でサーバの監視や管理を自動化したかった

# GoでCIMCにログイン・ログアウト

## ソース

```go
package main

import (
	"crypto/tls"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/urfave/cli"
)

// ログインのXML構造体
type aaaLogin struct {
	InName     string `xml:"inName,attr"`
	InPassword string `xml:"inPassword,attr"`
}

// ログアウトのXML構造体
type aaaLogout struct {
	Cookie   string `xml:"cookie,attr"`
	InCookie string `xml:"inCookie,attr"`
}

// Login CIMCへログインしてCookieを取得するメソッド
func (data *aaaLogin) Login(url string) (cookie string, err error) {
	// ログインレスポンスのBodyで得られるXMLの構造体
	type aaaLogin struct {
		Cookie           string `xml:"cookie,attr"`
		Response         string `xml:"response,attr"`
		OutCookie        string `xml:"outCookie,attr"`
		OutRefreshPeriod int32  `xml:"outRefreshPreiod,attr"`
		OutPriv          string `xml:"outPriv,attr"`
		OutSessionID     int32  `xml:"outSessionId,attr"`
		OutVersion       string `xml:"outVersion,attr"`
	}

	// ログイン用XMLに変換
	reqXML, err := xml.Marshal(data)
	if err != nil {
		return "", err
	}

	// Bodyを取得
	body, err := PostRequest(url, reqXML)
	if err != nil {
		return "", err
	}

	// Cookieを戻す
	resData := new(aaaLogin)
	if err := xml.Unmarshal(body, resData); err != nil {
		return "", err
	}
	return resData.OutCookie, nil
}

// Logout CIMCからログアウトするメソッド
func (data *aaaLogout) Logout(url string) (status string, err error) {
	// ログアウトレスポンスのBodyで得られるXMLの構造体
	type aaaLogout struct {
		Cookie    string `xml:"cookie,attr"`
		Response  string `xml:"response,attr"`
		OutStatus string `xml:"outStatus,attr"`
	}

	// ログアウト用XMLに変換
	reqXML, err := xml.Marshal(data)
	if err != nil {
		return "", err
	}

	// Bodyを取得
	body, err := PostRequest(url, reqXML)
	if err != nil {
		return "", err
	}

	// OutStatusを戻す
	resData := new(aaaLogout)
	if err := xml.Unmarshal(body, resData); err != nil {
		return "", err
	}
	return resData.OutStatus, nil
}

// PostRequest CIMCにPostリクエストを実行する関数
func PostRequest(url string, reqXML []byte) (body []byte, err error) {
	// HTTPクライアントを作成、SSL証明書の検証はスキップ
	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true},
		},
	}

	// リクエストを作成
	req, err := http.NewRequest("POST", url, strings.NewReader(string(reqXML)))
	if err != nil {
		return nil, err
	}
	// ヘッダ追加
	req.Header.Add("content-type", "application/x-www-form-urlencoded")

	// リクエストを実行
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	// 取得したBodyを戻す
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	return body, nil
}

func doMain(c *cli.Context) {
	// CIMCへログイン
	login := new(aaaLogin)
	login.InName = c.String("user")
	login.InPassword = c.String("password")
	cookie, _ := login.Login(c.String("url"))
	fmt.Println("cookie: " + cookie)

	// CIMCからログアウト
	logout := new(aaaLogout)
	logout.Cookie = cookie
	logout.InCookie = cookie
	status, _ := logout.Logout(c.String("url"))
	fmt.Println("Logout: " + status)
}

func main() {
	app := cli.NewApp()
	app.Name = "cimc_login_logout"
	app.Usage = "CIMCログイン,ログアウトツール"
	app.UsageText = "cimc_login_logout [--url CIMC URL] [--user CIMC USER] [--password CIMC USER PASSWORD]"
	app.Version = "0.1"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "url",
			Value: "https://127.0.0.1/nuova",
			Usage: "CIMC URL",
		},
		cli.StringFlag{
			Name:  "user, u",
			Value: "admin",
			Usage: "CIMC Login User.",
		},
		cli.StringFlag{
			Name:  "password, p",
			Value: "password",
			Usage: "CIMC Login User Password.",
		},
	}
	app.Action = doMain
	app.Run(os.Args)
}
```

## 実行

```bash
[root@localhost ~]# go build -o cimc_login_logout main.go
[root@localhost ~]# ./cimc_login_logout --url https://192.168.1.54/nuova
cookie: 1507529279/7cd516ac-5b17-1b17-800d-ee768522e470
Logout: success
```

できた :-)

# CIMCの管理構造について

CIMCの管理モデルはツリー構造化されています。
Emulatorで起動したUCS C220 M4の全ツリー構造については参考までに以下に保存してあります。

[CIMC MIM XML構造](https://gist.github.com/sky-joker/b9d43a7ec5e350ac9bfacacae0491446)

XMLだと見にくいのでJSONに変換してみました。

[CIMC MIM JSON構造](https://gist.github.com/sky-joker/2f7308afbcba1fe1b2e12677bc327735)

[^1]: Cisco UCS Cシリーズの管理コントローラー、HPだとiLo