2018-07-03 10:44:04 +00:00
|
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"time"
|
2019-04-26 09:08:44 +00:00
|
|
|
|
|
|
|
|
|
"github.com/sacloud/libsacloud/sacloud"
|
2018-07-03 10:44:04 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
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{}
|
2019-04-26 09:08:44 +00:00
|
|
|
|
|
|
|
|
|
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)
|
2018-07-03 10:44:04 +00:00
|
|
|
|
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{}
|
2019-04-26 09:08:44 +00:00
|
|
|
|
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)
|
2018-07-03 10:44:04 +00:00
|
|
|
|
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
|
|
|
|
|
}
|
2019-04-26 09:08:44 +00:00
|
|
|
|
// OPNsenseであれば編集不可
|
|
|
|
|
if disk.HasTag("distro-opnsense") {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
// Netwiser VEであれば編集不可
|
|
|
|
|
if disk.HasTag("pkg-netwiserve") {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
2018-07-03 10:44:04 +00:00
|
|
|
|
|
|
|
|
|
// ソースアーカイブ/ソースディスクともに持っていない場合
|
|
|
|
|
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
|
|
|
|
|
}
|
2019-04-26 09:08:44 +00:00
|
|
|
|
// OPNsenseであれば編集不可
|
|
|
|
|
if disk.HasTag("distro-opnsense") {
|
|
|
|
|
return emptyID, false
|
|
|
|
|
}
|
|
|
|
|
// Netwiser VEであれば編集不可
|
|
|
|
|
if disk.HasTag("pkg-netwiserve") {
|
|
|
|
|
return emptyID, false
|
|
|
|
|
}
|
2018-07-03 10:44:04 +00:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
}
|