diff --git a/cmd/climc/shell/cloudaccounts.go b/cmd/climc/shell/cloudaccounts.go index 46c6ca65d7..38ef4d53f0 100644 --- a/cmd/climc/shell/cloudaccounts.go +++ b/cmd/climc/shell/cloudaccounts.go @@ -236,6 +236,17 @@ func init() { return nil }) + R(&options.SCtyunCloudAccountCreateOptions{}, "cloud-account-create-ctyun", "Create a Ctyun cloud account", func(s *mcclient.ClientSession, args *options.SCtyunCloudAccountCreateOptions) error { + params := jsonutils.Marshal(args) + params.(*jsonutils.JSONDict).Add(jsonutils.NewString("Ctyun"), "provider") + result, err := modules.Cloudaccounts.Create(s, params) + if err != nil { + return err + } + printObject(result) + return nil + }) + type CloudaccountUpdateOptions struct { ID string `help:"ID or Name of cloud account"` Name string `help:"New name to update"` @@ -413,6 +424,19 @@ func init() { return nil }) + R(&options.SCtyunCloudAccountUpdateOptions{}, "cloud-account-update-ctyun", "update a Ctyun cloud account", func(s *mcclient.ClientSession, args *options.SCtyunCloudAccountUpdateOptions) error { + params := jsonutils.Marshal(args).(*jsonutils.JSONDict) + if params.Size() == 0 { + return InvalidUpdateError() + } + result, err := modules.Cloudaccounts.Update(s, args.ID, params) + if err != nil { + return err + } + printObject(result) + return nil + }) + type CloudaccountShowOptions struct { ID string `help:"ID or Name of cloud account"` } @@ -597,6 +621,16 @@ func init() { return nil }) + R(&options.SCtyunCloudAccountUpdateCredentialOptions{}, "cloud-account-update-credential-ctyun", "Update credential of an Ctyun cloud account", func(s *mcclient.ClientSession, args *options.SCtyunCloudAccountUpdateCredentialOptions) error { + params := jsonutils.Marshal(args) + result, err := modules.Cloudaccounts.PerformAction(s, args.ID, "update-credential", params) + if err != nil { + return err + } + printObject(result) + return nil + }) + type CloudaccountSyncOptions struct { ID string `help:"ID or Name of cloud account"` Force bool `help:"Force sync no matter what"` diff --git a/cmd/ctyuncli/main.go b/cmd/ctyuncli/main.go new file mode 100644 index 0000000000..6b188a5db5 --- /dev/null +++ b/cmd/ctyuncli/main.go @@ -0,0 +1,138 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "os" + + "yunion.io/x/structarg" + + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + _ "yunion.io/x/onecloud/pkg/multicloud/ctyun/shell" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +type BaseOptions struct { + Help bool `help:"Show help" default:"false"` + Debug bool `help:"Show debug" default:"false"` + AccessKey string `help:"Access key" default:"$CTYUN_ACCESS_KEY"` + Secret string `help:"Secret" default:"$CTYUN_SECRET"` + RegionId string `help:"RegionId" default:"$CTYUN_REGION"` + SUBCOMMAND string `help:"ctyuncli subcommand" subcommand:"true"` +} + +func getSubcommandParser() (*structarg.ArgumentParser, error) { + parse, e := structarg.NewArgumentParser(&BaseOptions{}, + "ctyuncli", + "Command-line interface to ctyun API.", + `See "ctyuncli help COMMAND" for help on a specific command.`) + + if e != nil { + return nil, e + } + + subcmd := parse.GetSubcommand() + if subcmd == nil { + return nil, fmt.Errorf("No subcommand argument.") + } + type HelpOptions struct { + SUBCOMMAND string `help:"sub-command name"` + } + shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error { + helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND) + if e != nil { + return e + } else { + fmt.Print(helpstr) + return nil + } + }) + for _, v := range shellutils.CommandTable { + _, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback) + if e != nil { + return nil, e + } + } + return parse, nil +} + +func showErrorAndExit(e error) { + fmt.Fprintf(os.Stderr, "%s", e) + fmt.Fprintln(os.Stderr) + os.Exit(1) +} + +func newClient(options *BaseOptions) (*ctyun.SRegion, error) { + if len(options.AccessKey) == 0 { + return nil, fmt.Errorf("Missing accessKey") + } + + if len(options.Secret) == 0 { + return nil, fmt.Errorf("Missing secret") + } + + cli, err := ctyun.NewSCtyunClient("", "", "", + options.AccessKey, options.Secret, options.Debug) + if err != nil { + return nil, err + } + + region := cli.GetRegion(options.RegionId) + if region == nil { + return nil, fmt.Errorf("No such region %s", options.RegionId) + } + + return region, nil +} + +func main() { + parser, e := getSubcommandParser() + if e != nil { + showErrorAndExit(e) + } + e = parser.ParseArgs(os.Args[1:], false) + options := parser.Options().(*BaseOptions) + + if options.Help { + fmt.Print(parser.HelpString()) + } else { + subcmd := parser.GetSubcommand() + subparser := subcmd.GetSubParser() + if e != nil { + if subparser != nil { + fmt.Print(subparser.Usage()) + } else { + fmt.Print(parser.Usage()) + } + showErrorAndExit(e) + } else { + suboptions := subparser.Options() + if options.SUBCOMMAND == "help" { + e = subcmd.Invoke(suboptions) + } else { + var region *ctyun.SRegion + region, e = newClient(options) + if e != nil { + showErrorAndExit(e) + } + e = subcmd.Invoke(region, suboptions) + } + if e != nil { + showErrorAndExit(e) + } + } + } +} diff --git a/pkg/apis/compute/cloudaccount_const.go b/pkg/apis/compute/cloudaccount_const.go index a882a42a58..e10a8ff950 100644 --- a/pkg/apis/compute/cloudaccount_const.go +++ b/pkg/apis/compute/cloudaccount_const.go @@ -40,6 +40,7 @@ const ( CLOUD_PROVIDER_UCLOUD = "Ucloud" CLOUD_PROVIDER_ZSTACK = "ZStack" CLOUD_PROVIDER_GOOGLE = "Google" + CLOUD_PROVIDER_CTYUN = "Ctyun" CLOUD_PROVIDER_GENERICS3 = "S3" CLOUD_PROVIDER_CEPH = "Ceph" @@ -65,6 +66,7 @@ const ( CLOUD_ACCESS_ENV_AZURE_CHINA = CLOUD_PROVIDER_AZURE CLOUD_ACCESS_ENV_HUAWEI_GLOBAL = CLOUD_PROVIDER_HUAWEI + "-int" CLOUD_ACCESS_ENV_HUAWEI_CHINA = CLOUD_PROVIDER_HUAWEI + CLOUD_ACCESS_ENV_CTYUN_CHINA = CLOUD_PROVIDER_CTYUN ) var ( @@ -84,6 +86,7 @@ var ( CLOUD_PROVIDER_UCLOUD, CLOUD_PROVIDER_ZSTACK, CLOUD_PROVIDER_GOOGLE, + CLOUD_PROVIDER_CTYUN, } ) diff --git a/pkg/apis/compute/cloudregions_const.go b/pkg/apis/compute/cloudregions_const.go index 9548623fce..7e57bf4519 100644 --- a/pkg/apis/compute/cloudregions_const.go +++ b/pkg/apis/compute/cloudregions_const.go @@ -37,6 +37,23 @@ const ( CITY_KAOHSIUNG = "Kaohsiung" //高雄市 CITY_CHENG_DU = "Chengdu" //成都 CITY_CHONG_QING = "Chongqing" //重庆 + CITY_LAN_ZHOU = "Lanzhou" //兰州 + CITY_TAI_YUAN = "Taiyuan" //太原 + CITY_TIAN_JIN = "Tianjin" //天津 + CITY_WU_LU_MU_QI = "Wulumuqi" //乌鲁木齐 + CITY_NAN_NING = "Nanning" //南宁 + CITY_ZHENG_ZHOU = "Zhengzhou" //郑州 + CITY_KUN_MING = "Kunming" //昆明 + CITY_XI_AN = "Xian" //西安 + CITY_HAI_KOU = "Haikou" //海口 + CITY_WU_HU = "Wuhu" //芜湖 + CITY_FU_ZHOU = "Fuzhou" //福州 + CITY_WU_HAN = "Wuhan" //武汉 + CITY_CHANG_SHA = "Changsha" //长沙 + CITY_SHU_ZHOU = "Shuzhou" //苏州 + CITY_BAO_DING = "Baoding" //保定 + CITY_NAN_JING = "Nanjing" //南京 + CITY_FO_SHAN = "Foshan" //佛山 // 日本 CITY_TOKYO = "Tokyo" //东京 diff --git a/pkg/apis/compute/guest_const.go b/pkg/apis/compute/guest_const.go index 56b4de140a..de4213b947 100644 --- a/pkg/apis/compute/guest_const.go +++ b/pkg/apis/compute/guest_const.go @@ -141,6 +141,7 @@ const ( HYPERVISOR_UCLOUD = "ucloud" HYPERVISOR_ZSTACK = "zstack" HYPERVISOR_GOOGLE = "google" + HYPERVISOR_CTYUN = "ctyun" // HYPERVISOR_DEFAULT = HYPERVISOR_KVM HYPERVISOR_DEFAULT = HYPERVISOR_KVM @@ -163,6 +164,7 @@ var HYPERVISORS = []string{ HYPERVISOR_UCLOUD, HYPERVISOR_ZSTACK, HYPERVISOR_GOOGLE, + HYPERVISOR_CTYUN, } var ONECLOUD_HYPERVISORS = []string{ @@ -179,6 +181,7 @@ var PUBLIC_CLOUD_HYPERVISORS = []string{ HYPERVISOR_HUAWEI, HYPERVISOR_UCLOUD, HYPERVISOR_GOOGLE, + HYPERVISOR_CTYUN, } var PRIVATE_CLOUD_HYPERVISORS = []string{ @@ -202,6 +205,7 @@ var HYPERVISOR_HOSTTYPE = map[string]string{ HYPERVISOR_UCLOUD: HOST_TYPE_UCLOUD, HYPERVISOR_ZSTACK: HOST_TYPE_ZSTACK, HYPERVISOR_GOOGLE: HOST_TYPE_GOOGLE, + HYPERVISOR_CTYUN: HOST_TYPE_CTYUN, } var HOSTTYPE_HYPERVISOR = map[string]string{ @@ -218,6 +222,7 @@ var HOSTTYPE_HYPERVISOR = map[string]string{ HOST_TYPE_UCLOUD: HYPERVISOR_UCLOUD, HOST_TYPE_ZSTACK: HYPERVISOR_ZSTACK, HOST_TYPE_GOOGLE: HYPERVISOR_GOOGLE, + HOST_TYPE_CTYUN: HYPERVISOR_CTYUN, } const ( diff --git a/pkg/apis/compute/host_const.go b/pkg/apis/compute/host_const.go index b7d7638fe2..4dee7e482e 100644 --- a/pkg/apis/compute/host_const.go +++ b/pkg/apis/compute/host_const.go @@ -32,6 +32,7 @@ const ( HOST_TYPE_UCLOUD = "ucloud" HOST_TYPE_ZSTACK = "zstack" HOST_TYPE_GOOGLE = "google" + HOST_TYPE_CTYUN = "ctyun" HOST_TYPE_DEFAULT = HOST_TYPE_HYPERVISOR @@ -108,6 +109,7 @@ var HOST_TYPES = []string{ HOST_TYPE_OPENSTACK, HOST_TYPE_UCLOUD, HOST_TYPE_ZSTACK, + HOST_TYPE_CTYUN, } var NIC_TYPES = []string{NIC_TYPE_IPMI, NIC_TYPE_ADMIN} diff --git a/pkg/apis/compute/instance_group.go b/pkg/apis/compute/instance_group.go index 4e16ef3299..52d5e362ed 100644 --- a/pkg/apis/compute/instance_group.go +++ b/pkg/apis/compute/instance_group.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package compute import "yunion.io/x/onecloud/pkg/apis" diff --git a/pkg/apis/compute/storage_const.go b/pkg/apis/compute/storage_const.go index c2f78b18b3..f7bbf2636b 100644 --- a/pkg/apis/compute/storage_const.go +++ b/pkg/apis/compute/storage_const.go @@ -75,6 +75,11 @@ const ( STORAGE_GOOGLE_LOCAL_STORAGE = "local-storage" //本地SSD暂存盘 (最多8个) STORAGE_GOOGLE_PD_STANDARD = "pd-standard" //标准永久性磁盘 STORAGE_GOOGLE_PD_SSD = "pd-ssd" //SSD永久性磁盘 + + // ctyun storage type + STORAGE_CTYUN_SSD = "SSD" // 超高IO云硬盘 + STORAGE_CTYUN_SAS = "SAS" // 高IO云硬盘 + STORAGE_CTYUN_SATA = "SATA" // 普通IO云硬盘 ) const ( diff --git a/pkg/apis/parameters.go b/pkg/apis/parameters.go index 39d7278331..a31bbd58f3 100644 --- a/pkg/apis/parameters.go +++ b/pkg/apis/parameters.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package apis type BaseListInput struct { diff --git a/pkg/compute/guestdrivers/ctyun.go b/pkg/compute/guestdrivers/ctyun.go new file mode 100644 index 0000000000..efc03fe38c --- /dev/null +++ b/pkg/compute/guestdrivers/ctyun.go @@ -0,0 +1,104 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package guestdrivers + +import ( + "fmt" + + "yunion.io/x/pkg/utils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type SCtyunGuestDriver struct { + SManagedVirtualizedGuestDriver +} + +func (self *SCtyunGuestDriver) GetHypervisor() string { + return api.HYPERVISOR_CTYUN +} + +func (self *SCtyunGuestDriver) GetProvider() string { + return api.CLOUD_PROVIDER_CTYUN +} + +func (self *SCtyunGuestDriver) GetQuotaPlatformID() []string { + return []string{ + api.CLOUD_ENV_PUBLIC_CLOUD, + api.CLOUD_PROVIDER_CTYUN, + } +} + +func (self *SCtyunGuestDriver) GetDefaultSysDiskBackend() string { + return api.STORAGE_CTYUN_SAS +} + +func (self *SCtyunGuestDriver) GetDeployStatus() ([]string, error) { + return []string{api.VM_READY, api.VM_RUNNING}, nil +} + +func (self *SCtyunGuestDriver) GetMinimalSysDiskSizeGb() int { + return 10 +} + +func (self *SCtyunGuestDriver) GetGuestInitialStateAfterCreate() string { + return api.VM_RUNNING +} + +func (self *SCtyunGuestDriver) GetDetachDiskStatus() ([]string, error) { + return []string{api.VM_READY, api.VM_RUNNING}, nil +} + +func (self *SCtyunGuestDriver) GetAttachDiskStatus() ([]string, error) { + return []string{api.VM_READY, api.VM_RUNNING}, nil +} + +func (self *SCtyunGuestDriver) GetChangeConfigStatus() ([]string, error) { + return []string{api.VM_READY}, nil +} + +func (self *SCtyunGuestDriver) GetRebuildRootStatus() ([]string, error) { + return []string{api.VM_READY, api.VM_RUNNING}, nil +} + +func (self *SCtyunGuestDriver) GetGuestInitialStateAfterRebuild() string { + return api.VM_RUNNING +} + +func (self *SCtyunGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error { + if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY}) { + return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status) + } + if !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_CTYUN_SATA, api.STORAGE_CTYUN_SAS, api.STORAGE_CTYUN_SSD}) { + return fmt.Errorf("Cannot resize disk with unsupported volumes type %s", storage.StorageType) + } + + return nil +} + +func (self *SCtyunGuestDriver) GetLinuxDefaultAccount(desc cloudprovider.SManagedVMCreateConfig) string { + if desc.OsType == "Windows" { + return "Administrator" + } + + return "root" +} + +func init() { + driver := SCtyunGuestDriver{} + models.RegisterGuestDriver(&driver) +} diff --git a/pkg/compute/hostdrivers/ctyun.go b/pkg/compute/hostdrivers/ctyun.go new file mode 100644 index 0000000000..565bd888b3 --- /dev/null +++ b/pkg/compute/hostdrivers/ctyun.go @@ -0,0 +1,65 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostdrivers + +import ( + "context" + "fmt" + + "yunion.io/x/jsonutils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SCtyunHostDriver struct { + SManagedVirtualizationHostDriver +} + +func init() { + driver := SCtyunHostDriver{} + models.RegisterHostDriver(&driver) +} + +func (self *SCtyunHostDriver) GetHostType() string { + return api.HOST_TYPE_CTYUN +} + +func (self *SCtyunHostDriver) GetHypervisor() string { + return api.HYPERVISOR_CTYUN +} + +// 系统盘必须至少40G +func (self *SCtyunHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error { + switch storage.StorageType { + case api.STORAGE_CTYUN_SSD, api.STORAGE_CTYUN_SATA, api.STORAGE_CTYUN_SAS: + if sizeGb < 10 || sizeGb > 32768 { + return fmt.Errorf("The %s disk size must be in the range of 10G ~ 32768GB", storage.StorageType) + } + default: + return fmt.Errorf("Not support create %s disk", storage.StorageType) + } + + return nil +} + +func (self *SCtyunHostDriver) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, guests []models.SGuest, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + if len(guests) >= 1 { + return nil, httperrors.NewBadRequestError("Disk must be dettached") + } + return data, nil +} diff --git a/pkg/compute/regiondrivers/ctyun.go b/pkg/compute/regiondrivers/ctyun.go new file mode 100644 index 0000000000..b6186d42f6 --- /dev/null +++ b/pkg/compute/regiondrivers/ctyun.go @@ -0,0 +1,51 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package regiondrivers + +import ( + "context" + + "yunion.io/x/jsonutils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SCtyunRegionDriver struct { + SManagedVirtualizationRegionDriver +} + +func init() { + driver := SCtyunRegionDriver{} + models.RegisterRegionDriver(&driver) +} + +func (self *SCtyunRegionDriver) GetProvider() string { + return api.CLOUD_PROVIDER_CTYUN +} + +func (self *SCtyunRegionDriver) ValidateCreateLoadbalancerData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("%s does not currently support creating loadbalancer", self.GetProvider()) +} + +func (self *SCtyunRegionDriver) ValidateCreateLoadbalancerAclData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("%s does not currently support creating loadbalancer acl", self.GetProvider()) +} + +func (self *SCtyunRegionDriver) ValidateCreateLoadbalancerCertificateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("%s does not currently support creating loadbalancer certificate", self.GetProvider()) +} diff --git a/pkg/devtool/tasks/template_binding_servers.go b/pkg/devtool/tasks/template_binding_servers.go index a913ad41d9..fad5adf61f 100644 --- a/pkg/devtool/tasks/template_binding_servers.go +++ b/pkg/devtool/tasks/template_binding_servers.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Copyright 2019 Yunion // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/devtool/tasks/template_unbinding_servers.go b/pkg/devtool/tasks/template_unbinding_servers.go index c34095cef7..ad0f623d69 100644 --- a/pkg/devtool/tasks/template_unbinding_servers.go +++ b/pkg/devtool/tasks/template_unbinding_servers.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Copyright 2019 Yunion // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/devtool/tasks/template_update.go b/pkg/devtool/tasks/template_update.go index c7b7c9833a..c44dc70f30 100644 --- a/pkg/devtool/tasks/template_update.go +++ b/pkg/devtool/tasks/template_update.go @@ -1,3 +1,17 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Copyright 2019 Yunion // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/mcclient/options/cloudaccounts.go b/pkg/mcclient/options/cloudaccounts.go index 6f94180099..e7a028758b 100644 --- a/pkg/mcclient/options/cloudaccounts.go +++ b/pkg/mcclient/options/cloudaccounts.go @@ -144,6 +144,11 @@ type SS3CloudAccountCreateOptions struct { Endpoint string `help:"S3 endpoint" required:"true" positional:"true" json:"endpoint"` } +type SCtyunCloudAccountCreateOptions struct { + SCloudAccountCreateBaseOptions + SAccessKeyCredentialWithEnvironment +} + // update credential options type SCloudAccountUpdateCredentialBaseOptions struct { @@ -200,6 +205,11 @@ type SS3CloudAccountUpdateCredentialOptions struct { SAccessKeyCredential } +type SCtyunCloudAccountUpdateCredentialOptions struct { + SCloudAccountUpdateCredentialBaseOptions + SAccessKeyCredential +} + // update type SCloudAccountUpdateBaseOptions struct { @@ -257,3 +267,7 @@ type SZStackCloudAccountUpdateOptions struct { type SS3CloudAccountUpdateOptions struct { SCloudAccountUpdateBaseOptions } + +type SCtyunCloudAccountUpdateOptions struct { + SCloudAccountUpdateBaseOptions +} diff --git a/pkg/multicloud/ctyun/ctyun.go b/pkg/multicloud/ctyun/ctyun.go new file mode 100644 index 0000000000..13d14a2a6d --- /dev/null +++ b/pkg/multicloud/ctyun/ctyun.go @@ -0,0 +1,289 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "context" + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils" + + "yunion.io/x/jsonutils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/util/httputils" +) + +const ( + CTYUN_API_HOST = "https://api.ctyun.cn" + CLOUD_PROVIDER_CTYUN = api.CLOUD_PROVIDER_CTYUN + CLOUD_PROVIDER_CTYUN_CN = "天翼云" + CTYUN_DEFAULT_REGION = "cn-bj4" + + CTYUN_API_VERSION = "2019-11-22" +) + +type SCtyunClient struct { + httpClient *http.Client + debug bool + + providerId string + providerName string + projectId string // 项目ID. + accessKey string + secret string + + iregions []cloudprovider.ICloudRegion +} + +func NewSCtyunClient(providerId string, providerName string, projectId string, accessKey string, secret string, debug bool) (*SCtyunClient, error) { + client := &SCtyunClient{httpClient: http.DefaultClient, providerId: providerId, providerName: providerName, projectId: projectId, accessKey: accessKey, secret: secret, debug: debug} + + err := client.init() + if err != nil { + return nil, err + } + + return client, nil +} + +func (client *SCtyunClient) init() error { + err := client.fetchRegions() + if err != nil { + return err + } + + return nil +} + +func (client *SCtyunClient) fetchRegions() error { + resp, err := client.DoGet("/apiproxy/v3/order/getZoneConfig", map[string]string{}) + if err != nil { + return err + } + + zones := []SZone{} + err = resp.Unmarshal(&zones, "returnObj") + if err != nil { + return err + } + + regionIds := []string{} + regions := map[string]SRegion{} + for i := range zones { + zone := zones[i] + if region, ok := regions[zone.RegionID]; !ok { + regionIds = append(regionIds, zone.RegionID) + region = SRegion{ + client: client, + Description: zone.ZoneName, + ID: zone.RegionID, + ParentRegionID: zone.RegionID, + izones: []cloudprovider.ICloudZone{&zone}, + } + + zone.region = ®ion + zone.host = &SHost{projectId: client.projectId, zone: &zone} + regions[zone.RegionID] = region + } else { + zone.region = ®ion + zone.host = &SHost{projectId: client.projectId, zone: &zone} + region.izones = append(region.izones, &zone) + } + } + + client.iregions = []cloudprovider.ICloudRegion{} + for k := range regions { + region := regions[k] + client.iregions = append(client.iregions, ®ion) + } + return nil +} + +func (client *SCtyunClient) DoGet(apiName string, queries map[string]string) (jsonutils.JSONObject, error) { + return formRequest(client, httputils.GET, apiName, queries, nil) +} + +func (client *SCtyunClient) DoPost(apiName string, params map[string]jsonutils.JSONObject) (jsonutils.JSONObject, error) { + return formRequest(client, httputils.POST, apiName, nil, params) +} + +func formRequest(client *SCtyunClient, method httputils.THttpMethod, apiName string, queries map[string]string, params map[string]jsonutils.JSONObject) (jsonutils.JSONObject, error) { + header := http.Header{} + // signer + { + content := []string{} + for k, v := range queries { + content = append(content, v) + header.Set(k, v) + } + + for _, v := range params { + c, _ := v.GetString() + content = append(content, c) + } + + // contentMd5 := fmt.Sprintf("%x", md5.Sum([]byte(strings.Join(content, "\n")))) + // contentMd5 = base64.StdEncoding.EncodeToString([]byte(contentMd5)) + contentRaw := strings.Join(content, "\n") + contentMd5 := utils.GetMD5Base64([]byte(contentRaw)) + + // EEE, d MMM yyyy HH:mm:ss z + // Mon, 2 Jan 2006 15:04:05 MST + requestDate := time.Now().Format("Mon, 2 Jan 2006 15:04:05 MST") + hashMac := hmac.New(sha1.New, []byte(client.secret)) + hashRawString := strings.Join([]string{contentMd5, requestDate, apiName}, "\n") + hashMac.Write([]byte(hashRawString)) + hsum := base64.StdEncoding.EncodeToString(hashMac.Sum(nil)) + + header.Set("accessKey", client.accessKey) + header.Set("contentMD5", contentMd5) + header.Set("requestDate", requestDate) + header.Set("hmac", hsum) + // 平台类型,整数类型,取值范围:2或3,传2表示2.0自营资源,传3表示3.0合营资源,该参数不需要加密。 + header.Set("platform", "3") + } + + ioData := strings.NewReader("") + if method == httputils.GET { + for k, v := range queries { + header.Set(k, v) + } + } else { + datas := url.Values{} + for k, v := range params { + c, _ := v.GetString() + datas.Add(k, c) + } + + ioData = strings.NewReader(datas.Encode()) + } + + header.Set("Content-Length", strconv.FormatInt(int64(ioData.Len()), 10)) + header.Set("Content-Type", "application/x-www-form-urlencoded") + + ctx := context.Background() + MAX_RETRY := 3 + retry := 0 + + for retry < MAX_RETRY { + resp, err := httputils.Request( + client.httpClient, + ctx, + method, + CTYUN_API_HOST+apiName, + header, + ioData, + client.debug) + + _, jsonResp, err := httputils.ParseJSONResponse(resp, err, client.debug) + if err == nil { + if code, _ := jsonResp.Int("statusCode"); code != 800 { + return nil, &httputils.JSONClientError{Code: 400, Details: jsonResp.String()} + } + + return jsonResp, nil + } + + switch e := err.(type) { + case *httputils.JSONClientError: + if e.Code >= 499 { + time.Sleep(3 * time.Second) + retry += 1 + continue + } else { + return nil, err + } + default: + return nil, err + } + } + + return nil, fmt.Errorf("timeout for request: %s", params) +} + +func (self *SCtyunClient) GetIRegions() []cloudprovider.ICloudRegion { + return self.iregions +} + +func (self *SCtyunClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { + subAccounts := make([]cloudprovider.SSubAccount, 0) + for i := range self.iregions { + iregion := self.iregions[i] + + s := cloudprovider.SSubAccount{ + Name: fmt.Sprintf("%s-%s", self.providerName, iregion.GetId()), + State: api.CLOUD_PROVIDER_CONNECTED, + Account: fmt.Sprintf("%s/%s", self.accessKey, iregion.GetId()), + HealthStatus: api.CLOUD_PROVIDER_HEALTH_NORMAL, + } + + subAccounts = append(subAccounts, s) + } + + return subAccounts, nil +} + +func (client *SCtyunClient) GetAccountId() string { + return client.accessKey +} + +func (self *SCtyunClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { + for i := 0; i < len(self.iregions); i += 1 { + if self.iregions[i].GetGlobalId() == id { + return self.iregions[i], nil + } + } + + return nil, cloudprovider.ErrNotFound +} + +func (self *SCtyunClient) GetIProjects() ([]cloudprovider.ICloudProject, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SCtyunClient) GetAccessEnv() string { + return api.CLOUD_ACCESS_ENV_CTYUN_CHINA +} + +func (self *SCtyunClient) GetRegions() []SRegion { + regions := make([]SRegion, len(self.iregions)) + for i := 0; i < len(regions); i += 1 { + region := self.iregions[i].(*SRegion) + regions[i] = *region + } + return regions +} + +func (self *SCtyunClient) GetRegion(regionId string) *SRegion { + if len(regionId) == 0 { + regionId = CTYUN_DEFAULT_REGION + } + for i := 0; i < len(self.iregions); i += 1 { + if self.iregions[i].GetId() == regionId { + return self.iregions[i].(*SRegion) + } + } + return nil +} diff --git a/pkg/multicloud/ctyun/disk.go b/pkg/multicloud/ctyun/disk.go new file mode 100644 index 0000000000..b7441004a9 --- /dev/null +++ b/pkg/multicloud/ctyun/disk.go @@ -0,0 +1,440 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "context" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + billing_api "yunion.io/x/onecloud/pkg/apis/billing" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +// http://ctyun-api-url/apiproxy/v3/ondemand/queryVolumes +type SDisk struct { + storage *SStorage + multicloud.SDisk + + diskDetails *DiskDetails + + ID string `json:"id"` + Status string `json:"status"` + Name string `json:"name"` + CreatedAt int64 `json:"created_at"` + UpdatedAt string `json:"updated_at"` + Multiattach bool `json:"multiattach"` + ReplicationStatus string `json:"replication_status"` + SizeGB int64 `json:"size"` + Metadata Metadata `json:"metadata"` + VolumeType string `json:"volume_type"` + UserID string `json:"user_id"` + Shareable bool `json:"shareable"` + Encrypted bool `json:"encrypted"` + Bootable string `json:"bootable"` + AvailabilityZone string `json:"availability_zone"` + Attachments []Attachment `json:"attachments"` + MasterOrderID string `json:"masterOrderId"` + IsSysVolume int `json:"isSysVolume"` + WorkOrderResourceID string `json:"workOrderResourceId"` + ExpireTime int64 `json:"expireTime"` + IsFreeze int64 `json:"isFreeze"` +} + +type Attachment struct { + VolumeID string `json:"volume_id"` + AttachmentID string `json:"attachment_id"` + AttachedAt string `json:"attached_at"` + ServerID string `json:"server_id"` + Device string `json:"device"` + ID string `json:"id"` +} + +type Metadata struct { + OrderID string `json:"orderID"` + AttachedMode string `json:"attached_mode"` + ResourceSpecCode string `json:"resourceSpecCode"` + ProductID string `json:"productID"` + Readonly string `json:"readonly"` +} + +type DiskDetails struct { + ID string `json:"id"` + ResEbsID string `json:"resEbsId"` + Size int64 `json:"size"` + Name string `json:"name"` + RegionID string `json:"regionId"` + AccountID string `json:"accountId"` + UserID string `json:"userId"` + HostID string `json:"hostId"` + OrderID string `json:"orderId"` + Status int64 `json:"status"` + Type string `json:"type"` + VolumeStatus int64 `json:"volumeStatus"` + CreateDate int64 `json:"createDate"` + DueDate int64 `json:"dueDate"` + ZoneID string `json:"zoneId"` + ZoneName string `json:"zoneName"` + IsSysVolume int64 `json:"isSysVolume"` + IsPackaged int64 `json:"isPackaged"` + WorkOrderResourceID string `json:"workOrderResourceId"` + IsFreeze int64 `json:"isFreeze"` +} + +func (self *SDisk) GetBillingType() string { + if self.ExpireTime > 0 { + return billing_api.BILLING_TYPE_PREPAID + } + + return billing_api.BILLING_TYPE_POSTPAID +} + +func (self *SDisk) GetCreatedAt() time.Time { + return time.Unix(self.CreatedAt/1000, 0) +} + +func (self *SDisk) GetExpiredAt() time.Time { + return time.Unix(self.ExpireTime/1000, 0) +} + +func (self *SDisk) GetId() string { + return self.ID +} + +func (self *SDisk) GetName() string { + if len(self.Name) > 0 { + return self.Name + } + + return self.ID +} + +func (self *SDisk) GetGlobalId() string { + return self.GetId() +} + +func (self *SDisk) GetStatus() string { + switch self.Status { + case "creating", "downloading": + return api.DISK_ALLOCATING + case "available", "in-use": + return api.DISK_READY + case "error": + return api.DISK_ALLOC_FAILED + case "attaching": + return api.DISK_ATTACHING + case "detaching": + return api.DISK_DETACHING + case "restoring-backup": + return api.DISK_REBUILD + case "backing-up": + return api.DISK_BACKUP_STARTALLOC + case "error_restoring": + return api.DISK_BACKUP_ALLOC_FAILED + case "uploading": + return api.DISK_SAVING + case "extending": + return api.DISK_RESIZING + case "error_extending": + return api.DISK_ALLOC_FAILED + case "deleting": + return api.DISK_DEALLOC + case "error_deleting": + return api.DISK_DEALLOC_FAILED + case "rollbacking": + return api.DISK_REBUILD + case "error_rollbacking": + return api.DISK_UNKNOWN + default: + return api.DISK_UNKNOWN + } +} + +func (self *SDisk) Refresh() error { + new, err := self.storage.zone.region.GetDisk(self.GetId()) + if err != nil { + return err + } + return jsonutils.Update(self, new) +} + +func (self *SDisk) IsEmulated() bool { + return false +} + +func (self *SDisk) GetMetadata() *jsonutils.JSONDict { + data := jsonutils.NewDict() + data.Add(jsonutils.NewString(api.HYPERVISOR_CTYUN), "hypervisor") + + return data +} + +func (self *SDisk) GetProjectId() string { + return "" +} + +func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return self.storage, nil +} + +func (self *SDisk) GetIStorageId() string { + return self.storage.GetId() +} + +func (self *SDisk) GetDiskFormat() string { + return "vhd" +} + +func (self *SDisk) GetDiskSizeMB() int { + return int(self.SizeGB * 1024) +} + +func (self *SDisk) GetIsAutoDelete() bool { + if len(self.Attachments) == 0 { + return false + } + + if self.Bootable == "true" { + return true + } + + return false +} + +func (self *SDisk) GetTemplateId() string { + return "" +} + +func (self *SDisk) GetDiskType() string { + if self.Bootable == "true" { + return api.DISK_TYPE_SYS + } else { + return api.DISK_TYPE_DATA + } +} + +func (self *SDisk) GetFsFormat() string { + return "" +} + +func (self *SDisk) GetIsNonPersistent() bool { + return false +} + +func (self *SDisk) GetDriver() string { + return "scsi" +} + +func (self *SDisk) GetCacheMode() string { + return "none" +} + +func (self *SDisk) GetMountpoint() string { + if len(self.Attachments) > 0 { + return self.Attachments[0].Device + } + + return "" +} + +func (self *SDisk) GetAccessPath() string { + return "" +} + +func (self *SDisk) Delete(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SDisk) GetISnapshot(idStr string) (cloudprovider.ICloudSnapshot, error) { + return self.storage.zone.region.GetSnapshot(self.GetId(), idStr) +} + +// GET http://ctyun-api-url/apiproxy/v3/ondemand/queryVBSs +func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + snapshots, err := self.storage.zone.region.GetSnapshots(self.GetId()) + if err != nil { + return nil, errors.Wrap(err, "SDisk.GetISnapshots") + } + + isnapshots := []cloudprovider.ICloudSnapshot{} + for i := range snapshots { + isnapshots[i] = &snapshots[i] + } + + return isnapshots, nil +} + +// POST http://ctyun-api-url/apiproxy/v3/ondemand/updateDiskBackupPolicy +func (self *SDisk) GetExtSnapshotPolicyIds() ([]string, error) { + return []string{}, nil +} + +func (self *SDisk) Resize(ctx context.Context, newSizeMB int64) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (self *SDisk) Rebuild(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SDisk) GetDiskDetails() (*DiskDetails, error) { + if self.diskDetails != nil { + return self.diskDetails, nil + } + + details, err := self.storage.zone.region.GetDiskDetailByDiskId(self.GetId()) + if err != nil { + return nil, errors.Wrap(err, "SDisk.GetDiskDetails.GetDiskDetailByDiskId") + } + + self.diskDetails = details + return self.diskDetails, nil +} + +func (self *SRegion) GetDisks() ([]SDisk, error) { + params := map[string]string{ + "regionId": self.GetId(), + } + + resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryVolumes", params) + if err != nil { + return nil, errors.Wrap(err, "Region.GetDisks.DoGet") + } + + disks := make([]SDisk, 0) + err = resp.Unmarshal(&disks, "returnObj", "volumes") + if err != nil { + return nil, errors.Wrap(err, "Region.GetDisks.Unmarshal") + } + + for i := range disks { + izone, err := self.GetIZoneById(disks[i].AvailabilityZone) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetDisk.GetIZoneById") + } + + disks[i].storage = &SStorage{ + zone: izone.(*SZone), + storageType: disks[i].VolumeType, + } + } + + return disks, nil +} + +func (self *SRegion) GetDisk(diskId string) (*SDisk, error) { + params := map[string]string{ + "regionId": self.GetId(), + "volumeId": diskId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryVolumes", params) + if err != nil { + return nil, errors.Wrap(err, "Region.GetDisks.DoGet") + } + + disks := make([]SDisk, 0) + err = resp.Unmarshal(&disks, "returnObj", "volumes") + if err != nil { + return nil, errors.Wrap(err, "Region.GetDisks.Unmarshal") + } + + if len(disks) == 0 { + return nil, errors.Wrap(cloudprovider.ErrNotFound, "SRegion.GetDisk") + } else if len(disks) == 1 { + izone, err := self.GetIZoneById(disks[0].AvailabilityZone) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetDisk.GetIZoneById") + } + + disks[0].storage = &SStorage{ + zone: izone.(*SZone), + storageType: disks[0].VolumeType, + } + + return &disks[0], nil + } else { + return nil, errors.Wrap(cloudprovider.ErrDuplicateId, "SRegion.GetDisk") + } +} + +func (self *SRegion) GetDiskDetailByDiskId(diskId string) (*DiskDetails, error) { + params := map[string]string{ + "volumeId": diskId, + "regionId": self.GetId(), + } + + resp, err := self.client.DoGet("/apiproxy/v3/queryDataDiskDetail", params) + if err != nil { + return nil, errors.Wrap(err, "Region.GetDiskDetailByDiskId.DoGet") + } + + disk := &DiskDetails{} + err = resp.Unmarshal(disk, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "Region.GetDiskDetailByDiskId.Unmarshal") + } + + return disk, nil +} + +func (self *SRegion) CreateDisk(zoneId, name, diskType, size string) (*SDisk, error) { + diskParams := jsonutils.NewDict() + diskParams.Set("regionId", jsonutils.NewString(self.GetId())) + diskParams.Set("zoneId", jsonutils.NewString(zoneId)) + diskParams.Set("name", jsonutils.NewString(name)) + diskParams.Set("type", jsonutils.NewString(diskType)) + diskParams.Set("size", jsonutils.NewString(size)) + diskParams.Set("count", jsonutils.NewString("1")) + + params := map[string]jsonutils.JSONObject{ + "createVolumeInfo": diskParams, + } + + resp, err := self.client.DoPost("/apiproxy/v3/ondemand/createVolume", params) + if err != nil { + return nil, errors.Wrap(err, "Region.CreateDisk.DoPost") + } + + disk := &SDisk{} + err = resp.Unmarshal(disk) + if err != nil { + return nil, errors.Wrap(err, "Region.CreateDisk.Unmarshal") + } + + izone, err := self.GetIZoneById(disk.AvailabilityZone) + if err != nil { + return nil, errors.Wrap(err, "SRegion.CreateDisk.GetIZoneById") + } + + disk.storage = &SStorage{ + zone: izone.(*SZone), + storageType: disk.VolumeType, + } + + return disk, nil +} diff --git a/pkg/multicloud/ctyun/diskbackup_policy.go b/pkg/multicloud/ctyun/diskbackup_policy.go new file mode 100644 index 0000000000..bccd44f47c --- /dev/null +++ b/pkg/multicloud/ctyun/diskbackup_policy.go @@ -0,0 +1,214 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "strconv" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SDiskBacupPolicy struct { + region *SRegion + + PolicyResourceCount int64 `json:"policy_resource_count"` + BackupPolicyName string `json:"backup_policy_name"` + ScheduledPolicy ScheduledPolicy `json:"scheduled_policy"` + BackupPolicyID string `json:"backup_policy_id"` +} + +type ScheduledPolicy struct { + RententionNum int `json:"rentention_num"` + StartTime string `json:"start_time"` + Status string `json:"status"` + RemainFirstBackupOfCurMonth string `json:"remain_first_backup_of_curMonth"` + Frequency int `json:"frequency"` +} + +func (self *SDiskBacupPolicy) GetId() string { + return self.BackupPolicyID +} + +func (self *SDiskBacupPolicy) GetName() string { + return self.BackupPolicyName +} + +func (self *SDiskBacupPolicy) GetGlobalId() string { + return self.GetId() +} + +func (self *SDiskBacupPolicy) GetStatus() string { + if self.ScheduledPolicy.Status == "ON" || self.ScheduledPolicy.Status == "OFF" { + return api.SNAPSHOT_POLICY_READY + } + return api.SNAPSHOT_POLICY_UNKNOWN +} + +func (self *SDiskBacupPolicy) Refresh() error { + policy, err := self.region.GetDiskBackupPolicy(self.GetId()) + if err != nil { + return errors.Wrap(err, "SDiskBacupPolicy.Refresh") + } + + err = jsonutils.Update(self, policy) + if err != nil { + return errors.Wrap(err, "SDiskBacupPolicy.Refresh.Update") + } + + return nil +} + +func (self *SDiskBacupPolicy) IsEmulated() bool { + return false +} + +func (self *SDiskBacupPolicy) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SDiskBacupPolicy) GetProjectId() string { + return "" +} + +func (self *SDiskBacupPolicy) IsActivated() bool { + if self.ScheduledPolicy.Status == "ON" { + return true + } + + return false +} + +func (self *SDiskBacupPolicy) GetRetentionDays() int { + return self.ScheduledPolicy.RententionNum +} + +func (self *SDiskBacupPolicy) GetRepeatWeekdays() ([]int, error) { + return []int{self.ScheduledPolicy.Frequency}, nil +} + +func (self *SDiskBacupPolicy) GetTimePoints() ([]int, error) { + ret, err := strconv.Atoi(self.ScheduledPolicy.StartTime[0:2]) + return []int{ret}, err +} + +func (self *SRegion) GetDiskBackupPolices() ([]SDiskBacupPolicy, error) { + params := map[string]string{ + "regionId": self.GetId(), + } + + resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryDiskBackupPolicys", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetDiskBackupPolices.DoGet") + } + + ret := make([]SDiskBacupPolicy, 0) + err = resp.Unmarshal(&ret, "returnObj", "backup_policies") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetDiskBackupPolices.Unmarshal") + } + + for i := range ret { + ret[i].region = self + } + + return ret, nil +} + +func (self *SRegion) GetDiskBackupPolicy(policyId string) (*SDiskBacupPolicy, error) { + polices, err := self.GetDiskBackupPolices() + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetDiskBackupPolicy.GetDiskBackupPolices") + } + + for i := range polices { + policy := polices[i] + if policy.BackupPolicyID == policyId { + return &policy, nil + } + } + + return nil, errors.Wrap(cloudprovider.ErrNotFound, "SRegion.GetDiskBackupPolicy") +} + +func (self *SRegion) CreateDiskBackupPolicy(name, startTime, frequency, rententionNum, firstBackup, status string) error { + policyParams := jsonutils.NewDict() + policyParams.Set("policyName", jsonutils.NewString(name)) + scheduleParams := jsonutils.NewDict() + scheduleParams.Set("startTime", jsonutils.NewString(startTime)) + scheduleParams.Set("frequency", jsonutils.NewString(frequency)) + scheduleParams.Set("rententionNum", jsonutils.NewString(rententionNum)) + scheduleParams.Set("firstBackup", jsonutils.NewString(firstBackup)) + scheduleParams.Set("status", jsonutils.NewString(status)) + policyParams.Set("scheduledPolicy", scheduleParams) + + params := map[string]jsonutils.JSONObject{ + "regionId": jsonutils.NewString(self.GetId()), + "jsonStr": policyParams, + } + + _, err := self.client.DoPost("/apiproxy/v3/ondemand/createDiskBackupPolicy", params) + if err != nil { + return errors.Wrap(err, "SRegion.CreateDiskBackupPolicy.DoPost") + } + + return nil +} + +func (self *SRegion) BindingDiskBackupPolicy(policyId, resourceId, resourceType string) error { + policyParams := jsonutils.NewDict() + policyParams.Set("policyId", jsonutils.NewString(policyId)) + resourcesParams := jsonutils.NewArray() + resourcesParam := jsonutils.NewDict() + resourcesParam.Set("resourceId", jsonutils.NewString(resourceId)) + resourcesParam.Set("resourceType", jsonutils.NewString(resourceType)) + resourcesParams.Add(resourcesParam) + policyParams.Set("resources", resourcesParams) + + params := map[string]jsonutils.JSONObject{ + "regionId": jsonutils.NewString(self.GetId()), + "jsonStr": policyParams, + } + + _, err := self.client.DoPost("/apiproxy/v3/ondemand/bindResourceToPolicy", params) + if err != nil { + return errors.Wrap(err, "SRegion.BindingDiskBackupPolicy.DoPost") + } + + return nil +} + +func (self *SRegion) UnBindDiskBackupPolicy(policyId, resourceId string) error { + resourcesParams := jsonutils.NewArray() + resourcesParam := jsonutils.NewDict() + resourcesParam.Set("resource_id", jsonutils.NewString(resourceId)) + resourcesParams.Add(resourcesParam) + + params := map[string]jsonutils.JSONObject{ + "regionId": jsonutils.NewString(self.GetId()), + "jsonStr": resourcesParams, + "policyId": jsonutils.NewString(policyId), + } + + _, err := self.client.DoPost("/apiproxy/v3/ondemand/unBindResourceToPolicy", params) + if err != nil { + return errors.Wrap(err, "SRegion.UnBindDiskBackupPolicy.DoPost") + } + + return nil +} diff --git a/pkg/multicloud/ctyun/doc.go b/pkg/multicloud/ctyun/doc.go new file mode 100644 index 0000000000..68aff1edcb --- /dev/null +++ b/pkg/multicloud/ctyun/doc.go @@ -0,0 +1 @@ +package ctyun // import "yunion.io/x/onecloud/pkg/multicloud/ctyun" diff --git a/pkg/multicloud/ctyun/eip.go b/pkg/multicloud/ctyun/eip.go new file mode 100644 index 0000000000..32844d8337 --- /dev/null +++ b/pkg/multicloud/ctyun/eip.go @@ -0,0 +1,264 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "strings" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + + billing_api "yunion.io/x/onecloud/pkg/apis/billing" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SEip struct { + region *SRegion + + IPVersion int64 `json:"ip_version"` + BandwidthShareType string `json:"bandwidth_share_type"` + Type string `json:"type"` + PrivateIPAddress string `json:"private_ip_address"` + EnterpriseProjectID string `json:"enterprise_project_id"` + Status string `json:"status"` + PublicIPAddress string `json:"public_ip_address"` + ID string `json:"id"` + TenantID string `json:"tenant_id"` + Profile Profile `json:"profile"` + BandwidthName string `json:"bandwidth_name"` + BandwidthID string `json:"bandwidth_id"` + PortID string `json:"port_id"` + BandwidthSize int `json:"bandwidth_size"` + CreateTime int64 `json:"create_time"` + MasterOrderID string `json:"masterOrderId"` + WorkOrderResourceID string `json:"workOrderResourceId"` + ExpireTime int64 `json:"expireTime"` + IsFreeze int64 `json:"isFreeze"` +} + +func (self *SEip) GetBillingType() string { + if len(self.MasterOrderID) > 0 { + return billing_api.BILLING_TYPE_PREPAID + } else { + return billing_api.BILLING_TYPE_POSTPAID + } +} + +func (self *SEip) GetCreatedAt() time.Time { + return time.Unix(self.CreateTime/1000, 0) +} + +func (self *SEip) GetExpiredAt() time.Time { + return time.Unix(self.ExpireTime/1000, 0) +} + +func (self *SEip) GetId() string { + return self.ID +} + +func (self *SEip) GetName() string { + return self.BandwidthName +} + +func (self *SEip) GetGlobalId() string { + return self.GetId() +} + +func (self *SEip) GetStatus() string { + switch self.Status { + case "bind", "unbind": + return api.EIP_STATUS_READY + case "binding": + return api.EIP_STATUS_ALLOCATE + case "error": + return api.EIP_STATUS_ALLOCATE_FAIL + case "unbinding": + return api.EIP_STATUS_DEALLOCATE + default: + return api.EIP_STATUS_UNKNOWN + } +} + +func (self *SEip) Refresh() error { + if self.IsEmulated() { + return nil + } + new, err := self.region.GetEip(self.ID) + if err != nil { + return err + } + return jsonutils.Update(self, new) +} + +func (self *SEip) IsEmulated() bool { + return false +} + +func (self *SEip) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SEip) GetProjectId() string { + return "" +} + +func (self *SEip) GetIpAddr() string { + return self.PublicIPAddress +} + +func (self *SEip) GetMode() string { + return api.EIP_MODE_STANDALONE_EIP +} + +func (self *SEip) GetINetworkId() string { + return "" +} + +func (self *SEip) GetAssociationType() string { + orders, err := self.region.GetOrder(self.WorkOrderResourceID) + if err != nil { + log.Errorf("SEip.GetAssociationType %s", err) + return "" + } + + for i := range orders { + order := orders[i] + if strings.Contains(order.ResourceType, "LOADBALANCER") { + return api.EIP_ASSOCIATE_TYPE_ELB + } else { + return api.EIP_ASSOCIATE_TYPE_SERVER + } + } + + return "" +} + +func (self *SEip) GetAssociationExternalId() string { + return self.WorkOrderResourceID +} + +// http://ctyun-api-url/apiproxy/v3/queryNetworkDetail +func (self *SEip) GetBandwidth() int { + return self.BandwidthSize +} + +func (self *SEip) GetInternetChargeType() string { + // todo: fix me + return api.EIP_CHARGE_TYPE_BY_BANDWIDTH +} + +func (self *SEip) Delete() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SEip) Associate(instanceId string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SEip) Dissociate() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SEip) ChangeBandwidth(bw int) error { + return cloudprovider.ErrNotImplemented +} + +type Profile struct { + OrderID string `json:"order_id"` + RegionID string `json:"region_id"` + UserID string `json:"user_id"` + ProductID string `json:"product_id"` +} + +func (self *SRegion) GetEips() ([]SEip, error) { + params := map[string]string{ + "regionId": self.GetId(), + } + + eips := make([]SEip, 0) + resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryIps", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetEips.DoGet") + } + + err = resp.Unmarshal(&eips, "returnObj", "publicips") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetEips.Unmarshal") + } + + for i := range eips { + eips[i].region = self + } + + return eips, nil +} + +func (self *SRegion) GetEip(eipId string) (*SEip, error) { + params := map[string]string{ + "regionId": self.GetId(), + "publicIpId": eipId, + } + + eips := make([]SEip, 0) + resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryIps", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetEip.DoGet") + } + + err = resp.Unmarshal(&eips, "returnObj", "publicips") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetEip.Unmarshal") + } + + if len(eips) == 0 { + return nil, errors.Wrap(cloudprovider.ErrNotFound, "SRegion.GetEip") + } else if len(eips) == 1 { + eips[0].region = self + return &eips[0], nil + } else { + return nil, errors.Wrap(cloudprovider.ErrDuplicateId, "SRegion.GetEip") + } +} + +func (self *SRegion) CreateEip(zoneId, name, size, shareType string) (*SEip, error) { + eipParams := jsonutils.NewDict() + eipParams.Set("regionId", jsonutils.NewString(self.GetId())) + eipParams.Set("zoneId", jsonutils.NewString(zoneId)) + eipParams.Set("name", jsonutils.NewString(name)) + eipParams.Set("type", jsonutils.NewString("5_telcom")) + eipParams.Set("size", jsonutils.NewString(size)) + eipParams.Set("shareType", jsonutils.NewString(shareType)) + + params := map[string]jsonutils.JSONObject{ + "createIpInfo": eipParams, + } + + eip := &SEip{} + resp, err := self.client.DoPost("/apiproxy/v3/ondemand/createIp", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.CreateEip.DoPost") + } + + err = resp.Unmarshal(eip, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.CreateEip.Unmarshal") + } + + eip.region = self + return eip, nil +} diff --git a/pkg/multicloud/ctyun/host.go b/pkg/multicloud/ctyun/host.go new file mode 100644 index 0000000000..cb36a5a2eb --- /dev/null +++ b/pkg/multicloud/ctyun/host.go @@ -0,0 +1,231 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SHost struct { + multicloud.SHostBase + zone *SZone + + projectId string +} + +func (self *SHost) GetId() string { + return fmt.Sprintf("%s-%s", self.zone.region.client.providerId, self.zone.GetId()) +} + +func (self *SHost) GetName() string { + return fmt.Sprintf("%s-%s", self.zone.region.client.providerName, self.zone.GetId()) +} + +func (self *SHost) GetGlobalId() string { + return self.GetId() +} + +func (self *SHost) GetStatus() string { + return api.HOST_STATUS_RUNNING +} + +func (self *SHost) Refresh() error { + return nil +} + +func (self *SHost) IsEmulated() bool { + return true +} + +func (self *SHost) GetMetadata() *jsonutils.JSONDict { + return nil +} + +// http://ctyun-api-url/apiproxy/v3/ondemand/queryVMs +func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) { + vms, err := self.zone.region.GetVMs() + if err != nil { + return nil, errors.Wrap(err, "SHost.GetVMs") + } + + ivms := make([]cloudprovider.ICloudVM, len(vms)) + for i := range vms { + ivms[i] = &vms[i] + } + + return ivms, nil +} + +// http://ctyun-api-url/apiproxy/v3/ondemand/queryVMDetail +// http://ctyun-api-url/apiproxy/v3/queryVMDetail +func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) { + return self.zone.region.GetIVMById(id) +} + +func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) { + return self.zone.GetIWires() +} + +func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + return self.zone.GetIStorages() +} + +func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + return self.zone.GetIStorageById(id) +} + +func (self *SHost) GetEnabled() bool { + return true +} + +func (self *SHost) GetHostStatus() string { + return api.HOST_ONLINE +} + +func (self *SHost) GetAccessIp() string { + return "" +} + +func (self *SHost) GetAccessMac() string { + return "" +} + +func (self *SHost) GetSysInfo() jsonutils.JSONObject { + info := jsonutils.NewDict() + info.Add(jsonutils.NewString(api.CLOUD_PROVIDER_CTYUN), "manufacture") + return info +} + +func (self *SHost) GetSN() string { + return "" +} + +func (self *SHost) GetCpuCount() int { + return 0 +} + +func (self *SHost) GetNodeCount() int8 { + return 0 +} + +func (self *SHost) GetCpuDesc() string { + return "" +} + +func (self *SHost) GetCpuMhz() int { + return 0 +} + +func (self *SHost) GetMemSizeMB() int { + return 0 +} + +func (self *SHost) GetStorageSizeMB() int { + return 0 +} + +func (self *SHost) GetStorageType() string { + return api.DISK_TYPE_HYBRID +} + +func (self *SHost) GetHostType() string { + return api.HOST_TYPE_CTYUN +} + +func (self *SHost) GetIsMaintenance() bool { + return false +} + +func (self *SHost) GetVersion() string { + return CTYUN_API_VERSION +} + +func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SRegion) getVMs(vmId string) ([]SInstance, error) { + params := map[string]string{ + "regionId": self.GetId(), + } + + if len(vmId) > 0 { + params["vmId"] = vmId + } + + resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryVMs", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.getVMs.DoGet") + } + + ret := make([]SInstance, 0) + err = resp.Unmarshal(&ret, "returnObj", "servers") + if err != nil { + return nil, errors.Wrap(err, "SRegion.getVMs.Unmarshal") + } + + for i := range ret { + izone, err := self.GetIZoneById(ret[i].OSEXTAZAvailabilityZone) + if err != nil { + return nil, errors.Wrap(err, "SRegion.getVMs.GetIZoneById") + } + + ret[i].host = &SHost{ + zone: izone.(*SZone), + projectId: self.client.projectId, + } + } + + return ret, nil +} + +func (self *SRegion) GetVMs() ([]SInstance, error) { + return self.getVMs("") +} + +func (self *SRegion) GetVMById(vmId string) (*SInstance, error) { + vms, err := self.getVMs(vmId) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetVMById") + } + + if len(vms) == 0 { + return nil, errors.Wrap(cloudprovider.ErrNotFound, "SRegion.GetVMById") + } else if len(vms) == 1 { + izone, err := self.GetIZoneById(vms[0].OSEXTAZAvailabilityZone) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetVMById.GetIZoneById") + } + + vms[0].host = &SHost{ + zone: izone.(*SZone), + projectId: self.client.projectId, + } + return &vms[0], nil + } else { + return nil, errors.Wrap(cloudprovider.ErrDuplicateId, "SRegion.GetVMById") + } +} diff --git a/pkg/multicloud/ctyun/image.go b/pkg/multicloud/ctyun/image.go new file mode 100644 index 0000000000..3f72819977 --- /dev/null +++ b/pkg/multicloud/ctyun/image.go @@ -0,0 +1,204 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "context" + "strconv" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/util/imagetools" +) + +const ( + ImageOwnerPublic string = "gold" // 公共镜像:gold + ImageOwnerSelf string = "private" // 私有镜像:private + ImageOwnerShared string = "shared" // 共享镜像:shared +) + +// http://ctyun-api-url/apiproxy/v3/order/getImages +type SImage struct { + storageCache *SStoragecache + + ID string `json:"id"` + OSType string `json:"osType"` + Platform string `json:"platform"` + Name string `json:"name"` + OSBit int64 `json:"osBit"` + MinRAM int64 `json:"minRam"` + MinDisk int64 `json:"minDisk"` + ImageType string `json:"imageType"` + Virtual bool `json:"virtual"` +} + +func (self *SImage) GetCreatedAt() time.Time { + return time.Time{} +} + +func (self *SImage) GetId() string { + return self.ID +} + +func (self *SImage) GetName() string { + return self.Name +} + +func (self *SImage) GetGlobalId() string { + return self.GetId() +} + +func (self *SImage) GetStatus() string { + return api.CACHED_IMAGE_STATUS_READY +} + +func (self *SImage) Refresh() error { + new, err := self.storageCache.region.GetImage(self.GetId()) + if err != nil { + return err + } + return jsonutils.Update(self, new) +} + +func (self *SImage) IsEmulated() bool { + return false +} + +func (self *SImage) GetMetadata() *jsonutils.JSONDict { + data := jsonutils.NewDict() + if self.OSBit > 0 { + data.Add(jsonutils.NewString(self.GetOsArch()), "os_arch") + } + if len(self.OSType) > 0 { + data.Add(jsonutils.NewString(self.GetOsType()), "os_name") + } + if len(self.Platform) > 0 { + data.Add(jsonutils.NewString(self.GetOsDist()), "os_distribution") + } + + data.Add(jsonutils.NewString(self.GetOsVersion()), "os_version") + + return data +} + +func (self *SImage) Delete(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache { + return self.storageCache +} + +func (self *SImage) GetSizeByte() int64 { + return self.MinDisk * 1024 * 1024 * 1024 +} + +func (self *SImage) GetImageType() string { + switch self.ImageType { + case "gold": + return cloudprovider.CachedImageTypeSystem + case "private": + return cloudprovider.CachedImageTypeCustomized + case "shared": + return cloudprovider.CachedImageTypeShared + default: + return cloudprovider.CachedImageTypeCustomized + } +} + +func (self *SImage) GetImageStatus() string { + return cloudprovider.IMAGE_STATUS_ACTIVE +} + +func (self *SImage) GetOsType() string { + return self.OSType +} + +func (self *SImage) GetOsDist() string { + return self.Platform +} + +func (self *SImage) GetOsVersion() string { + return imagetools.NormalizeImageInfo(self.Name, "", "", "", "").OsVersion +} + +func (self *SImage) GetOsArch() string { + return strconv.Itoa(int(self.OSBit)) +} + +func (self *SImage) GetMinOsDiskSizeGb() int { + return int(self.MinDisk) +} + +func (self *SImage) GetMinRamSizeMb() int { + return int(self.MinRAM) +} + +func (self *SImage) GetImageFormat() string { + return "" +} + +func (self *SImage) GetCreateTime() time.Time { + return time.Time{} +} + +func (self *SRegion) GetImages(imageType string) ([]SImage, error) { + params := map[string]string{ + "regionId": self.GetId(), + "imageType": imageType, + } + + resp, err := self.client.DoGet("/apiproxy/v3/order/getImages", params) + if err != nil { + return nil, errors.Wrap(err, "Region.GetImages.DoGet") + } + + images := make([]SImage, 0) + err = resp.Unmarshal(&images, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "Region.GetImages.Unmarshal") + } + + for i := range images { + images[i].storageCache = &SStoragecache{ + region: self, + } + } + + return images, nil +} + +func (self *SRegion) GetImage(imageId string) (*SImage, error) { + images, err := self.GetImages("") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetImage.GetImages") + } + + for i := range images { + if images[i].GetId() == imageId { + images[i].storageCache = &SStoragecache{ + region: self, + } + + return &images[i], nil + } + } + + return nil, errors.Wrap(cloudprovider.ErrNotFound, "SRegion.GetImage") +} diff --git a/pkg/multicloud/ctyun/instance.go b/pkg/multicloud/ctyun/instance.go new file mode 100644 index 0000000000..9f6a67a703 --- /dev/null +++ b/pkg/multicloud/ctyun/instance.go @@ -0,0 +1,560 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "context" + "fmt" + "strings" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + + billing_api "yunion.io/x/onecloud/pkg/apis/billing" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" + "yunion.io/x/onecloud/pkg/util/billing" +) + +type SInstance struct { + multicloud.SInstanceBase + + host *SHost + image *SImage + + vmDetails *InstanceDetails + + HostID string `json:"hostId"` + ID string `json:"id"` + Name string `json:"name"` + Status string `json:"status"` + TenantID string `json:"tenant_id"` + Metadata Metadata `json:"metadata"` + Image Image `json:"image"` + Flavor FlavorObj `json:"flavor"` + Addresses map[string][]Address `json:"addresses"` + UserID string `json:"user_id"` + Created int64 `json:"created"` + DueDate int64 `json:"dueDate"` + SecurityGroups []SecurityGroup `json:"security_groups"` + OSEXTAZAvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` + OSExtendedVolumesVolumesAttached []Volume `json:"os-extended-volumes:volumes_attached"` +} + +type InstanceDetails struct { + HostID string `json:"hostId"` + Name string `json:"name"` + Status string `json:"status"` + PrivateIPS []PrivateIP `json:"privateIps"` + PublicIPS []PublicIP `json:"publicIps"` + Volumes []Volume `json:"volumes"` + Created string `json:"created"` + FlavorObj FlavorObj `json:"flavorObj"` +} + +type Address struct { + Addr string `json:"addr"` + OSEXTIPSType string `json:"OS-EXT-IPS:type"` + Version int64 `json:"version"` + OSEXTIPSMACMACAddr string `json:"OS-EXT-IPS-MAC:mac_addr"` +} + +type Image struct { + Id string `json:"id"` +} + +type SecurityGroup struct { + Name string `json:"name"` +} + +type FlavorObj struct { + Name string `json:"name"` + CPUNum int `json:"cpuNum"` + MemSize int `json:"memSize"` + ID string `json:"id"` +} + +type PrivateIP struct { + ID string `json:"id"` + Address string `json:"address"` +} + +type PublicP struct { + ID string `json:"id"` + Address string `json:"address"` + Bandwidth string `json:"bandwidth"` +} + +func (self *SInstance) GetBillingType() string { + if self.DueDate > 0 { + return billing_api.BILLING_TYPE_PREPAID + } else { + return billing_api.BILLING_TYPE_POSTPAID + } +} + +func (self *SInstance) GetCreatedAt() time.Time { + return time.Unix(self.Created/1000, 0) +} + +func (self *SInstance) GetExpiredAt() time.Time { + return time.Unix(self.DueDate/1000, 0) +} + +func (self *SInstance) GetId() string { + return self.ID +} + +func (self *SInstance) GetName() string { + return self.Name +} + +func (self *SInstance) GetGlobalId() string { + return self.GetId() +} + +func (self *SInstance) GetStatus() string { + switch self.Status { + case "RUNNING", "ACTIVE": + return api.VM_RUNNING + case "RESTARTING", "BUILD", "RESIZE", "VERIFY_RESIZE": + return api.VM_STARTING + case "STOPPING", "HARD_REBOOT": + return api.VM_STOPPING + case "STOPPED", "SHUTOFF": + return api.VM_READY + default: + return api.VM_UNKNOWN + } +} + +func (self *SInstance) Refresh() error { + new, err := self.host.zone.region.GetVMById(self.GetId()) + new.host = self.host + if err != nil { + return err + } + + if new.Status == "DELETED" { + log.Debugf("Instance already terminated.") + return cloudprovider.ErrNotFound + } + + return jsonutils.Update(self, new) +} + +func (self *SInstance) IsEmulated() bool { + return false +} + +func (self *SInstance) GetMetadata() *jsonutils.JSONDict { + data := jsonutils.NewDict() + lowerOs := self.GetOSType() + if strings.HasPrefix(lowerOs, "win") { + lowerOs = "win" + } + priceKey := fmt.Sprintf("%s::%s::%s", self.host.zone.region.GetId(), self.GetInstanceType(), lowerOs) + data.Add(jsonutils.NewString(priceKey), "price_key") + data.Add(jsonutils.NewString(self.host.zone.GetGlobalId()), "zone_ext_id") + return data +} + +func (self *SInstance) GetProjectId() string { + return "" +} + +func (self *SInstance) GetIHost() cloudprovider.ICloudHost { + return self.host +} + +// GET http://ctyun-api-url/apiproxy/v3/queryDataDiskByVMId +func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + err := self.Refresh() + if err != nil { + return nil, errors.Wrap(err, "SInstance.GetIDisks.Refresh") + } + + disks, err := self.host.zone.region.GetVMDisks(self.GetId()) + if err != nil { + return nil, errors.Wrap(err, "SInstance.GetIDisks.GetVMDisks") + } + + idisks := make([]cloudprovider.ICloudDisk, 0) + for i := range disks { + disk := disks[i] + idisks[i] = &disk + } + + return idisks, nil +} + +func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) { + nics, err := self.host.zone.region.GetNics(self.GetId()) + if err != nil { + return nil, errors.Wrap(err, "SInstance.GetINics") + } + + inics := make([]cloudprovider.ICloudNic, len(nics)) + for i := range nics { + inics[i] = &nics[i] + } + + return inics, nil +} + +// GET http://ctyun-api-urlapiproxy/v3/queryNetworkByVMId +func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { + detail, err := self.GetDetails() + if err != nil { + return nil, errors.Wrap(err, "SInstance.GetIEIP.GetDetails") + } + + if len(detail.PublicIPS) > 0 { + return self.host.zone.region.GetEip(detail.PublicIPS[0].ID) + } + + return nil, nil +} + +func (self *SInstance) GetDetails() (*InstanceDetails, error) { + if self.vmDetails != nil { + return self.vmDetails, nil + } + + detail, err := self.host.zone.region.GetVMDetails(self.GetId()) + if err != nil { + return nil, errors.Wrap(err, "SInstance.GetDetails") + } + + self.vmDetails = detail + return self.vmDetails, nil +} + +func (self *SInstance) GetVcpuCount() int { + return self.Flavor.CPUNum +} + +func (self *SInstance) GetVmemSizeMB() int { + return self.Flavor.MemSize * 1024 +} + +func (self *SInstance) GetBootOrder() string { + return "dcn" +} + +func (self *SInstance) GetVga() string { + return "std" +} + +func (self *SInstance) GetVdi() string { + return "vnc" +} + +func (self *SInstance) GetImage() (*SImage, error) { + if self.image != nil { + return self.image, nil + } + + image, err := self.host.zone.region.GetImage(self.Image.Id) + if err != nil { + return nil, errors.Wrap(err, "SInstance.GetImage") + } + + self.image = image + return self.image, nil +} + +func (self *SInstance) GetOSType() string { + image, err := self.GetImage() + if err != nil { + log.Errorf("SInstance.GetOSType %s", err) + return "" + } + + return image.OSType +} + +func (self *SInstance) GetOSName() string { + image, err := self.GetImage() + if err != nil { + log.Errorf("SInstance.GetOSName %s", err) + return "" + } + + return image.Name +} + +func (self *SInstance) GetBios() string { + return "BIOS" +} + +func (self *SInstance) GetMachine() string { + return "pc" +} + +func (self *SInstance) GetInstanceType() string { + return self.Flavor.ID +} + +func (self *SInstance) GetSecurityGroupIds() ([]string, error) { + return nil, nil +} + +func (self *SInstance) AssignSecurityGroup(secgroupId string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) SetSecurityGroups(secgroupIds []string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) GetHypervisor() string { + return api.HYPERVISOR_CTYUN +} + +func (self *SInstance) StartVM(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) StopVM(ctx context.Context, isForce bool) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) DeleteVM(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) UpdateVM(ctx context.Context, name string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) UpdateUserData(userData string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { + return cloudprovider.ErrNotImplemented +} + +// http://ctyun-api-url/apiproxy/v3/queryVncUrl +func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) { + url, err := self.host.zone.region.GetInstanceVNCUrl(self.GetId()) + if err != nil { + return nil, err + } + ret := jsonutils.NewDict() + ret.Add(jsonutils.NewString(url), "url") + ret.Add(jsonutils.NewString("ctyun"), "protocol") + ret.Add(jsonutils.NewString(self.GetId()), "instance_id") + return ret, nil +} + +func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) Renew(bc billing.SBillingCycle) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) GetError() error { + return nil +} + +func (self *SRegion) GetVMDisks(vmId string) ([]SDisk, error) { + params := map[string]string{ + "VMId": vmId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/queryDataDiskByVMId", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetVMDisks.DoGet") + } + + ret := make([]SDisk, 0) + err = resp.Unmarshal(&ret, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetVMDisks.Unmarshal") + } + + disks := []SDisk{} + for i := 0; i < len(ret); i += 1 { + // 将系统盘放到第0个位置 + if ret[i].IsSysVolume == 1 { + _temp := ret[0] + disks[0] = ret[i] + disks[i] = _temp + } + } + + return ret, nil +} + +type SDiskDetails struct { + HostID string `json:"hostId"` + Name string `json:"name"` + Status string `json:"status"` + PrivateIPS []PrivateIP `json:"privateIps"` + PublicIPS []PublicIP `json:"publicIps"` + Volumes []Volume `json:"volumes"` + Created string `json:"created"` + FlavorObj FlavorObj `json:"flavorObj"` +} + +type PublicIP struct { + ID string `json:"id"` + Address string `json:"address"` + Bandwidth string `json:"bandwidth"` +} + +type Volume struct { + ID string `json:"id"` + Status string `json:"status"` + Type string `json:"type"` + Size string `json:"size"` + Name string `json:"name"` + Bootable bool `json:"bootable"` +} + +func (self *SRegion) GetVMDetails(vmId string) (*InstanceDetails, error) { + params := map[string]string{ + "regionId": self.GetId(), + "vmId": vmId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryVMDetail", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetVMDetails.DoGet") + } + + details := &InstanceDetails{} + err = resp.Unmarshal(details, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetVMDetails.Unmarshal") + } + + return details, nil +} + +type SVncInfo struct { + Type string `json:"type"` + URL string `json:"url"` +} + +func (self *SRegion) GetInstanceVNCUrl(vmId string) (string, error) { + params := map[string]string{ + "regionId": self.GetId(), + "vmId": vmId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/queryVncUrl", params) + if err != nil { + return "", errors.Wrap(err, "") + } + + ret := SVncInfo{} + err = resp.Unmarshal(&ret, "returnObj", "console") + if err != nil { + return "", errors.Wrap(err, "") + } + + return ret.URL, nil +} + +func (self *SRegion) CreateInstance(zoneId, name, imageId, volumetype, flavorRef, vpcid, subnetId, secGroupId, adminPass string) error { + rootParams := jsonutils.NewDict() + rootParams.Set("volumetype", jsonutils.NewString(volumetype)) + + nicParams := jsonutils.NewArray() + nicParam := jsonutils.NewDict() + nicParam.Set("subnet_id", jsonutils.NewString(subnetId)) + nicParams.Add(nicParam) + + secgroupParams := jsonutils.NewArray() + secgroupParam := jsonutils.NewDict() + secgroupParam.Set("id", jsonutils.NewString(secGroupId)) + secgroupParams.Add(secgroupParam) + + extParams := jsonutils.NewDict() + extParams.Set("regionID", jsonutils.NewString(self.GetId())) + + serverParams := jsonutils.NewDict() + serverParams.Set("availability_zone", jsonutils.NewString(zoneId)) + serverParams.Set("name", jsonutils.NewString(name)) + serverParams.Set("imageRef", jsonutils.NewString(imageId)) + serverParams.Set("root_volume", rootParams) + serverParams.Set("flavorRef", jsonutils.NewString(flavorRef)) + // todo: fix me + serverParams.Set("osType", jsonutils.NewString("Linux")) + serverParams.Set("vpcid", jsonutils.NewString(vpcid)) + serverParams.Set("security_groups", secgroupParams) + serverParams.Set("nics", nicParams) + serverParams.Set("adminPass", jsonutils.NewString(adminPass)) + serverParams.Set("count", jsonutils.NewString("1")) + serverParams.Set("extendparam", extParams) + + vmParams := jsonutils.NewDict() + vmParams.Set("server", serverParams) + + params := map[string]jsonutils.JSONObject{ + "createVMInfo": vmParams, + } + + _, err := self.client.DoPost("/apiproxy/v3/ondemand/createVM", params) + if err != nil { + return errors.Wrap(err, "SRegion.CreateInstance.DoPost") + } + + return nil +} + +func (self *SRegion) GetJob(jobId string) (jsonutils.JSONObject, error) { + params := map[string]string{ + "regionId": self.GetId(), + "jobId": jobId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/queryJobStatus", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetJob.DoGet") + } + + ret := jsonutils.NewDict() + err = resp.Unmarshal(&ret) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetJob.Unmarshal") + } + + return ret, nil +} diff --git a/pkg/multicloud/ctyun/instancenic.go b/pkg/multicloud/ctyun/instancenic.go new file mode 100644 index 0000000000..8cf5e8a4d2 --- /dev/null +++ b/pkg/multicloud/ctyun/instancenic.go @@ -0,0 +1,90 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/netutils" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SInstanceNic struct { + instance *SInstance + ipAddr string + FixedIPS []FixedIP `json:"fixed_ips"` + PortState string `json:"port_state"` + PortID string `json:"port_id"` + MACAddr string `json:"mac_addr"` + NetID string `json:"net_id"` +} + +type FixedIP struct { + IPAddress string `json:"ip_address"` + SubnetID string `json:"subnet_id"` +} + +func (self *SInstanceNic) GetIP() string { + return self.ipAddr +} + +func (self *SInstanceNic) GetMAC() string { + ip, _ := netutils.NewIPV4Addr(self.ipAddr) + return ip.ToMac("00:16:") +} + +func (self *SInstanceNic) GetDriver() string { + return "virtio" +} + +func (self *SInstanceNic) GetINetwork() cloudprovider.ICloudNetwork { + network, err := self.instance.host.zone.region.GetNetwork(self.NetID) + if err != nil { + log.Errorf("SInstanceNic.GetINetwork %s", err) + return nil + } + + return network +} + +func (self *SRegion) GetNics(vmId string) ([]SInstanceNic, error) { + params := map[string]string{ + "regionId": self.GetId(), + "vmId": vmId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/queryNetworkCards", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetNics.DoGet") + } + + ret := make([]SInstanceNic, 0) + err = resp.Unmarshal(&ret, "returnObj", "interfaceAttachments") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetNics.Unmarshal") + } + + for i := range ret { + ins, err := self.GetVMById(vmId) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetNics") + } + + ret[i].instance = ins + } + + return ret, nil +} diff --git a/pkg/multicloud/ctyun/keypair.go b/pkg/multicloud/ctyun/keypair.go new file mode 100644 index 0000000000..bd228d116c --- /dev/null +++ b/pkg/multicloud/ctyun/keypair.go @@ -0,0 +1,19 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +// http://ctyun-api-url/apiproxy/v3/order/getZoneConfig +type SKeypair struct { +} diff --git a/pkg/multicloud/ctyun/latitud_and_longitude.go b/pkg/multicloud/ctyun/latitud_and_longitude.go new file mode 100644 index 0000000000..80c31ab4f6 --- /dev/null +++ b/pkg/multicloud/ctyun/latitud_and_longitude.go @@ -0,0 +1,61 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +var LatitudeAndLongitude = map[string]cloudprovider.SGeographicInfo{ + "cn-beijing1": {Latitude: 39.997743, Longitude: 116.304542, City: api.CITY_BEI_JING, CountryCode: api.COUNTRY_CODE_CN}, + "cn-hz1": {Latitude: 30.274084, Longitude: 120.155067, City: api.CITY_HANG_ZHOU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-gslz1": {Latitude: 36.0613769373, Longitude: 103.8341600069, City: api.CITY_LAN_ZHOU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-sxty1": {Latitude: 37.8705857132, Longitude: 112.5506634865, City: api.CITY_TAI_YUAN, CountryCode: api.COUNTRY_CODE_CN}, + "cn-sh1": {Latitude: 31.210344, Longitude: 121.455364, City: api.CITY_SHANG_HAI, CountryCode: api.COUNTRY_CODE_CN}, + "cn-gz1": {Latitude: 26.6470035286, Longitude: 106.6302113880, City: api.CITY_GUI_YANG, CountryCode: api.COUNTRY_CODE_CN}, + "cn-sdqd1": {Latitude: 36.067108, Longitude: 120.382607, City: api.CITY_QING_DAO, CountryCode: api.COUNTRY_CODE_CN}, + "cn-tj1": {Latitude: 39.0850853357, Longitude: 117.1993482089, City: api.CITY_TIAN_JIN, CountryCode: api.COUNTRY_CODE_CN}, + "cn-xjcj1": {Latitude: 43.8266013700, Longitude: 87.6168405804, City: api.CITY_WU_LU_MU_QI, CountryCode: api.COUNTRY_CODE_CN}, + "cn-cq1": {Latitude: 29.431585, Longitude: 106.912254, City: api.CITY_CHONG_QING, CountryCode: api.COUNTRY_CODE_CN}, + "cn-gxnn1": {Latitude: 22.8167372565, Longitude: 108.3669005333, City: api.CITY_NAN_NING, CountryCode: api.COUNTRY_CODE_CN}, + "cn-hazz1": {Latitude: 34.7533581487, Longitude: 113.6313915479, City: api.CITY_ZHENG_ZHOU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-ynkm1": {Latitude: 24.8796595146, Longitude: 102.8332118852, City: api.CITY_KUN_MING, CountryCode: api.COUNTRY_CODE_CN}, + "cn-xian1": {Latitude: 34.3412614674, Longitude: 108.9398165260, City: api.CITY_XI_AN, CountryCode: api.COUNTRY_CODE_CN}, + "cn-hihk1": {Latitude: 20.0442268036, Longitude: 110.1998910288, City: api.CITY_HAI_KOU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-ahwh1": {Latitude: 31.3524675159, Longitude: 118.4331307290, City: api.CITY_WU_HU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-fz1": {Latitude: 26.0741979397, Longitude: 119.2964466153, City: api.CITY_FU_ZHOU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-nmhh1": {Latitude: 40.842358, Longitude: 111.749992, City: api.CITY_HU_HE_HAO_TE, CountryCode: api.COUNTRY_CODE_CN}, + "cn-shanghai2": {Latitude: 31.210344, Longitude: 121.455364, City: api.CITY_SHANG_HAI, CountryCode: api.COUNTRY_CODE_CN}, + "cn-snxy1": {Latitude: 34.3412614674, Longitude: 108.9398165260, City: api.CITY_XI_AN, CountryCode: api.COUNTRY_CODE_CN}, + "cn-hbwh1": {Latitude: 30.5927599029, Longitude: 114.3052387810, City: api.CITY_WU_HAN, CountryCode: api.COUNTRY_CODE_CN}, + "cn-hncs1": {Latitude: 28.2277765095, Longitude: 112.9388453666, City: api.CITY_CHANG_SHA, CountryCode: api.COUNTRY_CODE_CN}, + "cn-guangzhou2": {Latitude: 23.12911, Longitude: 113.264385, City: api.CITY_GUANG_ZHOU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-guizhou2": {Latitude: 26.6470035286, Longitude: 106.6302113880, City: api.CITY_GUI_YANG, CountryCode: api.COUNTRY_CODE_CN}, + "cn-jssz1": {Latitude: 31.2983479333, Longitude: 120.5831894861, City: api.CITY_SHU_ZHOU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-sccd1": {Latitude: 30.572815, Longitude: 104.066803, City: api.CITY_CHENG_DU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-guangzhou3": {Latitude: 23.12911, Longitude: 113.264385, City: api.CITY_GUANG_ZHOU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-shanghai3": {Latitude: 31.210344, Longitude: 121.455364, City: api.CITY_SHANG_HAI, CountryCode: api.COUNTRY_CODE_CN}, + "cn-neimeng4": {Latitude: 40.842358, Longitude: 111.749992, City: api.CITY_HU_HE_HAO_TE, CountryCode: api.COUNTRY_CODE_CN}, + "cn-beijing3": {Latitude: 39.904202, Longitude: 116.407394, City: api.CITY_BEI_JING, CountryCode: api.COUNTRY_CODE_CN}, + "cn-baoding1": {Latitude: 38.8739745619, Longitude: 115.4646082830, City: api.CITY_BAO_DING, CountryCode: api.COUNTRY_CODE_CN}, + "cn-nj2": {Latitude: 32.0584065670, Longitude: 118.7964897811, City: api.CITY_NAN_JING, CountryCode: api.COUNTRY_CODE_CN}, + "cn-gdgz1": {Latitude: 23.12911, Longitude: 113.264385, City: api.CITY_GUANG_ZHOU, CountryCode: api.COUNTRY_CODE_CN}, + "cn-bj4": {Latitude: 39.904202, Longitude: 116.407394, City: api.CITY_BEI_JING, CountryCode: api.COUNTRY_CODE_CN}, + "cn-neimeng5": {Latitude: 40.842358, Longitude: 111.749992, City: api.CITY_HU_HE_HAO_TE, CountryCode: api.COUNTRY_CODE_CN}, + "cn-shanghai5": {Latitude: 31.210344, Longitude: 121.455364, City: api.CITY_SHANG_HAI, CountryCode: api.COUNTRY_CODE_CN}, + "cn-sh6": {Latitude: 31.210344, Longitude: 121.455364, City: api.CITY_SHANG_HAI, CountryCode: api.COUNTRY_CODE_CN}, + "cn-gdfs2": {Latitude: 23.0218629843, Longitude: 113.1219225896, City: api.CITY_FO_SHAN, CountryCode: api.COUNTRY_CODE_CN}, +} diff --git a/pkg/multicloud/ctyun/network.go b/pkg/multicloud/ctyun/network.go new file mode 100644 index 0000000000..6e3a682c9e --- /dev/null +++ b/pkg/multicloud/ctyun/network.go @@ -0,0 +1,241 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/netutils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/util/rbacutils" +) + +type SNetwork struct { + vpc *SVpc + wire *SWire + + CIDR string `json:"cidr"` + FirstDcn string `json:"firstDcn"` + Gateway string `json:"gateway"` + Name string `json:"name"` + NeutronSubnetID string `json:"neutronSubnetId"` + RegionID string `json:"regionId"` + ResVLANID string `json:"resVlanId"` + SecondDcn string `json:"secondDcn"` + VLANStatus string `json:"vlanStatus"` + VpcID string `json:"vpcId"` + ZoneID string `json:"zoneId"` + ZoneName string `json:"zoneName"` +} + +func (self *SNetwork) GetId() string { + return self.ResVLANID +} + +func (self *SNetwork) GetName() string { + return self.Name +} + +func (self *SNetwork) GetGlobalId() string { + return self.GetId() +} + +func (self *SNetwork) GetStatus() string { + switch self.VLANStatus { + case "ACTIVE", "UNKNOWN": + return api.NETWORK_STATUS_AVAILABLE + case "ERROR": + return api.NETWORK_STATUS_UNKNOWN + default: + return api.NETWORK_STATUS_UNKNOWN + } +} + +func (self *SNetwork) Refresh() error { + log.Debugf("network refresh %s", self.GetId()) + new, err := self.wire.region.GetNetwork(self.GetId()) + if err != nil { + return err + } + return jsonutils.Update(self, new) +} + +func (self *SNetwork) IsEmulated() bool { + return false +} + +func (self *SNetwork) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SNetwork) GetProjectId() string { + return "" +} + +func (self *SNetwork) GetIWire() cloudprovider.ICloudWire { + return self.wire +} + +func (self *SNetwork) GetIpStart() string { + pref, _ := netutils.NewIPV4Prefix(self.CIDR) + startIp := pref.Address.NetAddr(pref.MaskLen) // 0 + startIp = startIp.StepUp() // 1 + startIp = startIp.StepUp() // 2 + return startIp.String() +} + +func (self *SNetwork) GetIpEnd() string { + pref, _ := netutils.NewIPV4Prefix(self.CIDR) + endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255 + endIp = endIp.StepDown() // 254 + endIp = endIp.StepDown() // 253 + endIp = endIp.StepDown() // 252 + return endIp.String() +} + +func (self *SNetwork) GetIpMask() int8 { + pref, _ := netutils.NewIPV4Prefix(self.CIDR) + return pref.MaskLen +} + +func (self *SNetwork) GetGateway() string { + pref, _ := netutils.NewIPV4Prefix(self.CIDR) + endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255 + endIp = endIp.StepDown() // 254 + return endIp.String() +} + +func (self *SNetwork) GetServerType() string { + return api.NETWORK_TYPE_GUEST +} + +func (self *SNetwork) GetIsPublic() bool { + return true +} + +func (self *SNetwork) GetPublicScope() rbacutils.TRbacScope { + return rbacutils.ScopeDomain +} + +func (self *SNetwork) Delete() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SNetwork) GetAllocTimeoutSeconds() int { + return 120 +} + +func (self *SRegion) GetNetwroks(vpcId string) ([]SNetwork, error) { + querys := map[string]string{ + "regionId": self.GetId(), + } + if len(vpcId) > 0 { + querys["vpcId"] = vpcId + } + + networks := make([]SNetwork, 0) + resp, err := self.client.DoGet("/apiproxy/v3/getSubnets", querys) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetNetwroks.DoGet") + } + + err = resp.Unmarshal(&networks, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetNetwroks.Unmarshal") + } + + for i := range networks { + vpc, err := self.GetVpc(networks[i].VpcID) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetNetwork.GetVpc") + } + networks[i].vpc = vpc + + networks[i].wire = &SWire{ + region: self, + vpc: vpc, + } + + networks[i].wire.addNetwork(&networks[i]) + } + + return networks, err +} + +func (self *SRegion) GetNetwork(subnetId string) (*SNetwork, error) { + querys := map[string]string{ + "subnetId": subnetId, + "regionId": self.GetId(), + } + + resp, err := self.client.DoGet("/apiproxy/v3/querySubnetDetail", querys) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetNetwork.DoGet") + } + + network := &SNetwork{} + err = resp.Unmarshal(network, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetNetwork.Unmarshal") + } + + vpc, err := self.GetVpc(network.VpcID) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetNetwork.GetVpc") + } + network.vpc = vpc + + network.wire = &SWire{ + region: self, + vpc: vpc, + } + + network.wire.addNetwork(network) + return network, err +} + +func (self *SRegion) CreateNetwork(vpcId, zoneId, name, cidr, gatewayIp, dhcpEnable string) (*SNetwork, error) { + networkParams := jsonutils.NewDict() + networkParams.Set("regionId", jsonutils.NewString(self.GetId())) + networkParams.Set("zoneId", jsonutils.NewString(zoneId)) + networkParams.Set("name", jsonutils.NewString(name)) + networkParams.Set("cidr", jsonutils.NewString(cidr)) + networkParams.Set("gatewayIp", jsonutils.NewString(gatewayIp)) + networkParams.Set("dhcpEnable", jsonutils.NewString(dhcpEnable)) + networkParams.Set("vpcId", jsonutils.NewString(vpcId)) + // DNS地址,如果主机需要访问公网就需要填写该值,不填写就不能使用DNS解析 + // networkParams.Set("primaryDns", jsonutils.NewString(primaryDns)) + // networkParams.Set("secondaryDns", jsonutils.NewString(secondaryDns)) + + params := map[string]jsonutils.JSONObject{ + "jsonStr": networkParams, + } + + network := &SNetwork{} + resp, err := self.client.DoPost("/apiproxy/v3/createSubnet", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.CreateNetwork.DoPost") + } + + err = resp.Unmarshal(network, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.CreateNetwork.Unmarshal") + } + + return network, err +} diff --git a/pkg/multicloud/ctyun/order.go b/pkg/multicloud/ctyun/order.go new file mode 100644 index 0000000000..90d5ae95d5 --- /dev/null +++ b/pkg/multicloud/ctyun/order.go @@ -0,0 +1,93 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import "yunion.io/x/pkg/errors" + +type SOrder struct { + OrderItemID string `json:"orderItemId"` + InstanceID string `json:"instanceId"` + AccountID string `json:"accountId"` + UserID string `json:"userId"` + InnerOrderID string `json:"innerOrderId"` + InnerOrderItemID string `json:"innerOrderItemId"` + ProductID string `json:"productId"` + MasterOrderID string `json:"masterOrderId"` + OrderID string `json:"orderId"` + MasterResourceID string `json:"masterResourceId"` + ResourceID string `json:"resourceId"` + ServiceTag string `json:"serviceTag"` + ResourceType string `json:"resourceType"` + ResourceInfo string `json:"resourceInfo"` + StartDate int64 `json:"startDate"` + ExpireDate int64 `json:"expireDate"` + CreateDate int64 `json:"createDate"` + UpdateDate int64 `json:"updateDate"` + Status int64 `json:"status"` + WorkOrderID string `json:"workOrderId"` + WorkOrderItemID string `json:"workOrderItemId"` + SalesEntryID string `json:"salesEntryId"` + OrderStatus int64 `json:"orderStatus"` + ToOndemand string `json:"toOndemand"` + ItemValue string `json:"itemValue"` + ChargingStatus int64 `json:"chargingStatus"` + ChargingDate int64 `json:"chargingDate"` + ResourceConfig string `json:"resourceConfig"` + AutoToOnDemand bool `json:"autoToOnDemand"` + BuildingChannel int64 `json:"buildingChannel"` + IsPlatformSpecific bool `json:"isPlatformSpecific"` + BillingOwner int64 `json:"billingOwner"` + IsPackage bool `json:"isPackage"` + CanRelease bool `json:"canRelease"` + IsChargeOff bool `json:"isChargeOff"` + IsPublicTest int64 `json:"isPublicTest"` + Master bool `json:"master"` + ResourceConfigMap ResourceConfigMap `json:"resourceConfigMap"` +} + +type ResourceConfigMap struct { + AvailabilityZone string `json:"availability_zone"` + Value string `json:"value"` + Number string `json:"number"` + IsSystemVolume bool `json:"isSystemVolume"` + VolumeType string `json:"volumeType"` + Size int64 `json:"size"` + ZoneID string `json:"zoneId"` + RegionID string `json:"regionId"` + Version string `json:"version"` + CycleCnt int64 `json:"cycleCnt"` + CycleType int64 `json:"cycleType"` + ResEbsID string `json:"resEbsId"` + ActualResourceID string `json:"actualResourceId"` +} + +func (self *SRegion) GetOrder(orderId string) ([]SOrder, error) { + params := map[string]string{ + "masterOrderId": orderId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/order/queryResourceInfoByMasterOrderId", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetOrder.DoGet") + } + + ret := make([]SOrder, 0) + err = resp.Unmarshal(&ret, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetOrder.DoGet") + } + + return ret, nil +} diff --git a/pkg/multicloud/ctyun/project.go b/pkg/multicloud/ctyun/project.go new file mode 100644 index 0000000000..2b89be2cd9 --- /dev/null +++ b/pkg/multicloud/ctyun/project.go @@ -0,0 +1,66 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "strings" + + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" +) + +// GET http://ctyun-api-url/apiproxy/v3/ondemand/queryProjectIds +type SProject struct { + Enabled bool `json:"enabled"` + Name string `json:"name"` + Description string `json:"description"` + ID string `json:"id"` +} + +func (self *SProject) GetRegionID() string { + return strings.Split(self.Name, "_")[1] +} + +func (self *SProject) GetHealthStatus() string { + if self.Enabled { + return api.CLOUD_PROVIDER_HEALTH_NORMAL + } + + return api.CLOUD_PROVIDER_HEALTH_SUSPENDED +} + +func (self *SCtyunClient) FetchProjects() ([]SProject, error) { + client, err := NewSCtyunClient("", "", "", self.accessKey, self.secret, self.debug) + if err != nil { + return nil, errors.Wrap(err, "CtyunClient.FetchProjects") + } + projects := make([]SProject, 0) + resp, err := client.DoGet("/apiproxy/v3/ondemand/queryProjectIds", map[string]string{}) + if err != nil { + return nil, errors.Wrap(err, "CtyunClient.FetchProjects.DoGet") + } + + err = resp.Unmarshal(&projects, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "CtyunClient.FetchProjects.Unmarshal") + } + + return projects, err +} + +func (self *SRegion) FetchProjects() ([]SProject, error) { + return self.client.FetchProjects() +} diff --git a/pkg/multicloud/ctyun/provider/doc.go b/pkg/multicloud/ctyun/provider/doc.go new file mode 100644 index 0000000000..d5210835d8 --- /dev/null +++ b/pkg/multicloud/ctyun/provider/doc.go @@ -0,0 +1 @@ +package provider // import "yunion.io/x/onecloud/pkg/multicloud/ctyun/provider" diff --git a/pkg/multicloud/ctyun/provider/provider.go b/pkg/multicloud/ctyun/provider/provider.go new file mode 100644 index 0000000000..c0a54fb2b3 --- /dev/null +++ b/pkg/multicloud/ctyun/provider/provider.go @@ -0,0 +1,165 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package provider + +import ( + "context" + "strings" + + "yunion.io/x/jsonutils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/multicloud/ctyun" +) + +type SCtyunProviderFactory struct { + cloudprovider.SPublicCloudBaseProviderFactor +} + +func (self *SCtyunProviderFactory) GetId() string { + return ctyun.CLOUD_PROVIDER_CTYUN +} + +func (self *SCtyunProviderFactory) GetName() string { + return ctyun.CLOUD_PROVIDER_CTYUN_CN +} + +func (self *SCtyunProviderFactory) IsSupportPrepaidResources() bool { + return true +} + +func (self *SCtyunProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, input *api.CloudaccountCreateInput) error { + if len(input.AccessKeyId) == 0 { + return httperrors.NewMissingParameterError("access_key_id") + } + if len(input.AccessKeySecret) == 0 { + return httperrors.NewMissingParameterError("access_key_secret") + } + if len(input.Environment) == 0 { + return httperrors.NewMissingParameterError("environment") + } + input.Account = input.AccessKeyId + input.Secret = input.AccessKeySecret + input.AccessUrl = input.Environment + return nil +} + +func (self *SCtyunProviderFactory) ValidateUpdateCloudaccountCredential(ctx context.Context, userCred mcclient.TokenCredential, input *api.CloudaccountCredentialInput, cloudaccount string) (*cloudprovider.SCloudaccount, error) { + if len(input.AccessKeyId) == 0 { + return nil, httperrors.NewMissingParameterError("access_key_id") + } + if len(input.AccessKeySecret) == 0 { + return nil, httperrors.NewMissingParameterError("access_key_secret") + } + account := &cloudprovider.SCloudaccount{ + Account: input.AccessKeyId, + Secret: input.AccessKeySecret, + } + return account, nil +} + +func (self *SCtyunProviderFactory) GetProvider(providerId, providerName, url, account, secret string) (cloudprovider.ICloudProvider, error) { + segs := strings.Split(account, "/") + projectId := "" + if len(segs) == 2 { + projectId = segs[1] + account = segs[0] + } + + client, err := ctyun.NewSCtyunClient(providerId, providerName, projectId, account, secret, false) + if err != nil { + return nil, err + } + return &SCtyunProvider{ + SBaseProvider: cloudprovider.NewBaseProvider(self), + client: client, + }, nil +} + +func (self *SCtyunProviderFactory) GetClientRC(url, account, secret string) (map[string]string, error) { + return map[string]string{ + "CTYUN_ACCESS_URL": url, + "CTYUN_ACCESS_KEY": account, + "CTYUN_SECRET": secret, + "CTYUN_REGION": ctyun.CTYUN_DEFAULT_REGION, + }, nil +} + +func init() { + factory := SCtyunProviderFactory{} + cloudprovider.RegisterFactory(&factory) +} + +type SCtyunProvider struct { + cloudprovider.SBaseProvider + client *ctyun.SCtyunClient +} + +func (self *SCtyunProvider) GetIGlobalnetworks() ([]cloudprovider.ICloudGlobalnetwork, error) { + return []cloudprovider.ICloudGlobalnetwork{}, nil +} + +func (self *SCtyunProvider) GetIGlobalnetworkById(id string) (cloudprovider.ICloudGlobalnetwork, error) { + return nil, cloudprovider.ErrNotFound +} + +func (self *SCtyunProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { + return self.client.GetSubAccounts() +} + +func (self *SCtyunProvider) GetAccountId() string { + return self.client.GetAccountId() +} + +func (self *SCtyunProvider) GetIRegions() []cloudprovider.ICloudRegion { + return self.client.GetIRegions() +} + +func (self *SCtyunProvider) GetSysInfo() (jsonutils.JSONObject, error) { + regions := self.client.GetIRegions() + info := jsonutils.NewDict() + info.Add(jsonutils.NewInt(int64(len(regions))), "region_count") + info.Add(jsonutils.NewString(ctyun.CTYUN_API_VERSION), "api_version") + return info, nil +} + +func (self *SCtyunProvider) GetVersion() string { + return ctyun.CTYUN_API_VERSION +} + +func (self *SCtyunProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { + return self.client.GetIRegionById(id) +} + +func (self *SCtyunProvider) GetBalance() (float64, string, error) { + return 0.0, api.CLOUD_PROVIDER_HEALTH_NORMAL, cloudprovider.ErrNotSupported +} + +func (self *SCtyunProvider) GetIProjects() ([]cloudprovider.ICloudProject, error) { + return self.client.GetIProjects() +} + +func (self *SCtyunProvider) GetStorageClasses(regionId string) []string { + return []string{ + "STANDARD", "WARM", "COLD", + } +} + +func (self *SCtyunProvider) GetCloudRegionExternalIdPrefix() string { + return self.client.GetAccessEnv() + "/" +} diff --git a/pkg/multicloud/ctyun/region.go b/pkg/multicloud/ctyun/region.go new file mode 100644 index 0000000000..3dfb6f473e --- /dev/null +++ b/pkg/multicloud/ctyun/region.go @@ -0,0 +1,451 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/secrules" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SRegion struct { + cloudprovider.SFakeOnPremiseRegion + multicloud.SRegion + multicloud.SNoObjectStorageRegion + + client *SCtyunClient + storageCache *SStoragecache + + Description string `json:"description"` + ID string `json:"id"` + ParentRegionID string `json:"parent_region_id"` + Type string `json:"type"` + + izones []cloudprovider.ICloudZone + ivpcs []cloudprovider.ICloudVpc +} + +func (self *SRegion) fetchIVpcs() error { + vpcs, err := self.GetVpcs() + if err != nil { + return errors.Wrap(err, "SRegion.fetchIVpcs") + } + + self.ivpcs = make([]cloudprovider.ICloudVpc, 0) + for i := range vpcs { + vpc := vpcs[i] + vpc.region = self + self.ivpcs = append(self.ivpcs, &vpc) + } + + return nil +} + +func (self *SRegion) fetchInfrastructure() error { + if err := self.fetchIVpcs(); err != nil { + return err + } + + for i := 0; i < len(self.ivpcs); i += 1 { + vpc := self.ivpcs[i].(*SVpc) + wire := SWire{region: self, vpc: vpc} + vpc.addWire(&wire) + + for j := 0; j < len(self.izones); j += 1 { + zone := self.izones[j].(*SZone) + zone.addWire(&wire) + } + } + return nil +} + +func (self *SRegion) GetVpcs() ([]SVpc, error) { + vpcs := make([]SVpc, 0) + params := map[string]string{ + "regionId": self.GetId(), + } + resp, err := self.client.DoGet("/apiproxy/v3/getVpcs", params) + if err != nil { + return nil, err + } + + err = resp.Unmarshal(&vpcs, "returnObj") + if err != nil { + return nil, err + } + + return vpcs, nil +} + +func (self *SRegion) CreateVpc(name, cidr string) (*SVpc, error) { + params := map[string]jsonutils.JSONObject{ + "regionId": jsonutils.NewString(self.GetId()), + "name": jsonutils.NewString(name), + "cidr": jsonutils.NewString(cidr), + } + resp, err := self.client.DoPost("/apiproxy/v3/createVPC", params) + if err != nil { + return nil, err + } + + vpc := &SVpc{} + err = resp.Unmarshal(vpc, "returnObj") + if err != nil { + return nil, err + } + + vpc.region = self + return vpc, nil +} + +func (self *SRegion) GetClient() *SCtyunClient { + return self.client +} + +func (self *SRegion) GetISecurityGroupById(secgroupId string) (cloudprovider.ICloudSecurityGroup, error) { + return self.GetSecurityGroupDetails(secgroupId) +} + +func (self *SRegion) GetISecurityGroupByName(vpcId string, name string) (cloudprovider.ICloudSecurityGroup, error) { + segroups, err := self.GetSecurityGroups(vpcId) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetISecurityGroupByName.GetSecurityGroups") + } + + for i := range segroups { + if segroups[i].Name == name { + return &segroups[i], nil + } + } + + return nil, errors.Wrap(cloudprovider.ErrNotFound, "SRegion.GetISecurityGroupByName.GetSecurityGroups") +} + +func (self *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SRegion) GetId() string { + return self.ID +} + +func (self *SRegion) GetName() string { + return self.ID +} + +func (self *SRegion) GetGlobalId() string { + return fmt.Sprintf("%s/%s", self.client.GetAccessEnv(), self.ID) +} + +func (self *SRegion) GetStatus() string { + return api.CLOUD_REGION_STATUS_INSERVER +} + +func (self *SRegion) Refresh() error { + return nil +} + +func (self *SRegion) IsEmulated() bool { + return false +} + +func (self *SRegion) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo { + if info, ok := LatitudeAndLongitude[self.ID]; ok { + return info + } + return cloudprovider.SGeographicInfo{} +} + +// http://ctyun-api-url/apiproxy/v3/order/getZoneConfig +func (self *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) { + if self.izones == nil { + var err error + err = self.fetchInfrastructure() + if err != nil { + return nil, err + } + } + return self.izones, nil +} + +// http://ctyun-api-url/apiproxy/v3/getVpcs +// http://ctyun-api-url/apiproxy/v3/getVpcs +func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) { + if self.ivpcs == nil { + err := self.fetchInfrastructure() + if err != nil { + return nil, err + } + } + return self.ivpcs, nil +} + +// http://ctyun-api-url/apiproxy/v3/ondemand/queryIps +func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { + eips, err := self.GetEips() + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetIEips.GetEips") + } + + ieips := make([]cloudprovider.ICloudEIP, len(eips)) + for i := range eips { + ieips[i] = &eips[i] + } + + return ieips, nil +} + +func (self *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { + return self.GetVpc(id) +} + +func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) { + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + if izones[i].GetGlobalId() == id { + return izones[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SRegion) GetIEipById(id string) (cloudprovider.ICloudEIP, error) { + return self.GetEip(id) +} + +func (self *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) { + return self.GetVMById(id) +} + +func (self *SRegion) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) { + return self.GetDisk(id) +} + +func (self *SRegion) DeleteSecurityGroup(vpcId, secgroupId string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SRegion) SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (self *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudprovider.ICloudVpc, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + snapshots, err := self.GetSnapshots("") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetISnapshots.GetSnapshots") + } + + isnapshots := make([]cloudprovider.ICloudSnapshot, len(snapshots)) + for i := range snapshots { + isnapshots[i] = &snapshots[i] + } + + return isnapshots, nil +} + +func (self *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) { + snapshots, err := self.GetSnapshots("") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetISnapshotById.GetSnapshots") + } + + for i := range snapshots { + if snapshots[i].GetId() == snapshotId { + return &snapshots[i], nil + } + } + + return nil, errors.Wrap(cloudprovider.ErrNotFound, "SRegion.GetISnapshotById") +} + +func (self *SRegion) CreateSnapshotPolicy(*cloudprovider.SnapshotPolicyInput) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (self *SRegion) UpdateSnapshotPolicy(*cloudprovider.SnapshotPolicyInput, string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SRegion) DeleteSnapshotPolicy(string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SRegion) ApplySnapshotPolicyToDisks(snapshotPolicyId string, diskId string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SRegion) CancelSnapshotPolicyToDisks(snapshotPolicyId string, diskId string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SRegion) GetISnapshotPolicies() ([]cloudprovider.ICloudSnapshotPolicy, error) { + polices, err := self.GetDiskBackupPolices() + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetISnapshotPolicies.GetDiskBackupPolices") + } + + ipolices := make([]cloudprovider.ICloudSnapshotPolicy, len(polices)) + for i := range polices { + ipolices[i] = &polices[i] + } + + return ipolices, nil +} + +func (self *SRegion) GetISnapshotPolicyById(snapshotPolicyId string) (cloudprovider.ICloudSnapshotPolicy, error) { + return self.GetDiskBackupPolicy(snapshotPolicyId) +} + +func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { + iHosts := make([]cloudprovider.ICloudHost, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneHost, err := izones[i].GetIHosts() + if err != nil { + return nil, err + } + iHosts = append(iHosts, iZoneHost...) + } + return iHosts, nil +} + +func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) { + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + ihost, err := izones[i].GetIHostById(id) + if err == nil { + return ihost, nil + } else if err != cloudprovider.ErrNotFound { + return nil, err + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + iStores := make([]cloudprovider.ICloudStorage, 0) + + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + iZoneStores, err := izones[i].GetIStorages() + if err != nil { + return nil, err + } + iStores = append(iStores, iZoneStores...) + } + return iStores, nil +} + +func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + izones, err := self.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(izones); i += 1 { + istore, err := izones[i].GetIStorageById(id) + if err == nil { + return istore, nil + } else if err != cloudprovider.ErrNotFound { + return nil, err + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) { + storageCache := self.getStoragecache() + return []cloudprovider.ICloudStoragecache{storageCache}, nil +} + +func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { + storageCache := self.getStoragecache() + if storageCache.GetGlobalId() == id { + return storageCache, nil + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SRegion) GetProvider() string { + return api.CLOUD_PROVIDER_CTYUN +} + +func (self *SRegion) GetInstances(instanceId string) ([]SInstance, error) { + params := map[string]string{ + "regionId": self.GetId(), + } + + resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryVMs", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetInstances.DoGet") + } + + ret := make([]SInstance, 0) + err = resp.Unmarshal(&ret, "returnObj", "servers") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetInstances.Unmarshal") + } + + return ret, nil +} + +func (self *SRegion) GetInstanceFlavors() ([]FlavorObj, error) { + params := map[string]string{ + "regionId": self.GetId(), + } + + resp, err := self.client.DoGet("/apiproxy/v3/order/getFlavors", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetInstanceFlavors.DoGet") + } + + ret := make([]FlavorObj, 0) + err = resp.Unmarshal(&ret, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetInstanceFlavors.Unmarshal") + } + + return ret, nil +} diff --git a/pkg/multicloud/ctyun/secrules.go b/pkg/multicloud/ctyun/secrules.go new file mode 100644 index 0000000000..4c9ff86617 --- /dev/null +++ b/pkg/multicloud/ctyun/secrules.go @@ -0,0 +1,103 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" +) + +type SSecurityGroupRule struct { + secgroup *SSecurityGroup + + PortRangeMax int64 `json:"port_range_max"` + SecurityGroupID string `json:"security_group_id"` + RemoteGroupId string `json:"remote_group_id"` + Description string `json:"description"` + RemoteIPPrefix string `json:"remote_ip_prefix"` + Protocol string `json:"protocol"` + Ethertype string `json:"ethertype"` + UpdatedAt string `json:"updated_at"` + Direction string `json:"direction"` + TenantID string `json:"tenant_id"` + ID string `json:"id"` + ProjectID string `json:"project_id"` + PortRangeMin int64 `json:"port_range_min"` + CreatedAt string `json:"created_at"` +} + +func (self *SRegion) GetSecurityGroupRules(secgroupId string) ([]SSecurityGroupRule, error) { + params := map[string]string{ + "regionId": self.GetId(), + "securityGroupId": secgroupId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/getSecurityGroupRules", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSecurityGroupRules.DoGet") + } + + ret := make([]SSecurityGroupRule, 0) + err = resp.Unmarshal(&ret, "returnObj", "security_group_rules") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSecurityGroupRules.Unmarshal") + } + + secgroup, err := self.GetSecurityGroupDetails(secgroupId) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSecurityGroupRules.GetSecurityGroupDetails") + } + + for i := range ret { + ret[i].secgroup = secgroup + } + + return ret, nil +} + +func (self *SRegion) CreateSecurityGroupRule(groupId, direction, ethertype, protocol, remoteIpPrefix string, portRangeMin, portRangeMax int64) error { + ruleParams := jsonutils.NewDict() + ruleParams.Set("regionId", jsonutils.NewString(self.GetId())) + ruleParams.Set("securityGroupId", jsonutils.NewString(groupId)) + ruleParams.Set("direction", jsonutils.NewString(direction)) + ruleParams.Set("ethertype", jsonutils.NewString(ethertype)) + + if len(protocol) > 0 { + ruleParams.Set("protocol", jsonutils.NewString(protocol)) + } + + if len(remoteIpPrefix) > 0 { + ruleParams.Set("remoteIpPrefix", jsonutils.NewString(remoteIpPrefix)) + } + + if portRangeMin > 0 { + ruleParams.Set("portRangeMin", jsonutils.NewInt(portRangeMin)) + } + + if portRangeMax > 0 { + ruleParams.Set("portRangeMax", jsonutils.NewInt(portRangeMax)) + } + + params := map[string]jsonutils.JSONObject{ + "jsonStr": ruleParams, + } + + _, err := self.client.DoPost("/apiproxy/v3/createSecurityGroupRule", params) + if err != nil { + return errors.Wrap(err, "SRegion.CreateSecurityGroupRule.DoPost") + } + + return err +} diff --git a/pkg/multicloud/ctyun/securitygroup.go b/pkg/multicloud/ctyun/securitygroup.go new file mode 100644 index 0000000000..854004f76e --- /dev/null +++ b/pkg/multicloud/ctyun/securitygroup.go @@ -0,0 +1,266 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "net" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/secrules" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SSecurityGroup struct { + region *SRegion + vpc *SVpc + + ID string `json:"id"` + ResSecurityGroupID string `json:"resSecurityGroupId"` + Name string `json:"name"` + AccountID string `json:"accountId"` + UserID string `json:"userId"` + RegionID string `json:"regionId"` + ZoneID string `json:"zoneId"` + VpcID string `json:"vpcId"` + CreateDate int64 `json:"createDate"` + Status int64 `json:"status"` +} + +func (self *SSecurityGroup) SyncRules(rules []secrules.SecurityRule) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SSecurityGroup) Delete() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SSecurityGroup) GetId() string { + return self.ResSecurityGroupID +} + +func (self *SSecurityGroup) GetName() string { + if len(self.Name) > 0 { + return self.Name + } + return self.ResSecurityGroupID +} + +func (self *SSecurityGroup) GetGlobalId() string { + return self.GetId() +} + +func (self *SSecurityGroup) GetStatus() string { + return "" +} + +func (self *SSecurityGroup) Refresh() error { + if new, err := self.region.GetSecurityGroupDetails(self.GetId()); err != nil { + return err + } else { + return jsonutils.Update(self, new) + } +} + +func (self *SSecurityGroup) IsEmulated() bool { + return false +} + +func (self *SSecurityGroup) GetMetadata() *jsonutils.JSONDict { + return jsonutils.NewDict() +} + +func (self *SSecurityGroup) GetDescription() string { + return "" +} + +// 判断是否兼容云端安全组规则 +func compatibleSecurityGroupRule(r SSecurityGroupRule) bool { + // 忽略了源地址是安全组的规则 + if len(r.RemoteGroupId) > 0 { + return false + } + + // 忽略IPV6 + if r.Ethertype == "IPv6" { + return false + } + + return true +} + +func (self *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) { + _rules, err := self.region.GetSecurityGroupRules(self.GetId()) + if err != nil { + return nil, errors.Wrap(err, "SSecurityGroup.GetRules.GetSecurityGroupRules") + } + + rules := make([]secrules.SecurityRule, 0) + for _, r := range _rules { + if !compatibleSecurityGroupRule(r) { + continue + } + + rule, err := self.GetSecurityRule(r, false) + if err != nil { + return rules, err + } + + rules = append(rules, rule) + } + + return rules, nil +} + +func (self *SSecurityGroup) GetSecurityRule(remoteRule SSecurityGroupRule, withRuleId bool) (secrules.SecurityRule, error) { + var err error + var direction secrules.TSecurityRuleDirection + if remoteRule.Direction == "ingress" { + direction = secrules.SecurityRuleIngress + } else { + direction = secrules.SecurityRuleEgress + } + + protocol := secrules.PROTO_ANY + if remoteRule.Protocol != "" { + protocol = remoteRule.Protocol + } + + var portStart int + var portEnd int + if protocol == secrules.PROTO_ICMP { + portStart = -1 + portEnd = -1 + } else { + portStart = int(remoteRule.PortRangeMin) + portEnd = int(remoteRule.PortRangeMax) + } + + ipNet := &net.IPNet{} + if len(remoteRule.RemoteIPPrefix) > 0 { + _, ipNet, err = net.ParseCIDR(remoteRule.RemoteIPPrefix) + } else { + _, ipNet, err = net.ParseCIDR("0.0.0.0/0") + } + + if err != nil { + return secrules.SecurityRule{}, err + } + + // withRuleId.将ruleId附加到description字段。该hook有特殊目的,仅在同步安全组时使用。 + desc := "" + if withRuleId { + desc = remoteRule.ID + } else { + desc = remoteRule.Description + } + + rule := secrules.SecurityRule{ + Priority: 1, + Action: secrules.SecurityRuleAllow, + IPNet: ipNet, + Protocol: protocol, + Direction: direction, + PortStart: portStart, + PortEnd: portEnd, + Ports: nil, + Description: desc, + } + + err = rule.ValidateRule() + return rule, err +} + +func (self *SSecurityGroup) GetVpcId() string { + if len(self.VpcID) == 0 { + return "normal" + } + + return self.VpcID +} + +func (self *SRegion) GetSecurityGroupDetails(groupId string) (*SSecurityGroup, error) { + params := map[string]string{ + "regionId": self.GetId(), + "securityGroupId": groupId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/querySecurityGroupDetail", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSecurityGroupDetails") + } + + ret := &SSecurityGroup{} + err = resp.Unmarshal(&ret, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSecurityGroupDetails.Unmarshal") + } + + ret.region = self + return ret, nil +} + +func (self *SRegion) GetSecurityGroups(vpcId string) ([]SSecurityGroup, error) { + params := map[string]string{ + "regionId": self.GetId(), + "vpcId": vpcId, + } + + resp, err := self.client.DoGet("/apiproxy/v3/getSecurityGroups", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSecurityGroups") + } + + ret := make([]SSecurityGroup, 0) + err = resp.Unmarshal(&ret, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSecurityGroups.Unmarshal") + } + + for i := range ret { + ret[i].region = self + } + + return ret, nil +} + +func (self *SRegion) CreateSecurityGroup(vpcId, name string) (*SSecurityGroup, error) { + params := map[string]jsonutils.JSONObject{ + "regionId": jsonutils.NewString(self.GetId()), + "vpcId": jsonutils.NewString(vpcId), + "name": jsonutils.NewString(name), + } + + resp, err := self.client.DoPost("/apiproxy/v3/createSecurityGroup", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.CreateSecurityGroup.DoPost") + } + + ret := &SSecurityGroup{} + err = resp.Unmarshal(ret, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.CreateSecurityGroup.Unmarshal") + } + + vpc, err := self.GetVpc(vpcId) + if err != nil { + return nil, errors.Wrap(err, "SRegion.CreateSecurityGroup.GetVpc") + } + + ret.vpc = vpc + ret.region = self + return ret, nil +} diff --git a/pkg/multicloud/ctyun/shell/disk.go b/pkg/multicloud/ctyun/shell/disk.go new file mode 100644 index 0000000000..91b7e52b84 --- /dev/null +++ b/pkg/multicloud/ctyun/shell/disk.go @@ -0,0 +1,48 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VDiskListOptions struct { + } + shellutils.R(&VDiskListOptions{}, "disk-list", "List disks", func(cli *ctyun.SRegion, args *VDiskListOptions) error { + disks, e := cli.GetDisks() + if e != nil { + return e + } + printList(disks, 0, 0, 0, nil) + return nil + }) + + type DiskCreateOptions struct { + ZoneId string `help:"zone id"` + Name string `help:"disk name"` + DiskType string `help:"disk type" choice:"SSD|SAS|SATA"` + Size string `help:"disk size"` + } + shellutils.R(&DiskCreateOptions{}, "disk-create", "Create disk", func(cli *ctyun.SRegion, args *DiskCreateOptions) error { + disk, e := cli.CreateDisk(args.ZoneId, args.Name, args.DiskType, args.Size) + if e != nil { + return e + } + printObject(disk) + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/diskbackuppolicy.go b/pkg/multicloud/ctyun/shell/diskbackuppolicy.go new file mode 100644 index 0000000000..79f3adff23 --- /dev/null +++ b/pkg/multicloud/ctyun/shell/diskbackuppolicy.go @@ -0,0 +1,77 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VPolicyListOptions struct { + } + shellutils.R(&VPolicyListOptions{}, "policy-list", "List polices", func(cli *ctyun.SRegion, args *VPolicyListOptions) error { + polices, e := cli.GetDiskBackupPolices() + if e != nil { + return e + } + printList(polices, 0, 0, 0, nil) + return nil + }) + + type PolicyCreateOptions struct { + Name string `help:"policy name"` + StartTime string `help:"startTime"` + Frequency string `help:"frequency"` + RententionNum string `help:"rententionNum"` + FirstBackup string `help:"firstBackup"` + Status string `help:"status"` + } + shellutils.R(&PolicyCreateOptions{}, "policy-create", "Create policy", func(cli *ctyun.SRegion, args *PolicyCreateOptions) error { + e := cli.CreateDiskBackupPolicy(args.Name, args.StartTime, args.Frequency, args.RententionNum, args.FirstBackup, args.Status) + if e != nil { + return e + } + + return nil + }) + + type BindingDiskBackupPolicyOptions struct { + PolicyId string `help:"policy id"` + ResourceId string `help:"resource id"` + ResourceType string `help:"resource type"` + } + shellutils.R(&BindingDiskBackupPolicyOptions{}, "policy-bind", "Binding policy", func(cli *ctyun.SRegion, args *BindingDiskBackupPolicyOptions) error { + e := cli.BindingDiskBackupPolicy(args.PolicyId, args.ResourceId, args.ResourceType) + if e != nil { + return e + } + + return nil + }) + + type UnBindDiskBackupPolicyOptions struct { + PolicyId string `help:"policy id"` + ResourceId string `help:"resourceId"` + } + shellutils.R(&UnBindDiskBackupPolicyOptions{}, "policy-unbind", "Unbind policy", func(cli *ctyun.SRegion, args *UnBindDiskBackupPolicyOptions) error { + e := cli.UnBindDiskBackupPolicy(args.PolicyId, args.ResourceId) + if e != nil { + return e + } + + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/doc.go b/pkg/multicloud/ctyun/shell/doc.go new file mode 100644 index 0000000000..ab43d345cb --- /dev/null +++ b/pkg/multicloud/ctyun/shell/doc.go @@ -0,0 +1 @@ +package shell // import "yunion.io/x/onecloud/pkg/multicloud/ctyun/shell" diff --git a/pkg/multicloud/ctyun/shell/eip.go b/pkg/multicloud/ctyun/shell/eip.go new file mode 100644 index 0000000000..da39b3c21b --- /dev/null +++ b/pkg/multicloud/ctyun/shell/eip.go @@ -0,0 +1,48 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VEipListOptions struct { + } + shellutils.R(&VEipListOptions{}, "eip-list", "List eips", func(cli *ctyun.SRegion, args *VEipListOptions) error { + eips, e := cli.GetEips() + if e != nil { + return e + } + printList(eips, 0, 0, 0, nil) + return nil + }) + + type EipCreateOptions struct { + ZoneId string `help:"zone id"` + Name string `help:"eip name"` + Size string `help:"size"` + ShareType string `help:"share type" choice:"PER|WHOLE"` + } + shellutils.R(&EipCreateOptions{}, "eip-create", "Create eip", func(cli *ctyun.SRegion, args *EipCreateOptions) error { + eip, e := cli.CreateEip(args.ZoneId, args.Name, args.Size, args.ShareType) + if e != nil { + return e + } + printObject(eip) + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/image.go b/pkg/multicloud/ctyun/shell/image.go new file mode 100644 index 0000000000..0416207eec --- /dev/null +++ b/pkg/multicloud/ctyun/shell/image.go @@ -0,0 +1,34 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VImageListOptions struct { + ImageType string `help:"image type" choices:"gold|private|shared"` + } + shellutils.R(&VImageListOptions{}, "image-list", "List images", func(cli *ctyun.SRegion, args *VImageListOptions) error { + images, e := cli.GetImages(args.ImageType) + if e != nil { + return e + } + printList(images, 0, 0, 0, nil) + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/instance.go b/pkg/multicloud/ctyun/shell/instance.go new file mode 100644 index 0000000000..4296b6fa4b --- /dev/null +++ b/pkg/multicloud/ctyun/shell/instance.go @@ -0,0 +1,55 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type InstanceListOptions struct { + Id string `help:"ID of instance to show"` + } + shellutils.R(&InstanceListOptions{}, "instance-list", "List intances", func(cli *ctyun.SRegion, args *InstanceListOptions) error { + instances, e := cli.GetInstances(args.Id) + if e != nil { + return e + } + printList(instances, 0, 0, 0, []string{}) + return nil + }) + + type InstanceCreateOptions struct { + ZoneId string `help:"zone ID of instance"` + NAME string `help:"name of instance"` + ADMINPASS string `help:"admin password of instance"` + ImageId string `help:"image Id of instance"` + VolumeType string `help:"volume type of instance"` + Flavor string `help:"Flavor of instance"` + VpcId string `help:"Vpc of instance"` + SubnetId string `help:"subnet Id of instance"` + SecGroupId string `help:"security group Id of instance"` + } + + shellutils.R(&InstanceCreateOptions{}, "instance-create", "Create intance", func(cli *ctyun.SRegion, args *InstanceCreateOptions) error { + e := cli.CreateInstance(args.ZoneId, args.NAME, args.ImageId, args.VolumeType, args.Flavor, args.VpcId, args.SubnetId, args.SecGroupId, args.ADMINPASS) + if e != nil { + return e + } + + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/job.go b/pkg/multicloud/ctyun/shell/job.go new file mode 100644 index 0000000000..8ec6933fb5 --- /dev/null +++ b/pkg/multicloud/ctyun/shell/job.go @@ -0,0 +1,34 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VJobShowOptions struct { + JOBID string `help:"Job ID"` + } + shellutils.R(&VJobShowOptions{}, "job-show", "Show job", func(cli *ctyun.SRegion, args *VJobShowOptions) error { + job, e := cli.GetJob(args.JOBID) + if e != nil { + return e + } + printObject(job) + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/network.go b/pkg/multicloud/ctyun/shell/network.go new file mode 100644 index 0000000000..a9f6408e7f --- /dev/null +++ b/pkg/multicloud/ctyun/shell/network.go @@ -0,0 +1,51 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VNetworkListOptions struct { + Vpc string `help:"Vpc ID"` + } + shellutils.R(&VNetworkListOptions{}, "subnet-list", "List subnets", func(cli *ctyun.SRegion, args *VNetworkListOptions) error { + vswitches, e := cli.GetNetwroks(args.Vpc) + if e != nil { + return e + } + printList(vswitches, 0, 0, 0, nil) + return nil + }) + + type NetworkCreateOptions struct { + VpcId string `help:"vpc id"` + ZoneId string `help:"zone id"` + Name string `help:"subnet name"` + Cidr string `help:"cidr"` + GatewayIp string `help:"gateway ip"` + DhcpEnable string `help:"gateway ip" choice:"true|false"` + } + shellutils.R(&NetworkCreateOptions{}, "subnet-create", "Create subnet", func(cli *ctyun.SRegion, args *NetworkCreateOptions) error { + vpc, e := cli.CreateNetwork(args.VpcId, args.ZoneId, args.Name, args.Cidr, args.GatewayIp, args.DhcpEnable) + if e != nil { + return e + } + printObject(vpc) + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/printutils.go b/pkg/multicloud/ctyun/shell/printutils.go new file mode 100644 index 0000000000..1b46ec704a --- /dev/null +++ b/pkg/multicloud/ctyun/shell/printutils.go @@ -0,0 +1,25 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import "yunion.io/x/onecloud/pkg/util/printutils" + +func printList(data interface{}, total, offset, limit int, columns []string) { + printutils.PrintInterfaceList(data, total, offset, limit, columns) +} + +func printObject(obj interface{}) { + printutils.PrintInterfaceObject(obj) +} diff --git a/pkg/multicloud/ctyun/shell/project.go b/pkg/multicloud/ctyun/shell/project.go new file mode 100644 index 0000000000..ad760cf49d --- /dev/null +++ b/pkg/multicloud/ctyun/shell/project.go @@ -0,0 +1,33 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VProjectListOptions struct { + } + shellutils.R(&VProjectListOptions{}, "project-list", "List projects", func(cli *ctyun.SRegion, args *VProjectListOptions) error { + projects, e := cli.FetchProjects() + if e != nil { + return e + } + printList(projects, 0, 0, 0, nil) + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/region.go b/pkg/multicloud/ctyun/shell/region.go new file mode 100644 index 0000000000..8061444c4a --- /dev/null +++ b/pkg/multicloud/ctyun/shell/region.go @@ -0,0 +1,30 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type RegionListOptions struct { + } + shellutils.R(&RegionListOptions{}, "region-list", "List regions", func(cli *ctyun.SRegion, args *RegionListOptions) error { + regions := cli.GetClient().GetRegions() + printList(regions, 0, 0, 0, nil) + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/secgroup.go b/pkg/multicloud/ctyun/shell/secgroup.go new file mode 100644 index 0000000000..3ea8df10d4 --- /dev/null +++ b/pkg/multicloud/ctyun/shell/secgroup.go @@ -0,0 +1,77 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VSecurityGroupListOptions struct { + Vpc string `help:"Vpc ID"` + } + shellutils.R(&VSecurityGroupListOptions{}, "secgroup-list", "List secgroups", func(cli *ctyun.SRegion, args *VSecurityGroupListOptions) error { + secgroups, e := cli.GetSecurityGroups(args.Vpc) + if e != nil { + return e + } + printList(secgroups, 0, 0, 0, nil) + return nil + }) + + type VSecurityGroupRuleListOptions struct { + Group string `help:"Security Group ID"` + } + shellutils.R(&VSecurityGroupRuleListOptions{}, "secrule-list", "List secgroup rules", func(cli *ctyun.SRegion, args *VSecurityGroupRuleListOptions) error { + secrules, e := cli.GetSecurityGroupRules(args.Group) + if e != nil { + return e + } + printList(secrules, 0, 0, 0, nil) + return nil + }) + + type SecurityGroupCreateOptions struct { + VpcId string `help:"vpc id"` + Name string `help:"secgroup name"` + } + shellutils.R(&SecurityGroupCreateOptions{}, "secgroup-create", "Create secgroup", func(cli *ctyun.SRegion, args *SecurityGroupCreateOptions) error { + vpc, e := cli.CreateSecurityGroup(args.VpcId, args.Name) + if e != nil { + return e + } + printObject(vpc) + return nil + }) + + type SecurityGroupRuleCreateOptions struct { + Group string `help:"secgroup id"` + Direction string `help:"direction"` + Ethertype string `help:"ethertype" choice:"IPv4|IPv6"` + Protocol string `help:"protocol,icmp,tcp,udp,and so on "` + IpPrefix string `help:"remote ip prefix"` + PortMin int64 `help:"portRangeMin"` + PortMax int64 `help:"portRangeMax"` + } + shellutils.R(&SecurityGroupRuleCreateOptions{}, "secrule-create", "Create secgroup rule", func(cli *ctyun.SRegion, args *SecurityGroupRuleCreateOptions) error { + e := cli.CreateSecurityGroupRule(args.Group, args.Direction, args.Ethertype, args.Protocol, args.IpPrefix, args.PortMin, args.PortMax) + if e != nil { + return e + } + + return nil + }) +} diff --git a/pkg/multicloud/ctyun/shell/vpc.go b/pkg/multicloud/ctyun/shell/vpc.go new file mode 100644 index 0000000000..fa308bd820 --- /dev/null +++ b/pkg/multicloud/ctyun/shell/vpc.go @@ -0,0 +1,46 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ctyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VpcListOptions struct { + } + shellutils.R(&VpcListOptions{}, "vpc-list", "List vpcs", func(cli *ctyun.SRegion, args *VpcListOptions) error { + vpcs, e := cli.GetVpcs() + if e != nil { + return e + } + printList(vpcs, 0, 0, 0, nil) + return nil + }) + + type VpcCreateOptions struct { + NAME string `help:"vpc name"` + CIDR string `help:"10.0.0.0/8~10.255.255.0/24或者172.16.0.0/12 ~ 172.31.255.0/24或者192.168.0.0/16 ~ 192.168.255.0/24"` + } + shellutils.R(&VpcCreateOptions{}, "vpc-create", "Create vpc", func(cli *ctyun.SRegion, args *VpcCreateOptions) error { + vpc, e := cli.CreateVpc(args.NAME, args.CIDR) + if e != nil { + return e + } + printObject(vpc) + return nil + }) +} diff --git a/pkg/multicloud/ctyun/snapshot.go b/pkg/multicloud/ctyun/snapshot.go new file mode 100644 index 0000000000..72bf31998c --- /dev/null +++ b/pkg/multicloud/ctyun/snapshot.go @@ -0,0 +1,152 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SSnapshot struct { + region *SRegion + + Status string `json:"status"` + Description string `json:"description"` + AvailabilityZone string `json:"availability_zone"` + VolumeID string `json:"volume_id"` + FailReason string `json:"fail_reason"` + ID string `json:"id"` + Size int64 `json:"size"` + Container string `json:"container"` + Name string `json:"name"` + CreatedAt string `json:"created_at"` +} + +func (self *SSnapshot) GetId() string { + return self.ID +} + +func (self *SSnapshot) GetName() string { + return self.Name +} + +func (self *SSnapshot) GetGlobalId() string { + return self.GetId() +} + +func (self *SSnapshot) GetStatus() string { + switch self.Status { + case "available": + return api.SNAPSHOT_READY + case "creating": + return api.SNAPSHOT_CREATING + case "deleting": + return api.SNAPSHOT_DELETING + case "error_deleting", "error": + return api.SNAPSHOT_FAILED + case "rollbacking": + return api.SNAPSHOT_ROLLBACKING + default: + return api.SNAPSHOT_UNKNOWN + } +} + +func (self *SSnapshot) Refresh() error { + snapshot, err := self.region.GetSnapshot(self.VolumeID, self.GetId()) + if err != nil { + return err + } + + if err := jsonutils.Update(self, snapshot); err != nil { + return err + } + + return nil +} + +func (self *SSnapshot) IsEmulated() bool { + return false +} + +func (self *SSnapshot) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SSnapshot) GetProjectId() string { + return "" +} + +func (self *SSnapshot) GetSizeMb() int32 { + return 0 +} + +func (self *SSnapshot) GetDiskId() string { + return self.VolumeID +} + +func (self *SSnapshot) GetDiskType() string { + return api.DISK_TYPE_SYS +} + +func (self *SSnapshot) Delete() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SRegion) GetSnapshot(diskId string, snapshotId string) (*SSnapshot, error) { + snapshots, err := self.GetSnapshots(diskId) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSnapshot.GetSnapshots") + } + + for i := range snapshots { + snapshot := snapshots[i] + if snapshot.ID == snapshotId { + snapshot.region = self + return &snapshot, nil + } + } + + return nil, errors.Wrap(errors.ErrNotFound, "SRegion.GetSnapshot") +} + +func (self *SRegion) GetSnapshots(diskId string) ([]SSnapshot, error) { + params := map[string]string{ + "regionId": self.GetId(), + } + + if len(diskId) > 0 { + params["volumeId"] = diskId + } + + resp, err := self.client.DoGet("/apiproxy/v3/ondemand/queryVBSDetails", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSnapshots.DoGet") + } + + ret := make([]SSnapshot, 0) + err = resp.Unmarshal(&ret, "returnObj", "backups") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetSnapshots.Unmarshal") + } + + for i := range ret { + ret[i].region = self + } + + return ret, nil +} diff --git a/pkg/multicloud/ctyun/storage.go b/pkg/multicloud/ctyun/storage.go new file mode 100644 index 0000000000..bbb7ed8c55 --- /dev/null +++ b/pkg/multicloud/ctyun/storage.go @@ -0,0 +1,154 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +var StorageTypes = []string{ + api.STORAGE_CTYUN_SAS, + api.STORAGE_CTYUN_SATA, + api.STORAGE_CTYUN_SSD, +} + +type SStorage struct { + zone *SZone + storageType string +} + +func (self *SStorage) GetId() string { + return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetId(), self.storageType) +} + +func (self *SStorage) GetName() string { + return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerName, self.zone.GetId(), self.storageType) +} + +func (self *SStorage) GetGlobalId() string { + return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetGlobalId(), self.storageType) +} + +func (self *SStorage) GetStatus() string { + return api.STORAGE_ONLINE +} + +func (self *SStorage) Refresh() error { + return nil +} + +func (self *SStorage) IsEmulated() bool { + return true +} + +func (self *SStorage) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { + return self.zone.region.getStoragecache() +} + +func (self *SStorage) GetIZone() cloudprovider.ICloudZone { + return self.zone +} + +// http://ctyun-api-url/apiproxy/v3/ondemand/queryVolumes +// http://ctyun-api-url/apiproxy/v3/queryDataDisks +func (self *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + disks, err := self.zone.region.GetDisks() + if err != nil { + return nil, err + } + + // 按storage type 过滤出disk + filtedDisks := make([]SDisk, 0) + for i := range disks { + disk := disks[i] + if disk.VolumeType == self.storageType && disk.AvailabilityZone == self.zone.GetId() { + filtedDisks = append(filtedDisks, disk) + } + } + + idisks := make([]cloudprovider.ICloudDisk, len(filtedDisks)) + for i := 0; i < len(filtedDisks); i += 1 { + filtedDisks[i].storage = self + idisks[i] = &filtedDisks[i] + } + return idisks, nil +} + +func (self *SStorage) GetStorageType() string { + return self.storageType +} + +func (self *SStorage) GetMediumType() string { + if self.storageType == api.STORAGE_CTYUN_SSD { + return api.DISK_TYPE_SSD + } else { + return api.DISK_TYPE_ROTATE + } +} + +func (self *SStorage) GetCapacityMB() int64 { + return 0 +} + +func (self *SStorage) GetStorageConf() jsonutils.JSONObject { + return jsonutils.NewDict() +} + +func (self *SStorage) GetEnabled() bool { + return true +} + +func (self *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SStorage) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) { + if len(idStr) == 0 { + log.Debugf("GetIDiskById disk id should not be empty") + return nil, cloudprovider.ErrNotFound + } + + if disk, err := self.zone.region.GetDisk(idStr); err != nil { + return nil, err + } else { + disk.storage = self + return disk, nil + } +} + +func (self *SStorage) GetMountPoint() string { + return "" +} + +func (self *SStorage) IsSysDiskStore() bool { + return true +} + +func (self *SRegion) getStoragecache() *SStoragecache { + if self.storageCache == nil { + self.storageCache = &SStoragecache{region: self} + } + return self.storageCache +} diff --git a/pkg/multicloud/ctyun/storagecache.go b/pkg/multicloud/ctyun/storagecache.go new file mode 100644 index 0000000000..090ee1b9b3 --- /dev/null +++ b/pkg/multicloud/ctyun/storagecache.go @@ -0,0 +1,124 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "context" + "fmt" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SStoragecache struct { + region *SRegion + + iimages []cloudprovider.ICloudImage +} + +func (self *SStoragecache) CreateIImage(snapshotId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, isForce bool) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func GetBucketName(regionId string, imageId string) string { + return fmt.Sprintf("imgcache-%s-%s", strings.ToLower(regionId), imageId) +} + +func (self *SStoragecache) fetchImages() error { + imagesGold, err := self.region.GetImages(ImageOwnerPublic) + if err != nil { + return err + } + + imagesSelf, err := self.region.GetImages(ImageOwnerSelf) + if err != nil { + return err + } + + self.iimages = make([]cloudprovider.ICloudImage, len(imagesGold)+len(imagesSelf)) + for i := range imagesGold { + imagesGold[i].storageCache = self + self.iimages[i] = &imagesGold[i] + } + for i := range imagesSelf { + imagesSelf[i].storageCache = self + self.iimages[i+len(imagesGold)] = &imagesSelf[i] + } + return nil +} + +func (self *SStoragecache) GetId() string { + return fmt.Sprintf("%s-%s", self.region.client.providerId, self.region.GetId()) +} + +func (self *SStoragecache) GetName() string { + return fmt.Sprintf("%s-%s", self.region.client.providerName, self.region.GetId()) +} + +func (self *SStoragecache) GetGlobalId() string { + return fmt.Sprintf("%s-%s", self.region.client.providerId, self.region.GetGlobalId()) +} + +func (self *SStoragecache) GetStatus() string { + return "available" +} + +func (self *SStoragecache) Refresh() error { + return nil +} + +func (self *SStoragecache) IsEmulated() bool { + return false +} + +func (self *SStoragecache) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) { + if self.iimages == nil { + err := self.fetchImages() + if err != nil { + return nil, err + } + } + return self.iimages, nil +} + +func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) { + image, err := self.region.GetImage(extId) + if err != nil { + return nil, errors.Wrap(err, "SStoragecache.GetIImageById.GetImage") + } + + image.storageCache = self + return image, err +} + +func (self *SStoragecache) GetPath() string { + return "" +} diff --git a/pkg/multicloud/ctyun/vpc.go b/pkg/multicloud/ctyun/vpc.go new file mode 100644 index 0000000000..06a8c24bd2 --- /dev/null +++ b/pkg/multicloud/ctyun/vpc.go @@ -0,0 +1,214 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SVpc struct { + multicloud.SVpc + + region *SRegion + + iwires []cloudprovider.ICloudWire + secgroups []cloudprovider.ICloudSecurityGroup + + ResVpcID string `json:"resVpcId"` + Name string `json:"name"` + CIDR string `json:"cidr"` + ZoneID string `json:"zoneId"` + ZoneName string `json:"zoneName"` + VpcStatus string `json:"vpcStatus"` + RegionID string `json:"regionId"` + CreateDate int64 `json:"createDate"` +} + +func (self *SVpc) addWire(wire *SWire) { + if self.iwires == nil { + self.iwires = make([]cloudprovider.ICloudWire, 0) + } + self.iwires = append(self.iwires, wire) +} + +func (self *SVpc) GetId() string { + return self.ResVpcID +} + +func (self *SVpc) GetName() string { + if len(self.Name) > 0 { + return self.Name + } + return self.ResVpcID +} + +func (self *SVpc) GetGlobalId() string { + return self.GetId() +} + +func (self *SVpc) GetStatus() string { + return api.VPC_STATUS_AVAILABLE +} + +func (self *SVpc) Refresh() error { + new, err := self.region.GetVpc(self.GetId()) + if err != nil { + return err + } + return jsonutils.Update(self, new) +} + +func (self *SVpc) IsEmulated() bool { + return false +} + +func (self *SVpc) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SVpc) GetRegion() cloudprovider.ICloudRegion { + return self.region +} + +func (self *SVpc) GetIsDefault() bool { + return false +} + +func (self *SVpc) GetCidrBlock() string { + return self.CIDR +} + +func (self *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) { + if self.iwires == nil { + err := self.fetchNetworks() + if err != nil { + return nil, err + } + } + return self.iwires, nil +} + +// http://ctyun-api-url/apiproxy/v3/getSecurityGroupRules +// http://ctyun-api-url/apiproxy/v3/getSecurityGroups +func (self *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) { + if self.secgroups == nil { + err := self.fetchSecurityGroups() + if err != nil { + return nil, err + } + } + return self.secgroups, nil +} + +func (self *SVpc) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) { + rts := []cloudprovider.ICloudRouteTable{} + return rts, nil +} + +func (self *SVpc) Delete() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SVpc) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) { + if self.iwires == nil { + err := self.fetchNetworks() + if err != nil { + return nil, err + } + } + for i := 0; i < len(self.iwires); i += 1 { + if self.iwires[i].GetGlobalId() == wireId { + return self.iwires[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SVpc) fetchSecurityGroups() error { + secgroups, err := self.region.GetSecurityGroups("") + if err != nil { + return err + } + + self.secgroups = make([]cloudprovider.ICloudSecurityGroup, len(secgroups)) + for i := 0; i < len(secgroups); i++ { + secgroups[i].vpc = self + self.secgroups[i] = &secgroups[i] + } + + return nil +} + +func (self *SVpc) getWireByRegionId(regionId string) *SWire { + if len(regionId) == 0 { + return nil + } + + for i := 0; i < len(self.iwires); i++ { + wire := self.iwires[i].(*SWire) + + if wire.region.GetId() == regionId { + return wire + } + } + + return nil +} + +func (self *SVpc) fetchNetworks() error { + networks, err := self.region.GetNetwroks(self.GetId()) + if err != nil { + return err + } + + if len(networks) == 0 { + self.iwires = append(self.iwires, &SWire{region: self.region, vpc: self}) + return nil + } + + for i := 0; i < len(networks); i += 1 { + wire := self.getWireByRegionId(self.region.GetId()) + networks[i].wire = wire + wire.addNetwork(&networks[i]) + } + + return nil +} + +func (self *SRegion) GetVpc(vpcId string) (*SVpc, error) { + params := map[string]string{ + "vpcId": vpcId, + "regionId": self.GetId(), + } + + vpc := &SVpc{} + resp, err := self.client.DoGet("/apiproxy/v3/queryVPCDetail", params) + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetVpc.DoGet") + } + + err = resp.Unmarshal(vpc, "returnObj") + if err != nil { + return nil, errors.Wrap(err, "SRegion.GetVpc.Unmarshal") + } + + vpc.region = self + return vpc, nil +} diff --git a/pkg/multicloud/ctyun/wire.go b/pkg/multicloud/ctyun/wire.go new file mode 100644 index 0000000000..ec84ade432 --- /dev/null +++ b/pkg/multicloud/ctyun/wire.go @@ -0,0 +1,115 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "fmt" + + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SWire struct { + region *SRegion + vpc *SVpc + + inetworks []cloudprovider.ICloudNetwork +} + +func (self *SWire) GetId() string { + return fmt.Sprintf("%s-%s", self.vpc.GetId(), self.region.GetId()) +} + +func (self *SWire) GetName() string { + return self.GetId() +} + +func (self *SWire) GetGlobalId() string { + return fmt.Sprintf("%s-%s", self.vpc.GetGlobalId(), self.region.GetGlobalId()) +} + +func (self *SWire) GetStatus() string { + return "available" +} + +func (self *SWire) Refresh() error { + return nil +} + +func (self *SWire) IsEmulated() bool { + return true +} + +func (self *SWire) GetMetadata() *jsonutils.JSONDict { + return nil +} + +// http://ctyun-api-url/apiproxy/v3/queryVPCDetail +func (self *SWire) GetIVpc() cloudprovider.ICloudVpc { + return self.vpc +} + +func (self *SWire) GetIZone() cloudprovider.ICloudZone { + return nil +} + +// http://ctyun-api-url/apiproxy/v3/getSubnets +func (self *SWire) GetINetworks() ([]cloudprovider.ICloudNetwork, error) { + if self.inetworks == nil { + err := self.vpc.fetchNetworks() + if err != nil { + return nil, err + } + } + return self.inetworks, nil +} + +func (self *SWire) GetBandwidth() int { + return 10000 +} + +func (self *SWire) GetINetworkById(netid string) (cloudprovider.ICloudNetwork, error) { + networks, err := self.GetINetworks() + if err != nil { + return nil, err + } + for i := 0; i < len(networks); i += 1 { + if networks[i].GetGlobalId() == netid { + return networks[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SWire) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SWire) addNetwork(network *SNetwork) { + if self.inetworks == nil { + self.inetworks = make([]cloudprovider.ICloudNetwork, 0) + } + find := false + for i := 0; i < len(self.inetworks); i += 1 { + if self.inetworks[i].GetId() == network.GetId() { + find = true + break + } + } + if !find { + self.inetworks = append(self.inetworks, network) + } +} diff --git a/pkg/multicloud/ctyun/zone.go b/pkg/multicloud/ctyun/zone.go new file mode 100644 index 0000000000..9d78979f1d --- /dev/null +++ b/pkg/multicloud/ctyun/zone.go @@ -0,0 +1,134 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ctyun + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SZone struct { + region *SRegion + host *SHost + + iwires []cloudprovider.ICloudWire + istorages []cloudprovider.ICloudStorage + /* 支持的磁盘种类集合 */ + storageTypes []string + + RegionID string `json:"regionId"` + ZoneID string `json:"zoneId"` + ZoneName string `json:"zoneName"` + ZoneType string `json:"zoneType"` +} + +func (self *SZone) addWire(wire *SWire) { + if self.iwires == nil { + self.iwires = make([]cloudprovider.ICloudWire, 0) + } + self.iwires = append(self.iwires, wire) +} + +func (self *SZone) GetId() string { + return self.ZoneID +} + +func (self *SZone) GetName() string { + return self.ZoneName +} + +func (self *SZone) GetGlobalId() string { + return self.GetId() +} + +func (self *SZone) GetStatus() string { + return "enable" +} + +func (self *SZone) Refresh() error { + return nil +} + +func (self *SZone) IsEmulated() bool { + return false +} + +func (self *SZone) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SZone) GetIRegion() cloudprovider.ICloudRegion { + return self.region +} + +func (self *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) { + return []cloudprovider.ICloudHost{self.getHost()}, nil +} + +func (self *SZone) GetIHostById(id string) (cloudprovider.ICloudHost, error) { + host := self.getHost() + if host.GetGlobalId() == id { + return host, nil + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SZone) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + if self.istorages == nil { + self.fetchStorages() + } + return self.istorages, nil +} + +func (self *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + if self.istorages == nil { + self.fetchStorages() + } + for i := 0; i < len(self.istorages); i += 1 { + if self.istorages[i].GetGlobalId() == id { + return self.istorages[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SZone) getHost() *SHost { + if self.host == nil { + self.host = &SHost{zone: self, projectId: self.region.client.projectId} + } + return self.host +} + +func (self *SZone) fetchStorages() error { + self.getStorageType() + self.istorages = make([]cloudprovider.ICloudStorage, len(self.storageTypes)) + + for i, sc := range self.storageTypes { + storage := SStorage{zone: self, storageType: sc} + self.istorages[i] = &storage + } + return nil +} + +func (self *SZone) getStorageType() { + if len(self.storageTypes) == 0 { + self.storageTypes = StorageTypes + } +} + +func (self *SZone) GetIWires() ([]cloudprovider.ICloudWire, error) { + return self.iwires, nil +} diff --git a/pkg/multicloud/google/doc.go b/pkg/multicloud/google/doc.go index 9aeaa8dfc4..c86aac10fa 100644 --- a/pkg/multicloud/google/doc.go +++ b/pkg/multicloud/google/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package google // import "yunion.io/x/onecloud/pkg/multicloud/google" diff --git a/pkg/multicloud/google/provider/doc.go b/pkg/multicloud/google/provider/doc.go index 7ff1ffef50..83aa7a1e10 100644 --- a/pkg/multicloud/google/provider/doc.go +++ b/pkg/multicloud/google/provider/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package provider // import "yunion.io/x/onecloud/pkg/multicloud/google/provider" diff --git a/pkg/multicloud/google/shell/doc.go b/pkg/multicloud/google/shell/doc.go index e7936b2011..0cfa0144c9 100644 --- a/pkg/multicloud/google/shell/doc.go +++ b/pkg/multicloud/google/shell/doc.go @@ -1 +1,15 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package shell // import "yunion.io/x/onecloud/pkg/multicloud/google/shell" diff --git a/pkg/multicloud/loader/loader.go b/pkg/multicloud/loader/loader.go index a1a43974e8..600ccafc56 100644 --- a/pkg/multicloud/loader/loader.go +++ b/pkg/multicloud/loader/loader.go @@ -20,6 +20,7 @@ import ( _ "yunion.io/x/onecloud/pkg/multicloud/aliyun/provider" _ "yunion.io/x/onecloud/pkg/multicloud/aws/provider" _ "yunion.io/x/onecloud/pkg/multicloud/azure/provider" + _ "yunion.io/x/onecloud/pkg/multicloud/ctyun/provider" _ "yunion.io/x/onecloud/pkg/multicloud/esxi/provider" // private clouds _ "yunion.io/x/onecloud/pkg/multicloud/google/provider" // public clouds _ "yunion.io/x/onecloud/pkg/multicloud/huawei/provider"