350 lines
9.3 KiB
Go
350 lines
9.3 KiB
Go
package api
|
||
|
||
import (
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/sacloud/libsacloud/sacloud"
|
||
)
|
||
|
||
var (
|
||
// allowDiskEditTags ディスクの編集可否判定に用いるタグ
|
||
allowDiskEditTags = []string{
|
||
"os-unix",
|
||
"os-linux",
|
||
}
|
||
|
||
// bundleInfoWindowsHostClass ディスクの編集可否判定に用いる、BundleInfoでのWindows判定文字列
|
||
bundleInfoWindowsHostClass = "ms_windows"
|
||
)
|
||
|
||
// DiskAPI ディスクAPI
|
||
type DiskAPI struct {
|
||
*baseAPI
|
||
}
|
||
|
||
// NewDiskAPI ディスクAPI作成
|
||
func NewDiskAPI(client *Client) *DiskAPI {
|
||
return &DiskAPI{
|
||
&baseAPI{
|
||
client: client,
|
||
// FuncGetResourceURL
|
||
FuncGetResourceURL: func() string {
|
||
return "disk"
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
// SortByConnectionOrder 接続順でのソート
|
||
func (api *DiskAPI) SortByConnectionOrder(reverse bool) *DiskAPI {
|
||
api.sortBy("ConnectionOrder", reverse)
|
||
return api
|
||
}
|
||
|
||
// WithServerID サーバーID条件
|
||
func (api *DiskAPI) WithServerID(id int64) *DiskAPI {
|
||
api.FilterBy("Server.ID", id)
|
||
return api
|
||
}
|
||
|
||
// Create 新規作成
|
||
func (api *DiskAPI) Create(value *sacloud.Disk) (*sacloud.Disk, error) {
|
||
//HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないため文字列で受ける
|
||
type diskResponse struct {
|
||
*sacloud.Response
|
||
// Success
|
||
Success string `json:",omitempty"`
|
||
}
|
||
res := &diskResponse{}
|
||
|
||
rawBody := &sacloud.Request{}
|
||
rawBody.Disk = value
|
||
if len(value.DistantFrom) > 0 {
|
||
rawBody.DistantFrom = value.DistantFrom
|
||
value.DistantFrom = []int64{}
|
||
}
|
||
|
||
err := api.create(rawBody, res)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return res.Disk, nil
|
||
}
|
||
|
||
// CreateWithConfig ディスク作成とディスクの修正、サーバ起動(指定されていれば)を1回のAPI呼び出しで実行
|
||
func (api *DiskAPI) CreateWithConfig(value *sacloud.Disk, config *sacloud.DiskEditValue, bootAtAvailable bool) (*sacloud.Disk, error) {
|
||
//HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないため文字列で受ける("Accepted"などが返る)
|
||
type diskResponse struct {
|
||
*sacloud.Response
|
||
// Success
|
||
Success string `json:",omitempty"`
|
||
}
|
||
res := &diskResponse{}
|
||
|
||
type diskRequest struct {
|
||
*sacloud.Request
|
||
Config *sacloud.DiskEditValue `json:",omitempty"`
|
||
BootAtAvailable bool `json:",omitempty"`
|
||
}
|
||
|
||
rawBody := &diskRequest{
|
||
Request: &sacloud.Request{},
|
||
BootAtAvailable: bootAtAvailable,
|
||
}
|
||
rawBody.Disk = value
|
||
rawBody.Config = config
|
||
|
||
if len(value.DistantFrom) > 0 {
|
||
rawBody.DistantFrom = value.DistantFrom
|
||
value.DistantFrom = []int64{}
|
||
}
|
||
|
||
err := api.create(rawBody, res)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return res.Disk, nil
|
||
}
|
||
|
||
// NewCondig ディスクの修正用パラメーター作成
|
||
func (api *DiskAPI) NewCondig() *sacloud.DiskEditValue {
|
||
return &sacloud.DiskEditValue{}
|
||
}
|
||
|
||
// Config ディスクの修正
|
||
func (api *DiskAPI) Config(id int64, disk *sacloud.DiskEditValue) (bool, error) {
|
||
var (
|
||
method = "PUT"
|
||
uri = fmt.Sprintf("%s/%d/config", api.getResourceURL(), id)
|
||
)
|
||
|
||
return api.modify(method, uri, disk)
|
||
}
|
||
|
||
func (api *DiskAPI) install(id int64, body *sacloud.Disk) (bool, error) {
|
||
var (
|
||
method = "PUT"
|
||
uri = fmt.Sprintf("%s/%d/install", api.getResourceURL(), id)
|
||
)
|
||
//HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないため文字列で受ける
|
||
type diskResponse struct {
|
||
*sacloud.ResultFlagValue
|
||
// Success
|
||
Success string `json:",omitempty"`
|
||
}
|
||
res := &diskResponse{}
|
||
rawBody := &sacloud.Request{}
|
||
rawBody.Disk = body
|
||
if len(body.DistantFrom) > 0 {
|
||
rawBody.DistantFrom = body.DistantFrom
|
||
body.DistantFrom = []int64{}
|
||
}
|
||
|
||
err := api.baseAPI.request(method, uri, rawBody, res)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
return res.IsOk, nil
|
||
}
|
||
|
||
// ReinstallFromBlank ブランクディスクから再インストール
|
||
func (api *DiskAPI) ReinstallFromBlank(id int64, sizeMB int) (bool, error) {
|
||
var body = &sacloud.Disk{}
|
||
body.SetSizeMB(sizeMB)
|
||
|
||
return api.install(id, body)
|
||
}
|
||
|
||
// ReinstallFromArchive アーカイブからの再インストール
|
||
func (api *DiskAPI) ReinstallFromArchive(id int64, archiveID int64, distantFrom ...int64) (bool, error) {
|
||
var body = &sacloud.Disk{}
|
||
body.SetSourceArchive(archiveID)
|
||
if len(distantFrom) > 0 {
|
||
body.SetDistantFrom(distantFrom)
|
||
}
|
||
return api.install(id, body)
|
||
}
|
||
|
||
// ReinstallFromDisk ディスクからの再インストール
|
||
func (api *DiskAPI) ReinstallFromDisk(id int64, diskID int64, distantFrom ...int64) (bool, error) {
|
||
var body = &sacloud.Disk{}
|
||
body.SetSourceDisk(diskID)
|
||
if len(distantFrom) > 0 {
|
||
body.SetDistantFrom(distantFrom)
|
||
}
|
||
return api.install(id, body)
|
||
}
|
||
|
||
// ToBlank ディスクを空にする
|
||
func (api *DiskAPI) ToBlank(diskID int64) (bool, error) {
|
||
var (
|
||
method = "PUT"
|
||
uri = fmt.Sprintf("%s/%d/to/blank", api.getResourceURL(), diskID)
|
||
)
|
||
return api.modify(method, uri, nil)
|
||
}
|
||
|
||
// ResizePartition パーティションのリサイズ
|
||
func (api *DiskAPI) ResizePartition(diskID int64) (bool, error) {
|
||
var (
|
||
method = "PUT"
|
||
uri = fmt.Sprintf("%s/%d/resize-partition", api.getResourceURL(), diskID)
|
||
)
|
||
return api.modify(method, uri, nil)
|
||
}
|
||
|
||
// DisconnectFromServer サーバーとの接続解除
|
||
func (api *DiskAPI) DisconnectFromServer(diskID int64) (bool, error) {
|
||
var (
|
||
method = "DELETE"
|
||
uri = fmt.Sprintf("%s/%d/to/server", api.getResourceURL(), diskID)
|
||
)
|
||
return api.modify(method, uri, nil)
|
||
}
|
||
|
||
// ConnectToServer サーバーとの接続
|
||
func (api *DiskAPI) ConnectToServer(diskID int64, serverID int64) (bool, error) {
|
||
var (
|
||
method = "PUT"
|
||
uri = fmt.Sprintf("%s/%d/to/server/%d", api.getResourceURL(), diskID, serverID)
|
||
)
|
||
return api.modify(method, uri, nil)
|
||
}
|
||
|
||
// State ディスクの状態を取得し有効な状態か判定
|
||
func (api *DiskAPI) State(diskID int64) (bool, error) {
|
||
disk, err := api.Read(diskID)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
return disk.IsAvailable(), nil
|
||
}
|
||
|
||
// SleepWhileCopying コピー終了まで待機
|
||
func (api *DiskAPI) SleepWhileCopying(id int64, timeout time.Duration) error {
|
||
handler := waitingForAvailableFunc(func() (hasAvailable, error) {
|
||
return api.Read(id)
|
||
}, 0)
|
||
return blockingPoll(handler, timeout)
|
||
}
|
||
|
||
// AsyncSleepWhileCopying コピー終了まで待機(非同期)
|
||
func (api *DiskAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration) (chan (interface{}), chan (interface{}), chan (error)) {
|
||
handler := waitingForAvailableFunc(func() (hasAvailable, error) {
|
||
return api.Read(id)
|
||
}, 0)
|
||
return poll(handler, timeout)
|
||
}
|
||
|
||
// Monitor アクティビティーモニター取得
|
||
func (api *DiskAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) {
|
||
return api.baseAPI.monitor(id, body)
|
||
}
|
||
|
||
// CanEditDisk ディスクの修正が可能か判定
|
||
func (api *DiskAPI) CanEditDisk(id int64) (bool, error) {
|
||
|
||
disk, err := api.Read(id)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
|
||
if disk == nil {
|
||
return false, nil
|
||
}
|
||
|
||
// BundleInfoがあれば編集不可
|
||
if disk.BundleInfo != nil && disk.BundleInfo.HostClass == bundleInfoWindowsHostClass {
|
||
// Windows
|
||
return false, nil
|
||
}
|
||
|
||
// SophosUTMであれば編集不可
|
||
if disk.HasTag("pkg-sophosutm") || disk.IsSophosUTM() {
|
||
return false, nil
|
||
}
|
||
// OPNsenseであれば編集不可
|
||
if disk.HasTag("distro-opnsense") {
|
||
return false, nil
|
||
}
|
||
// Netwiser VEであれば編集不可
|
||
if disk.HasTag("pkg-netwiserve") {
|
||
return false, nil
|
||
}
|
||
|
||
// ソースアーカイブ/ソースディスクともに持っていない場合
|
||
if disk.SourceArchive == nil && disk.SourceDisk == nil {
|
||
//ブランクディスクがソース
|
||
return false, nil
|
||
}
|
||
|
||
for _, t := range allowDiskEditTags {
|
||
if disk.HasTag(t) {
|
||
// 対応OSインストール済みディスク
|
||
return true, nil
|
||
}
|
||
}
|
||
|
||
// ここまできても判定できないならソースに投げる
|
||
if disk.SourceDisk != nil && disk.SourceDisk.Availability != "discontinued" {
|
||
return api.client.Disk.CanEditDisk(disk.SourceDisk.ID)
|
||
}
|
||
if disk.SourceArchive != nil && disk.SourceArchive.Availability != "discontinued" {
|
||
return api.client.Archive.CanEditDisk(disk.SourceArchive.ID)
|
||
}
|
||
|
||
return false, nil
|
||
|
||
}
|
||
|
||
// GetPublicArchiveIDFromAncestors 祖先の中からパブリックアーカイブのIDを検索
|
||
func (api *DiskAPI) GetPublicArchiveIDFromAncestors(id int64) (int64, bool) {
|
||
|
||
emptyID := int64(0)
|
||
|
||
disk, err := api.Read(id)
|
||
if err != nil {
|
||
return emptyID, false
|
||
}
|
||
|
||
if disk == nil {
|
||
return emptyID, false
|
||
}
|
||
|
||
// BundleInfoがあれば編集不可
|
||
if disk.BundleInfo != nil && disk.BundleInfo.HostClass == bundleInfoWindowsHostClass {
|
||
// Windows
|
||
return emptyID, false
|
||
}
|
||
|
||
// SophosUTMであれば編集不可
|
||
if disk.HasTag("pkg-sophosutm") || disk.IsSophosUTM() {
|
||
return emptyID, false
|
||
}
|
||
// OPNsenseであれば編集不可
|
||
if disk.HasTag("distro-opnsense") {
|
||
return emptyID, false
|
||
}
|
||
// Netwiser VEであれば編集不可
|
||
if disk.HasTag("pkg-netwiserve") {
|
||
return emptyID, false
|
||
}
|
||
|
||
for _, t := range allowDiskEditTags {
|
||
if disk.HasTag(t) {
|
||
// 対応OSインストール済みディスク
|
||
return disk.ID, true
|
||
}
|
||
}
|
||
|
||
// ここまできても判定できないならソースに投げる
|
||
if disk.SourceDisk != nil && disk.SourceDisk.Availability != "discontinued" {
|
||
return api.client.Disk.GetPublicArchiveIDFromAncestors(disk.SourceDisk.ID)
|
||
}
|
||
if disk.SourceArchive != nil && disk.SourceArchive.Availability != "discontinued" {
|
||
return api.client.Archive.GetPublicArchiveIDFromAncestors(disk.SourceArchive.ID)
|
||
}
|
||
return emptyID, false
|
||
|
||
}
|