From 344596cdf0ef0e879abe553e40a425becbd0d852 Mon Sep 17 00:00:00 2001 From: ioito Date: Fri, 7 Jan 2022 18:57:56 +0800 Subject: [PATCH] feat(region): add nutanix sync --- cmd/climc/shell/compute/cloudaccounts.go | 3 + cmd/ncli/main.go | 164 ++++++++ pkg/apis/compute/cloudaccount_const.go | 5 + pkg/apis/compute/guest_const.go | 5 + pkg/apis/compute/host_const.go | 1 + pkg/compute/guestdrivers/nutanix.go | 152 +++++++ pkg/compute/hostdrivers/nutanix.go | 45 +++ pkg/compute/models/cloudsync.go | 2 - pkg/compute/models/guests.go | 22 +- pkg/compute/regiondrivers/nutanix.go | 56 +++ pkg/mcclient/options/cloudaccounts.go | 31 ++ pkg/multicloud/loader/loader.go | 1 + pkg/multicloud/no_lb_region.go | 59 +++ pkg/multicloud/nutanix/cluster.go | 279 +++++++++++++ pkg/multicloud/nutanix/disk.go | 312 +++++++++++++++ pkg/multicloud/nutanix/doc.go | 1 + pkg/multicloud/nutanix/host.go | 414 ++++++++++++++++++++ pkg/multicloud/nutanix/image.go | 159 ++++++++ pkg/multicloud/nutanix/instance.go | 276 +++++++++++++ pkg/multicloud/nutanix/instancenic.go | 102 +++++ pkg/multicloud/nutanix/network.go | 120 ++++++ pkg/multicloud/nutanix/nutanix.go | 212 ++++++++++ pkg/multicloud/nutanix/provider/doc.go | 1 + pkg/multicloud/nutanix/provider/provider.go | 201 ++++++++++ pkg/multicloud/nutanix/region.go | 198 ++++++++++ pkg/multicloud/nutanix/shell/cluster.go | 47 +++ pkg/multicloud/nutanix/shell/disk.go | 49 +++ pkg/multicloud/nutanix/shell/doc.go | 1 + pkg/multicloud/nutanix/shell/host.go | 47 +++ pkg/multicloud/nutanix/shell/image.go | 47 +++ pkg/multicloud/nutanix/shell/instance.go | 47 +++ pkg/multicloud/nutanix/shell/printutils.go | 25 ++ pkg/multicloud/nutanix/shell/storage.go | 47 +++ pkg/multicloud/nutanix/shell/vpc.go | 47 +++ pkg/multicloud/nutanix/storage.go | 318 +++++++++++++++ pkg/multicloud/nutanix/storagecache.go | 119 ++++++ pkg/multicloud/nutanix/vpc.go | 127 ++++++ pkg/multicloud/nutanix/wire.go | 93 +++++ pkg/multicloud/nutanix/zone.go | 118 ++++++ 39 files changed, 3946 insertions(+), 7 deletions(-) create mode 100644 cmd/ncli/main.go create mode 100644 pkg/compute/guestdrivers/nutanix.go create mode 100644 pkg/compute/hostdrivers/nutanix.go create mode 100644 pkg/compute/regiondrivers/nutanix.go create mode 100644 pkg/multicloud/no_lb_region.go create mode 100644 pkg/multicloud/nutanix/cluster.go create mode 100644 pkg/multicloud/nutanix/disk.go create mode 100644 pkg/multicloud/nutanix/doc.go create mode 100644 pkg/multicloud/nutanix/host.go create mode 100644 pkg/multicloud/nutanix/image.go create mode 100644 pkg/multicloud/nutanix/instance.go create mode 100644 pkg/multicloud/nutanix/instancenic.go create mode 100644 pkg/multicloud/nutanix/network.go create mode 100644 pkg/multicloud/nutanix/nutanix.go create mode 100644 pkg/multicloud/nutanix/provider/doc.go create mode 100644 pkg/multicloud/nutanix/provider/provider.go create mode 100644 pkg/multicloud/nutanix/region.go create mode 100644 pkg/multicloud/nutanix/shell/cluster.go create mode 100644 pkg/multicloud/nutanix/shell/disk.go create mode 100644 pkg/multicloud/nutanix/shell/doc.go create mode 100644 pkg/multicloud/nutanix/shell/host.go create mode 100644 pkg/multicloud/nutanix/shell/image.go create mode 100644 pkg/multicloud/nutanix/shell/instance.go create mode 100644 pkg/multicloud/nutanix/shell/printutils.go create mode 100644 pkg/multicloud/nutanix/shell/storage.go create mode 100644 pkg/multicloud/nutanix/shell/vpc.go create mode 100644 pkg/multicloud/nutanix/storage.go create mode 100644 pkg/multicloud/nutanix/storagecache.go create mode 100644 pkg/multicloud/nutanix/vpc.go create mode 100644 pkg/multicloud/nutanix/wire.go create mode 100644 pkg/multicloud/nutanix/zone.go diff --git a/cmd/climc/shell/compute/cloudaccounts.go b/cmd/climc/shell/compute/cloudaccounts.go index 6ccaba3799..c68b56877d 100644 --- a/cmd/climc/shell/compute/cloudaccounts.go +++ b/cmd/climc/shell/compute/cloudaccounts.go @@ -48,6 +48,7 @@ func init() { cmd.CreateWithKeyword("create-ecloud", &options.SEcloudCloudAccountCreateOptions{}) cmd.CreateWithKeyword("create-jdcloud", &options.SJDcloudCloudAccountCreateOptions{}) cmd.CreateWithKeyword("create-cloudpods", &options.SCloudpodsCloudAccountCreateOptions{}) + cmd.CreateWithKeyword("create-nutanix", &options.SNutanixCloudAccountCreateOptions{}) cmd.UpdateWithKeyword("update-vmware", &options.SVMwareCloudAccountUpdateOptions{}) cmd.UpdateWithKeyword("update-aliyun", &options.SAliyunCloudAccountUpdateOptions{}) @@ -64,6 +65,7 @@ func init() { cmd.UpdateWithKeyword("update-ctyun", &options.SCtyunCloudAccountUpdateOptions{}) cmd.UpdateWithKeyword("update-jdcloud", &options.SJDcloudCloudAccountUpdateOptions{}) cmd.UpdateWithKeyword("update-cloudpods", &options.SCloudpodsCloudAccountUpdateOptions{}) + cmd.UpdateWithKeyword("update-nutanix", &options.SNutanixCloudAccountUpdateOptions{}) cmd.Perform("update-credential", &options.CloudaccountUpdateCredentialOptions{}) @@ -82,6 +84,7 @@ func init() { cmd.PerformWithKeyword("update-credential-ctyun", "update-credential", &options.SCtyunCloudAccountUpdateCredentialOptions{}) cmd.PerformWithKeyword("update-credential-jdcloud", "update-credential", &options.SJDcloudCloudAccountUpdateCredentialOptions{}) cmd.PerformWithKeyword("update-credential-cloudpods", "update-credential", &options.SCloudpodsCloudAccountUpdateCredentialOptions{}) + cmd.PerformWithKeyword("update-credential-nutanix", "update-credential", &options.SNutanixCloudAccountUpdateCredentialOptions{}) cmd.PerformWithKeyword("test-connectivity-google", "test-connectivity", &options.SGoogleCloudAccountUpdateCredentialOptions{}) cmd.PerformWithKeyword("test-connectivity-vmware", "test-connectivity", &options.SVMwareCloudAccountUpdateCredentialOptions{}) diff --git a/cmd/ncli/main.go b/cmd/ncli/main.go new file mode 100644 index 0000000000..a2f6228538 --- /dev/null +++ b/cmd/ncli/main.go @@ -0,0 +1,164 @@ +// 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" + "net/http" + "net/url" + "os" + + "golang.org/x/net/http/httpproxy" + + "yunion.io/x/structarg" + + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud/nutanix" + _ "yunion.io/x/onecloud/pkg/multicloud/nutanix/shell" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +type BaseOptions struct { + Debug bool `help:"debug mode"` + Help bool `help:"Show help"` + Host string `help:"Host" default:"$NUTANIX_HOST" metavar:"NUTANIX_HOST"` + Username string `help:"Username" default:"$NUTANIX_USERNAME" metavar:"NUTANIX_USERNAME"` + Password string `help:"Password" default:"$NUTANIX_PASSWORD" metavar:"NUTANIX_PASSWORD"` + Port int `help:"Port" default:"$NUTANIX_PORT|9440" metavar:"NUTANIX_PORT"` + SUBCOMMAND string `help:"ncli subcommand" subcommand:"true"` +} + +func getSubcommandParser() (*structarg.ArgumentParser, error) { + parse, e := structarg.NewArgumentParser(&BaseOptions{}, + "ncli", + "Command-line interface to nutanix API.", + `See "ncli 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) (*nutanix.SRegion, error) { + if len(options.Host) == 0 { + return nil, fmt.Errorf("Missing host") + } + + if len(options.Username) == 0 { + return nil, fmt.Errorf("Missing username") + } + + if len(options.Password) == 0 { + return nil, fmt.Errorf("Missing password") + } + + cfg := &httpproxy.Config{ + HTTPProxy: os.Getenv("HTTP_PROXY"), + HTTPSProxy: os.Getenv("HTTPS_PROXY"), + NoProxy: os.Getenv("NO_PROXY"), + } + cfgProxyFunc := cfg.ProxyFunc() + proxyFunc := func(req *http.Request) (*url.URL, error) { + return cfgProxyFunc(req.URL) + } + + cli, err := nutanix.NewNutanixClient( + nutanix.NewNutanixClientConfig( + options.Host, + options.Username, + options.Password, + options.Port, + ).Debug(options.Debug). + CloudproviderConfig( + cloudprovider.ProviderConfig{ + ProxyFunc: proxyFunc, + }, + ), + ) + if err != nil { + return nil, err + } + + return cli.GetRegion() +} + +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()) + return + } + subcmd := parser.GetSubcommand() + subparser := subcmd.GetSubParser() + if e != nil { + if subparser != nil { + fmt.Print(subparser.Usage()) + } else { + fmt.Print(parser.Usage()) + } + showErrorAndExit(e) + return + } + suboptions := subparser.Options() + if options.SUBCOMMAND == "help" { + e = subcmd.Invoke(suboptions) + } else { + var region *nutanix.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 7ee2d4eb3d..d5dba48e5f 100644 --- a/pkg/apis/compute/cloudaccount_const.go +++ b/pkg/apis/compute/cloudaccount_const.go @@ -38,6 +38,7 @@ const ( CLOUD_PROVIDER_ONECLOUD = "OneCloud" CLOUD_PROVIDER_VMWARE = "VMware" + CLOUD_PROVIDER_NUTANIX = "Nutanix" CLOUD_PROVIDER_ALIYUN = "Aliyun" CLOUD_PROVIDER_APSARA = "Apsara" CLOUD_PROVIDER_QCLOUD = "Qcloud" @@ -118,6 +119,7 @@ var ( CLOUD_PROVIDER_ECLOUD, CLOUD_PROVIDER_JDCLOUD, CLOUD_PROVIDER_CLOUDPODS, + CLOUD_PROVIDER_NUTANIX, } CLOUD_PROVIDER_HOST_TYPE_MAP = map[string][]string{ @@ -174,6 +176,9 @@ var ( CLOUD_PROVIDER_CLOUDPODS: { HOST_TYPE_CLOUDPODS, }, + CLOUD_PROVIDER_NUTANIX: { + HOST_TYPE_NUTANIX, + }, } ) diff --git a/pkg/apis/compute/guest_const.go b/pkg/apis/compute/guest_const.go index bd4171fe0e..05ccf4a20c 100644 --- a/pkg/apis/compute/guest_const.go +++ b/pkg/apis/compute/guest_const.go @@ -175,6 +175,7 @@ const ( HYPERVISOR_ECLOUD = "ecloud" HYPERVISOR_JDCLOUD = "jdcloud" HYPERVISOR_CLOUDPODS = "cloudpods" + HYPERVISOR_NUTANIX = "nutanix" // HYPERVISOR_DEFAULT = HYPERVISOR_KVM HYPERVISOR_DEFAULT = HYPERVISOR_KVM @@ -222,6 +223,7 @@ var HYPERVISORS = []string{ HYPERVISOR_ECLOUD, HYPERVISOR_JDCLOUD, HYPERVISOR_CLOUDPODS, + HYPERVISOR_NUTANIX, } var ONECLOUD_HYPERVISORS = []string{ @@ -249,6 +251,7 @@ var PRIVATE_CLOUD_HYPERVISORS = []string{ HYPERVISOR_APSARA, HYPERVISOR_CLOUDPODS, HYPERVISOR_HCSO, + HYPERVISOR_NUTANIX, } // var HYPERVISORS = []string{HYPERVISOR_ALIYUN} @@ -273,6 +276,7 @@ var HYPERVISOR_HOSTTYPE = map[string]string{ HYPERVISOR_ECLOUD: HOST_TYPE_ECLOUD, HYPERVISOR_JDCLOUD: HOST_TYPE_JDCLOUD, HYPERVISOR_CLOUDPODS: HOST_TYPE_CLOUDPODS, + HYPERVISOR_NUTANIX: HOST_TYPE_NUTANIX, } var HOSTTYPE_HYPERVISOR = map[string]string{ @@ -295,6 +299,7 @@ var HOSTTYPE_HYPERVISOR = map[string]string{ HOST_TYPE_ECLOUD: HYPERVISOR_ECLOUD, HOST_TYPE_JDCLOUD: HYPERVISOR_JDCLOUD, HOST_TYPE_CLOUDPODS: HYPERVISOR_CLOUDPODS, + HOST_TYPE_NUTANIX: HYPERVISOR_NUTANIX, } const ( diff --git a/pkg/apis/compute/host_const.go b/pkg/apis/compute/host_const.go index 0e8d57c666..4f0e91031f 100644 --- a/pkg/apis/compute/host_const.go +++ b/pkg/apis/compute/host_const.go @@ -38,6 +38,7 @@ const ( HOST_TYPE_ECLOUD = "ecloud" HOST_TYPE_JDCLOUD = "jdcloud" HOST_TYPE_CLOUDPODS = "cloudpods" + HOST_TYPE_NUTANIX = "nutanix" HOST_TYPE_DEFAULT = HOST_TYPE_HYPERVISOR diff --git a/pkg/compute/guestdrivers/nutanix.go b/pkg/compute/guestdrivers/nutanix.go new file mode 100644 index 0000000000..fde0abf8af --- /dev/null +++ b/pkg/compute/guestdrivers/nutanix.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 guestdrivers + +import ( + "context" + "time" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/compute/options" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/billing" + "yunion.io/x/onecloud/pkg/util/rbacutils" +) + +type SNutanixGuestDriver struct { + SManagedVirtualizedGuestDriver +} + +func init() { + driver := SNutanixGuestDriver{} + models.RegisterGuestDriver(&driver) +} + +func (self *SNutanixGuestDriver) DoScheduleCPUFilter() bool { return true } + +func (self *SNutanixGuestDriver) DoScheduleMemoryFilter() bool { return true } + +func (self *SNutanixGuestDriver) DoScheduleSKUFilter() bool { return false } + +func (self *SNutanixGuestDriver) DoScheduleStorageFilter() bool { return true } + +func (self *SNutanixGuestDriver) GetHypervisor() string { + return api.HYPERVISOR_NUTANIX +} + +func (self *SNutanixGuestDriver) GetProvider() string { + return api.CLOUD_PROVIDER_NUTANIX +} + +func (self *SNutanixGuestDriver) GetInstanceCapability() cloudprovider.SInstanceCapability { + return cloudprovider.SInstanceCapability{ + Hypervisor: self.GetHypervisor(), + Provider: self.GetProvider(), + DefaultAccount: cloudprovider.SDefaultAccount{ + Linux: cloudprovider.SOsDefaultAccount{ + DefaultAccount: api.VM_DEFAULT_LINUX_LOGIN_USER, + Changeable: true, + }, + Windows: cloudprovider.SOsDefaultAccount{ + DefaultAccount: api.VM_DEFAULT_WINDOWS_LOGIN_USER, + Changeable: false, + }, + }, + } +} + +func (self *SNutanixGuestDriver) GetComputeQuotaKeys(scope rbacutils.TRbacScope, ownerId mcclient.IIdentityProvider, brand string) models.SComputeResourceKeys { + keys := models.SComputeResourceKeys{} + keys.SBaseProjectQuotaKeys = quotas.OwnerIdProjectQuotaKeys(scope, ownerId) + keys.CloudEnv = api.CLOUD_ENV_PRIVATE_CLOUD + keys.Provider = api.CLOUD_PROVIDER_NUTANIX + keys.Brand = api.CLOUD_PROVIDER_NUTANIX + keys.Hypervisor = api.HYPERVISOR_NUTANIX + return keys +} + +func (self *SNutanixGuestDriver) GetDefaultSysDiskBackend() string { + return "" +} + +func (self *SNutanixGuestDriver) ChooseHostStorage(host *models.SHost, guest *models.SGuest, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SNutanixGuestDriver) GetMinimalSysDiskSizeGb() int { + return options.Options.DefaultDiskSizeMB / 1024 +} + +func (self *SNutanixGuestDriver) RequestSyncSecgroupsOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error { + return nil // do nothing, not support securitygroup +} + +func (self *SNutanixGuestDriver) GetMaxSecurityGroupCount() int { + //暂不支持绑定安全组 + return 0 +} + +func (self *SNutanixGuestDriver) GetDetachDiskStatus() ([]string, error) { + return []string{api.VM_READY, api.VM_RUNNING}, nil +} + +func (self *SNutanixGuestDriver) GetAttachDiskStatus() ([]string, error) { + return []string{api.VM_READY, api.VM_RUNNING}, nil +} + +func (self *SNutanixGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) { + return []string{api.VM_READY}, nil +} + +func (self *SNutanixGuestDriver) CanKeepDetachDisk() bool { + return false +} + +func (self *SNutanixGuestDriver) GetRebuildRootStatus() ([]string, error) { + return []string{api.VM_READY}, nil +} + +func (self *SNutanixGuestDriver) GetDeployStatus() ([]string, error) { + return []string{api.VM_READY}, nil +} + +func (self *SNutanixGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerCreateEipInput) error { + return httperrors.NewInputParameterError("%s not support create eip", self.GetHypervisor()) +} + +func (self *SNutanixGuestDriver) AllowReconfigGuest() bool { + return true +} + +func (self *SNutanixGuestDriver) RequestRenewInstance(guest *models.SGuest, bc billing.SBillingCycle) (time.Time, error) { + return time.Time{}, nil +} + +func (self *SNutanixGuestDriver) IsSupportEip() bool { + return false +} + +func (self *SNutanixGuestDriver) IsSupportCdrom(guest *models.SGuest) (bool, error) { + return false, nil +} + +func (self *SNutanixGuestDriver) RequestRemoteUpdate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, replaceTags bool) error { + return nil +} diff --git a/pkg/compute/hostdrivers/nutanix.go b/pkg/compute/hostdrivers/nutanix.go new file mode 100644 index 0000000000..95286280a1 --- /dev/null +++ b/pkg/compute/hostdrivers/nutanix.go @@ -0,0 +1,45 @@ +// 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 ( + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type SNutanixHostDriver struct { + SManagedVirtualizationHostDriver +} + +func init() { + driver := SNutanixHostDriver{} + models.RegisterHostDriver(&driver) +} + +func (self *SNutanixHostDriver) GetHostType() string { + return api.HOST_TYPE_NUTANIX +} + +func (self *SNutanixHostDriver) GetHypervisor() string { + return api.HYPERVISOR_NUTANIX +} + +func (self *SNutanixHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error { + return nil +} + +func (driver *SNutanixHostDriver) GetStoragecacheQuota(host *models.SHost) int { + return 100 +} diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index 7d4f427614..6254ee7ef6 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -984,8 +984,6 @@ func syncVMEip(ctx context.Context, userCred mcclient.TokenCredential, provider func syncVMSecgroups(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, localVM *SGuest, remoteVM cloudprovider.ICloudVM) error { secgroupIds, err := remoteVM.GetSecurityGroupIds() if err != nil { - // msg := fmt.Sprintf("GetSecurityGroupIds for VM %s failed %s", remoteVM.GetName(), err) - // log.Errorf(msg) return errors.Wrap(err, "remoteVM.GetSecurityGroupIds") } return localVM.SyncVMSecgroups(ctx, userCred, secgroupIds) diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 959d8f6d2c..4676894008 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -646,16 +646,18 @@ func (manager *SGuestManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field func (manager *SGuestManager) InitializeData() error { guests := make([]SGuest, 0, 10) - q := manager.Query().Equals("hypervisor", "esxi") + q := manager.Query() + q = q.In("hypervisor", []string{api.HYPERVISOR_ESXI, api.HYPERVISOR_NUTANIX}).Filter( + sqlchemy.NOT( + sqlchemy.IsNullOrEmpty(q.Field("secgrp_id")), + ), + ) err := db.FetchModelObjects(manager, q, &guests) if err != nil { return errors.Wrap(err, "db.FetchModelObjects") } - // remove secgroup for esxi guest + // remove secgroup for esxi nutanix guest for i := range guests { - if len(guests[i].SecgrpId) == 0 { - continue - } db.Update(&guests[i], func() error { guests[i].SecgrpId = "" return nil @@ -4889,6 +4891,16 @@ func getSecgroupsBySecgroupExternalIds(managerId string, externalIds []string) ( } func (self *SGuest) SyncVMSecgroups(ctx context.Context, userCred mcclient.TokenCredential, externalIds []string) error { + // clear secgroup if vm not support security group + if self.GetDriver().GetMaxSecurityGroupCount() == 0 && (len(self.SecgrpId) > 0 || len(self.AdminSecgrpId) > 0) { + _, err := db.Update(self, func() error { + self.SecgrpId = "" + self.AdminSecgrpId = "" + return nil + }) + return err + } + secgroups, err := self.getSecgroupsBySecgroupExternalIds(externalIds) if err != nil { return errors.Wrap(err, "getSecgroupsBySecgroupExternalIds") diff --git a/pkg/compute/regiondrivers/nutanix.go b/pkg/compute/regiondrivers/nutanix.go new file mode 100644 index 0000000000..f2f6419a49 --- /dev/null +++ b/pkg/compute/regiondrivers/nutanix.go @@ -0,0 +1,56 @@ +// 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" + "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 SNutanixRegionDriver struct { + SManagedVirtualizationRegionDriver +} + +func init() { + driver := SNutanixRegionDriver{} + models.RegisterRegionDriver(&driver) +} + +func (self *SNutanixRegionDriver) GetProvider() string { + return api.CLOUD_PROVIDER_NUTANIX +} + +func (self *SNutanixRegionDriver) ValidateCreateLoadbalancerData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewUnsupportOperationError("%s does not support creating loadbalancer", self.GetProvider()) +} + +func (self *SNutanixRegionDriver) ValidateCreateLoadbalancerAclData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("%s does not support creating loadbalancer acl", self.GetProvider()) +} + +func (self *SNutanixRegionDriver) ValidateCreateLoadbalancerCertificateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("%s does not support creating loadbalancer certificate", self.GetProvider()) +} + +func (self *SNutanixRegionDriver) ValidateCreateSnapshotData(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, storage *models.SStorage, input *api.SnapshotCreateInput) error { + return fmt.Errorf("%s does not support creating snapshot", self.GetProvider()) +} diff --git a/pkg/mcclient/options/cloudaccounts.go b/pkg/mcclient/options/cloudaccounts.go index a3593490df..417d97b846 100644 --- a/pkg/mcclient/options/cloudaccounts.go +++ b/pkg/mcclient/options/cloudaccounts.go @@ -47,6 +47,13 @@ type SVMwareCredentialWithEnvironment struct { Port string `help:"VMware VCenter/ESXi host port" default:"443"` } +type SNutanixCredentialWithEnvironment struct { + SUserPasswordCredential + + Host string `help:"Nutanix host" positional:"true"` + Port string `help:"Nutanix host port" default:"9440"` +} + type SAzureCredential struct { ClientID string `help:"Azure client_id" positional:"true"` ClientSecret string `help:"Azure clinet_secret" positional:"true"` @@ -1039,3 +1046,27 @@ type ClouaccountProjectMappingOptions struct { func (opts *ClouaccountProjectMappingOptions) Params() (jsonutils.JSONObject, error) { return jsonutils.Marshal(map[string]string{"project_mapping_id": opts.ProjectMappingId}), nil } + +type SNutanixCloudAccountCreateOptions struct { + SCloudAccountCreateBaseOptions + SNutanixCredentialWithEnvironment +} + +func (opts *SNutanixCloudAccountCreateOptions) Params() (jsonutils.JSONObject, error) { + params := jsonutils.Marshal(opts) + params.(*jsonutils.JSONDict).Add(jsonutils.NewString("Nutanix"), "provider") + return params, nil +} + +type SNutanixCloudAccountUpdateCredentialOptions struct { + SCloudAccountIdOptions + SUserPasswordCredential +} + +func (opts *SNutanixCloudAccountUpdateCredentialOptions) Params() (jsonutils.JSONObject, error) { + return jsonutils.Marshal(opts.SUserPasswordCredential), nil +} + +type SNutanixCloudAccountUpdateOptions struct { + SCloudAccountUpdateBaseOptions +} diff --git a/pkg/multicloud/loader/loader.go b/pkg/multicloud/loader/loader.go index 54a147691c..f3bd4fc48f 100644 --- a/pkg/multicloud/loader/loader.go +++ b/pkg/multicloud/loader/loader.go @@ -29,6 +29,7 @@ import ( _ "yunion.io/x/onecloud/pkg/multicloud/hcso/provider" _ "yunion.io/x/onecloud/pkg/multicloud/huawei/provider" _ "yunion.io/x/onecloud/pkg/multicloud/jdcloud/provider" // public clouds + _ "yunion.io/x/onecloud/pkg/multicloud/nutanix/provider" // private clouds _ "yunion.io/x/onecloud/pkg/multicloud/objectstore/ceph/provider" _ "yunion.io/x/onecloud/pkg/multicloud/objectstore/provider" _ "yunion.io/x/onecloud/pkg/multicloud/objectstore/xsky/provider" diff --git a/pkg/multicloud/no_lb_region.go b/pkg/multicloud/no_lb_region.go new file mode 100644 index 0000000000..f735e1eb94 --- /dev/null +++ b/pkg/multicloud/no_lb_region.go @@ -0,0 +1,59 @@ +// 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 multicloud + +import "yunion.io/x/onecloud/pkg/cloudprovider" + +type SNoLbRegion struct{} + +func (self *SNoLbRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SNoLbRegion) GetILoadBalancerAcls() ([]cloudprovider.ICloudLoadbalancerAcl, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SNoLbRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SNoLbRegion) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SNoLbRegion) GetILoadBalancerById(loadbalancerId string) (cloudprovider.ICloudLoadbalancer, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SNoLbRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloudLoadbalancerAcl, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SNoLbRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SNoLbRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SNoLbRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SNoLbRegion) CreateILoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) { + return nil, cloudprovider.ErrNotSupported +} diff --git a/pkg/multicloud/nutanix/cluster.go b/pkg/multicloud/nutanix/cluster.go new file mode 100644 index 0000000000..f51f3f1689 --- /dev/null +++ b/pkg/multicloud/nutanix/cluster.go @@ -0,0 +1,279 @@ +// 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 nutanix + +type RackableUnits struct { + Id int `json:"id"` + RackableUnitUUID string `json:"rackable_unit_uuid"` + Model string `json:"model"` + ModelName string `json:"model_name"` + //Location interface{} `json:"location"` + Serial string `json:"serial"` + Positions []string `json:"positions"` + Nodes []int `json:"nodes"` + NodeUuids []string `json:"node_uuids"` +} + +type ClusterRedundancyState struct { + CurrentRedundancyFactor int `json:"current_redundancy_factor"` + DesiredRedundancyFactor int `json:"desired_redundancy_factor"` + RedundancyStatus RedundancyStatus `json:"redundancy_status"` +} + +type RedundancyStatus struct { + KCassandraPrepareDone bool `json:"kCassandraPrepareDone"` + KZookeeperPrepareDone bool `json:"kZookeeperPrepareDone"` +} + +type SecurityComplianceConfig struct { + Schedule string `json:"schedule"` + EnableAide bool `json:"enable_aide"` + EnableCore bool `json:"enable_core"` + EnableHighStrengthPassword bool `json:"enable_high_strength_password"` + EnableBanner bool `json:"enable_banner"` + EnableSnmpv3Only bool `json:"enable_snmpv3_only"` +} +type HypervisorSecurityComplianceConfig struct { + Schedule string `json:"schedule"` + EnableAide bool `json:"enable_aide"` + EnableCore bool `json:"enable_core"` + EnableHighStrengthPassword bool `json:"enable_high_strength_password"` + EnableBanner bool `json:"enable_banner"` +} + +type HypervisorLldpConfig struct { + EnableLldpTx bool `json:"enable_lldp_tx"` +} + +type ClusterStats struct { + HypervisorAvgIoLatencyUsecs string `json:"hypervisor_avg_io_latency_usecs"` + NumReadIops string `json:"num_read_iops"` + HypervisorWriteIoBandwidthKBps string `json:"hypervisor_write_io_bandwidth_kBps"` + TimespanUsecs string `json:"timespan_usecs"` + ControllerNumReadIops string `json:"controller_num_read_iops"` + ReadIoPpm string `json:"read_io_ppm"` + ControllerNumIops string `json:"controller_num_iops"` + TotalReadIoTimeUsecs string `json:"total_read_io_time_usecs"` + ControllerTotalReadIoTimeUsecs string `json:"controller_total_read_io_time_usecs"` + ReplicationTransmittedBandwidthKBps string `json:"replication_transmitted_bandwidth_kBps"` + HypervisorNumIo string `json:"hypervisor_num_io"` + ControllerTotalTransformedUsageBytes string `json:"controller_total_transformed_usage_bytes"` + HypervisorCPUUsagePpm string `json:"hypervisor_cpu_usage_ppm"` + ControllerNumWriteIo string `json:"controller_num_write_io"` + AvgReadIoLatencyUsecs string `json:"avg_read_io_latency_usecs"` + ContentCacheLogicalSsdUsageBytes string `json:"content_cache_logical_ssd_usage_bytes"` + ControllerTotalIoTimeUsecs string `json:"controller_total_io_time_usecs"` + ControllerTotalReadIoSizeKbytes string `json:"controller_total_read_io_size_kbytes"` + ControllerNumSeqIo string `json:"controller_num_seq_io"` + ControllerReadIoPpm string `json:"controller_read_io_ppm"` + ContentCacheNumLookups string `json:"content_cache_num_lookups"` + ControllerTotalIoSizeKbytes string `json:"controller_total_io_size_kbytes"` + ContentCacheHitPpm string `json:"content_cache_hit_ppm"` + ControllerNumIo string `json:"controller_num_io"` + HypervisorAvgReadIoLatencyUsecs string `json:"hypervisor_avg_read_io_latency_usecs"` + ContentCacheNumDedupRefCountPph string `json:"content_cache_num_dedup_ref_count_pph"` + NumWriteIops string `json:"num_write_iops"` + ControllerNumRandomIo string `json:"controller_num_random_io"` + NumIops string `json:"num_iops"` + ReplicationReceivedBandwidthKBps string `json:"replication_received_bandwidth_kBps"` + HypervisorNumReadIo string `json:"hypervisor_num_read_io"` + HypervisorTotalReadIoTimeUsecs string `json:"hypervisor_total_read_io_time_usecs"` + ControllerAvgIoLatencyUsecs string `json:"controller_avg_io_latency_usecs"` + HypervisorHypervCPUUsagePpm string `json:"hypervisor_hyperv_cpu_usage_ppm"` + NumIo string `json:"num_io"` + ControllerNumReadIo string `json:"controller_num_read_io"` + HypervisorNumWriteIo string `json:"hypervisor_num_write_io"` + ControllerSeqIoPpm string `json:"controller_seq_io_ppm"` + ControllerReadIoBandwidthKBps string `json:"controller_read_io_bandwidth_kBps"` + ControllerIoBandwidthKBps string `json:"controller_io_bandwidth_kBps"` + HypervisorHypervMemoryUsagePpm string `json:"hypervisor_hyperv_memory_usage_ppm"` + HypervisorTimespanUsecs string `json:"hypervisor_timespan_usecs"` + HypervisorNumWriteIops string `json:"hypervisor_num_write_iops"` + ReplicationNumTransmittedBytes string `json:"replication_num_transmitted_bytes"` + TotalReadIoSizeKbytes string `json:"total_read_io_size_kbytes"` + HypervisorTotalIoSizeKbytes string `json:"hypervisor_total_io_size_kbytes"` + AvgIoLatencyUsecs string `json:"avg_io_latency_usecs"` + HypervisorNumReadIops string `json:"hypervisor_num_read_iops"` + ContentCacheSavedSsdUsageBytes string `json:"content_cache_saved_ssd_usage_bytes"` + ControllerWriteIoBandwidthKBps string `json:"controller_write_io_bandwidth_kBps"` + ControllerWriteIoPpm string `json:"controller_write_io_ppm"` + HypervisorAvgWriteIoLatencyUsecs string `json:"hypervisor_avg_write_io_latency_usecs"` + HypervisorTotalReadIoSizeKbytes string `json:"hypervisor_total_read_io_size_kbytes"` + ReadIoBandwidthKBps string `json:"read_io_bandwidth_kBps"` + HypervisorEsxMemoryUsagePpm string `json:"hypervisor_esx_memory_usage_ppm"` + HypervisorMemoryUsagePpm string `json:"hypervisor_memory_usage_ppm"` + HypervisorNumIops string `json:"hypervisor_num_iops"` + HypervisorIoBandwidthKBps string `json:"hypervisor_io_bandwidth_kBps"` + ControllerNumWriteIops string `json:"controller_num_write_iops"` + TotalIoTimeUsecs string `json:"total_io_time_usecs"` + HypervisorKvmCPUUsagePpm string `json:"hypervisor_kvm_cpu_usage_ppm"` + ContentCachePhysicalSsdUsageBytes string `json:"content_cache_physical_ssd_usage_bytes"` + ControllerRandomIoPpm string `json:"controller_random_io_ppm"` + ControllerAvgReadIoSizeKbytes string `json:"controller_avg_read_io_size_kbytes"` + TotalTransformedUsageBytes string `json:"total_transformed_usage_bytes"` + AvgWriteIoLatencyUsecs string `json:"avg_write_io_latency_usecs"` + NumReadIo string `json:"num_read_io"` + WriteIoBandwidthKBps string `json:"write_io_bandwidth_kBps"` + HypervisorReadIoBandwidthKBps string `json:"hypervisor_read_io_bandwidth_kBps"` + RandomIoPpm string `json:"random_io_ppm"` + ContentCacheNumHits string `json:"content_cache_num_hits"` + TotalUntransformedUsageBytes string `json:"total_untransformed_usage_bytes"` + HypervisorTotalIoTimeUsecs string `json:"hypervisor_total_io_time_usecs"` + NumRandomIo string `json:"num_random_io"` + HypervisorKvmMemoryUsagePpm string `json:"hypervisor_kvm_memory_usage_ppm"` + ControllerAvgWriteIoSizeKbytes string `json:"controller_avg_write_io_size_kbytes"` + ControllerAvgReadIoLatencyUsecs string `json:"controller_avg_read_io_latency_usecs"` + NumWriteIo string `json:"num_write_io"` + HypervisorEsxCPUUsagePpm string `json:"hypervisor_esx_cpu_usage_ppm"` + TotalIoSizeKbytes string `json:"total_io_size_kbytes"` + IoBandwidthKBps string `json:"io_bandwidth_kBps"` + ContentCachePhysicalMemoryUsageBytes string `json:"content_cache_physical_memory_usage_bytes"` + ReplicationNumReceivedBytes string `json:"replication_num_received_bytes"` + ControllerTimespanUsecs string `json:"controller_timespan_usecs"` + NumSeqIo string `json:"num_seq_io"` + ContentCacheSavedMemoryUsageBytes string `json:"content_cache_saved_memory_usage_bytes"` + SeqIoPpm string `json:"seq_io_ppm"` + WriteIoPpm string `json:"write_io_ppm"` + ControllerAvgWriteIoLatencyUsecs string `json:"controller_avg_write_io_latency_usecs"` + ContentCacheLogicalMemoryUsageBytes string `json:"content_cache_logical_memory_usage_bytes"` +} + +type ClusterUsageStats struct { + DataReductionOverallSavingRatioPpm string `json:"data_reduction.overall.saving_ratio_ppm"` + StorageReservedFreeBytes string `json:"storage.reserved_free_bytes"` + StorageTierDasSataUsageBytes string `json:"storage_tier.das-sata.usage_bytes"` + DataReductionCompressionSavedBytes string `json:"data_reduction.compression.saved_bytes"` + DataReductionSavingRatioPpm string `json:"data_reduction.saving_ratio_ppm"` + DataReductionErasureCodingPostReductionBytes string `json:"data_reduction.erasure_coding.post_reduction_bytes"` + StorageTierSsdPinnedUsageBytes string `json:"storage_tier.ssd.pinned_usage_bytes"` + StorageReservedUsageBytes string `json:"storage.reserved_usage_bytes"` + DataReductionErasureCodingSavingRatioPpm string `json:"data_reduction.erasure_coding.saving_ratio_ppm"` + DataReductionThinProvisionSavedBytes string `json:"data_reduction.thin_provision.saved_bytes"` + StorageTierDasSataCapacityBytes string `json:"storage_tier.das-sata.capacity_bytes"` + StorageTierDasSataFreeBytes string `json:"storage_tier.das-sata.free_bytes"` + StorageUsageBytes string `json:"storage.usage_bytes"` + DataReductionErasureCodingSavedBytes string `json:"data_reduction.erasure_coding.saved_bytes"` + DataReductionCompressionPreReductionBytes string `json:"data_reduction.compression.pre_reduction_bytes"` + StorageRebuildCapacityBytes string `json:"storage.rebuild_capacity_bytes"` + StorageTierDasSataPinnedUsageBytes string `json:"storage_tier.das-sata.pinned_usage_bytes"` + DataReductionPreReductionBytes string `json:"data_reduction.pre_reduction_bytes"` + StorageTierSsdCapacityBytes string `json:"storage_tier.ssd.capacity_bytes"` + DataReductionCloneSavedBytes string `json:"data_reduction.clone.saved_bytes"` + StorageTierSsdFreeBytes string `json:"storage_tier.ssd.free_bytes"` + DataReductionDedupPreReductionBytes string `json:"data_reduction.dedup.pre_reduction_bytes"` + DataReductionErasureCodingPreReductionBytes string `json:"data_reduction.erasure_coding.pre_reduction_bytes"` + StorageCapacityBytes string `json:"storage.capacity_bytes"` + DataReductionDedupPostReductionBytes string `json:"data_reduction.dedup.post_reduction_bytes"` + DataReductionCloneSavingRatioPpm string `json:"data_reduction.clone.saving_ratio_ppm"` + StorageLogicalUsageBytes string `json:"storage.logical_usage_bytes"` + DataReductionSavedBytes string `json:"data_reduction.saved_bytes"` + StorageFreeBytes string `json:"storage.free_bytes"` + StorageTierSsdUsageBytes string `json:"storage_tier.ssd.usage_bytes"` + DataReductionCompressionPostReductionBytes string `json:"data_reduction.compression.post_reduction_bytes"` + DataReductionPostReductionBytes string `json:"data_reduction.post_reduction_bytes"` + DataReductionDedupSavedBytes string `json:"data_reduction.dedup.saved_bytes"` + DataReductionOverallSavedBytes string `json:"data_reduction.overall.saved_bytes"` + DataReductionThinProvisionPostReductionBytes string `json:"data_reduction.thin_provision.post_reduction_bytes"` + DataReductionThinProvisionSavingRatioPpm string `json:"data_reduction.thin_provision.saving_ratio_ppm"` + DataReductionCompressionSavingRatioPpm string `json:"data_reduction.compression.saving_ratio_ppm"` + DataReductionDedupSavingRatioPpm string `json:"data_reduction.dedup.saving_ratio_ppm"` + StorageTierSsdPinnedBytes string `json:"storage_tier.ssd.pinned_bytes"` + StorageReservedCapacityBytes string `json:"storage.reserved_capacity_bytes"` + DataReductionThinProvisionPreReductionBytes string `json:"data_reduction.thin_provision.pre_reduction_bytes"` +} + +type SCluster struct { + Id string `json:"id"` + UUID string `json:"uuid"` + ClusterIncarnationId int64 `json:"cluster_incarnation_id"` + ClusterUUID string `json:"cluster_uuid"` + Name string `json:"name"` + //ClusterExternalIpaddress interface{} `json:"cluster_external_ipaddress"` + //ClusterFullyQualifiedDomainName interface{} `json:"cluster_fully_qualified_domain_name"` + IsNsenabled bool `json:"is_nsenabled"` + //ClusterExternalDataServicesIpaddress interface{} `json:"cluster_external_data_services_ipaddress"` + //SegmentedIscsiDataServicesIpaddress interface{} `json:"segmented_iscsi_data_services_ipaddress"` + //ClusterMasqueradingIpaddress interface{} `json:"cluster_masquerading_ipaddress"` + //ClusterMasqueradingPort interface{} `json:"cluster_masquerading_port"` + Timezone string `json:"timezone"` + SupportVerbosityType string `json:"support_verbosity_type"` + OperationMode string `json:"operation_mode"` + Encrypted bool `json:"encrypted"` + ClusterUsageWarningAlertThresholdPct int `json:"cluster_usage_warning_alert_threshold_pct"` + ClusterUsageCriticalAlertThresholdPct int `json:"cluster_usage_critical_alert_threshold_pct"` + StorageType string `json:"storage_type"` + ClusterFunctions []string `json:"cluster_functions"` + IsLts bool `json:"is_lts"` + //IsRegisteredToPc interface{} `json:"is_registered_to_pc"` + NumNodes int `json:"num_nodes"` + BlockSerials []string `json:"block_serials"` + Version string `json:"version"` + FullVersion string `json:"full_version"` + TargetVersion string `json:"target_version"` + ExternalSubnet string `json:"external_subnet"` + InternalSubnet string `json:"internal_subnet"` + NccVersion string `json:"ncc_version"` + EnableLockDown bool `json:"enable_lock_down"` + EnablePasswordRemoteLoginToCluster bool `json:"enable_password_remote_login_to_cluster"` + FingerprintContentCachePercentage int `json:"fingerprint_content_cache_percentage"` + SsdPinningPercentageLimit int `json:"ssd_pinning_percentage_limit"` + EnableShadowClones bool `json:"enable_shadow_clones"` + //GlobalNfsWhiteList []interface{} `json:"global_nfs_white_list"` + //NameServers []string `json:"name_servers"` + //NtpServers []string `json:"ntp_servers"` + //ServiceCenters []interface{} `json:"service_centers"` + //HTTPProxies []interface{} `json:"http_proxies"` + //RackableUnits []RackableUnits `json:"rackable_units"` + //PublicKeys []interface{} `json:"public_keys"` + //SMTPServer interface{} `json:"smtp_server"` + HypervisorTypes []string `json:"hypervisor_types"` + ClusterRedundancyState ClusterRedundancyState `json:"cluster_redundancy_state"` + Multicluster bool `json:"multicluster"` + Cloudcluster bool `json:"cloudcluster"` + HasSelfEncryptingDrive bool `json:"has_self_encrypting_drive"` + IsUpgradeInProgress bool `json:"is_upgrade_in_progress"` + SecurityComplianceConfig SecurityComplianceConfig `json:"security_compliance_config"` + HypervisorSecurityComplianceConfig HypervisorSecurityComplianceConfig `json:"hypervisor_security_compliance_config"` + HypervisorLldpConfig HypervisorLldpConfig `json:"hypervisor_lldp_config"` + ClusterArch string `json:"cluster_arch"` + //IscsiConfig interface{} `json:"iscsi_config"` + //Domain interface{} `json:"domain"` + NosClusterAndHostsDomainJoined bool `json:"nos_cluster_and_hosts_domain_joined"` + AllHypervNodesInFailoverCluster bool `json:"all_hyperv_nodes_in_failover_cluster"` + //Credential interface{} `json:"credential"` + Stats ClusterStats `json:"stats"` + UsageStats ClusterUsageStats `json:"usage_stats"` + EnforceRackableUnitAwarePlacement bool `json:"enforce_rackable_unit_aware_placement"` + DisableDegradedNodeMonitoring bool `json:"disable_degraded_node_monitoring"` + CommonCriteriaMode bool `json:"common_criteria_mode"` + EnableOnDiskDedup bool `json:"enable_on_disk_dedup"` + //ManagementServers interface{} `json:"management_servers"` + FaultToleranceDomainType string `json:"fault_tolerance_domain_type"` + //ThresholdForStorageThinProvision interface{} `json:"threshold_for_storage_thin_provision"` +} + +func (self *SRegion) GetClusters() ([]SCluster, error) { + clusters := []SCluster{} + err := self.listAll("clusters", nil, &clusters) + if err != nil { + return nil, err + } + return clusters, nil +} + +func (self *SRegion) GetCluster(id string) (*SCluster, error) { + cluster := &SCluster{} + return cluster, self.cli.get("clusters", id, nil, cluster) +} diff --git a/pkg/multicloud/nutanix/disk.go b/pkg/multicloud/nutanix/disk.go new file mode 100644 index 0000000000..4a2b5e75d5 --- /dev/null +++ b/pkg/multicloud/nutanix/disk.go @@ -0,0 +1,312 @@ +// 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 nutanix + +import ( + "context" + "net/url" + "strings" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type DiskStats struct { + HypervisorAvgIoLatencyUsecs string `json:"hypervisor_avg_io_latency_usecs"` + HypervisorWriteIoBandwidthKBps string `json:"hypervisor_write_io_bandwidth_kBps"` + ControllerRandomOpsPpm string `json:"controller.random_ops_ppm"` + ControllerStorageTierSsdUsageBytes string `json:"controller.storage_tier.ssd.usage_bytes"` + ReadIoPpm string `json:"read_io_ppm"` + ControllerFrontendReadLatencyHistogram1000Us string `json:"controller.frontend_read_latency_histogram_1000us"` + ControllerNumIops string `json:"controller_num_iops"` + ControllerFrontendWriteOps string `json:"controller.frontend_write_ops"` + ControllerFrontendWriteLatencyHistogram10000Us string `json:"controller.frontend_write_latency_histogram_10000us"` + ControllerReadSizeHistogram1024KB string `json:"controller.read_size_histogram_1024kB"` + TotalReadIoTimeUsecs string `json:"total_read_io_time_usecs"` + ControllerTotalReadIoTimeUsecs string `json:"controller_total_read_io_time_usecs"` + ControllerWss3600SWriteMB string `json:"controller.wss_3600s_write_MB"` + ControllerFrontendReadLatencyHistogram50000Us string `json:"controller.frontend_read_latency_histogram_50000us"` + ControllerFrontendReadLatencyHistogram2000Us string `json:"controller.frontend_read_latency_histogram_2000us"` + ControllerNumWriteIo string `json:"controller_num_write_io"` + ControllerReadSourceCacheSsdBytes string `json:"controller.read_source_cache_ssd_bytes"` + ControllerReadSourceOplogBytes string `json:"controller.read_source_oplog_bytes"` + ControllerReadSourceCacheDramBytes string `json:"controller.read_source_cache_dram_bytes"` + ControllerRandomReadOps string `json:"controller.random_read_ops"` + ControllerTotalIoTimeUsecs string `json:"controller_total_io_time_usecs"` + ControllerNumSeqIo string `json:"controller_num_seq_io"` + ControllerTotalIoSizeKbytes string `json:"controller_total_io_size_kbytes"` + ControllerWss120SWriteMB string `json:"controller.wss_120s_write_MB"` + ControllerReadSourceBlockStoreBytes string `json:"controller.read_source_block_store_bytes"` + ControllerNumIo string `json:"controller_num_io"` + ControllerReadSourceEstoreZeroBytes string `json:"controller.read_source_estore_zero_bytes"` + ControllerNumRandomIo string `json:"controller_num_random_io"` + HypervisorNumReadIo string `json:"hypervisor_num_read_io"` + HypervisorTotalReadIoTimeUsecs string `json:"hypervisor_total_read_io_time_usecs"` + NumIo string `json:"num_io"` + HypervisorNumWriteIo string `json:"hypervisor_num_write_io"` + ControllerWriteSizeHistogram32KB string `json:"controller.write_size_histogram_32kB"` + ControllerFrontendReadLatencyHistogram20000Us string `json:"controller.frontend_read_latency_histogram_20000us"` + ControllerReadSizeHistogram32KB string `json:"controller.read_size_histogram_32kB"` + HypervisorNumWriteIops string `json:"hypervisor_num_write_iops"` + AvgIoLatencyUsecs string `json:"avg_io_latency_usecs"` + ControllerWriteIoPpm string `json:"controller_write_io_ppm"` + ControllerReadSourceEstoreSsdBytes string `json:"controller.read_source_estore_ssd_bytes"` + HypervisorTotalReadIoSizeKbytes string `json:"hypervisor_total_read_io_size_kbytes"` + ControllerNumWriteIops string `json:"controller_num_write_iops"` + TotalIoTimeUsecs string `json:"total_io_time_usecs"` + ControllerWss3600SReadMB string `json:"controller.wss_3600s_read_MB"` + ControllerSummaryReadSourceSsdBytesPerSec string `json:"controller.summary_read_source_ssd_bytes_per_sec"` + ControllerWriteSizeHistogram16KB string `json:"controller.write_size_histogram_16kB"` + TotalTransformedUsageBytes string `json:"total_transformed_usage_bytes"` + AvgWriteIoLatencyUsecs string `json:"avg_write_io_latency_usecs"` + ControllerCseTarget90PercentWriteMB string `json:"controller.cse_target_90_percent_write_MB"` + NumReadIo string `json:"num_read_io"` + HypervisorReadIoBandwidthKBps string `json:"hypervisor_read_io_bandwidth_kBps"` + HypervisorTotalIoTimeUsecs string `json:"hypervisor_total_io_time_usecs"` + NumRandomIo string `json:"num_random_io"` + ControllerWriteDestEstoreBytes string `json:"controller.write_dest_estore_bytes"` + ControllerFrontendWriteLatencyHistogram5000Us string `json:"controller.frontend_write_latency_histogram_5000us"` + ControllerStorageTierDasSataPinnedUsageBytes string `json:"controller.storage_tier.das-sata.pinned_usage_bytes"` + NumWriteIo string `json:"num_write_io"` + ControllerFrontendWriteLatencyHistogram2000Us string `json:"controller.frontend_write_latency_histogram_2000us"` + ControllerRandomWriteOpsPerSec string `json:"controller.random_write_ops_per_sec"` + ControllerFrontendWriteLatencyHistogram20000Us string `json:"controller.frontend_write_latency_histogram_20000us"` + IoBandwidthKBps string `json:"io_bandwidth_kBps"` + ControllerWriteSizeHistogram512KB string `json:"controller.write_size_histogram_512kB"` + ControllerReadSizeHistogram16KB string `json:"controller.read_size_histogram_16kB"` + WriteIoPpm string `json:"write_io_ppm"` + ControllerAvgWriteIoLatencyUsecs string `json:"controller_avg_write_io_latency_usecs"` + ControllerFrontendReadLatencyHistogram100000Us string `json:"controller.frontend_read_latency_histogram_100000us"` + NumReadIops string `json:"num_read_iops"` + ControllerSummaryReadSourceHddBytesPerSec string `json:"controller.summary_read_source_hdd_bytes_per_sec"` + ControllerReadSourceExtentCacheBytes string `json:"controller.read_source_extent_cache_bytes"` + TimespanUsecs string `json:"timespan_usecs"` + ControllerNumReadIops string `json:"controller_num_read_iops"` + ControllerFrontendReadLatencyHistogram10000Us string `json:"controller.frontend_read_latency_histogram_10000us"` + ControllerWriteSizeHistogram64KB string `json:"controller.write_size_histogram_64kB"` + ControllerFrontendWriteLatencyHistogram0Us string `json:"controller.frontend_write_latency_histogram_0us"` + ControllerFrontendWriteLatencyHistogram100000Us string `json:"controller.frontend_write_latency_histogram_100000us"` + HypervisorNumIo string `json:"hypervisor_num_io"` + ControllerTotalTransformedUsageBytes string `json:"controller_total_transformed_usage_bytes"` + AvgReadIoLatencyUsecs string `json:"avg_read_io_latency_usecs"` + ControllerTotalReadIoSizeKbytes string `json:"controller_total_read_io_size_kbytes"` + ControllerReadIoPpm string `json:"controller_read_io_ppm"` + ControllerFrontendOps string `json:"controller.frontend_ops"` + ControllerWss120SReadMB string `json:"controller.wss_120s_read_MB"` + ControllerReadSizeHistogram512KB string `json:"controller.read_size_histogram_512kB"` + HypervisorAvgReadIoLatencyUsecs string `json:"hypervisor_avg_read_io_latency_usecs"` + ControllerWriteSizeHistogram1024KB string `json:"controller.write_size_histogram_1024kB"` + ControllerWriteDestBlockStoreBytes string `json:"controller.write_dest_block_store_bytes"` + ControllerReadSizeHistogram4KB string `json:"controller.read_size_histogram_4kB"` + NumWriteIops string `json:"num_write_iops"` + ControllerRandomOpsPerSec string `json:"controller.random_ops_per_sec"` + NumIops string `json:"num_iops"` + ControllerStorageTierCloudPinnedUsageBytes string `json:"controller.storage_tier.cloud.pinned_usage_bytes"` + ControllerAvgIoLatencyUsecs string `json:"controller_avg_io_latency_usecs"` + ControllerReadSizeHistogram8KB string `json:"controller.read_size_histogram_8kB"` + ControllerNumReadIo string `json:"controller_num_read_io"` + ControllerSeqIoPpm string `json:"controller_seq_io_ppm"` + ControllerReadIoBandwidthKBps string `json:"controller_read_io_bandwidth_kBps"` + ControllerIoBandwidthKBps string `json:"controller_io_bandwidth_kBps"` + ControllerReadSizeHistogram0KB string `json:"controller.read_size_histogram_0kB"` + ControllerRandomOps string `json:"controller.random_ops"` + HypervisorTimespanUsecs string `json:"hypervisor_timespan_usecs"` + TotalReadIoSizeKbytes string `json:"total_read_io_size_kbytes"` + HypervisorTotalIoSizeKbytes string `json:"hypervisor_total_io_size_kbytes"` + ControllerFrontendOpsPerSec string `json:"controller.frontend_ops_per_sec"` + ControllerWriteDestOplogBytes string `json:"controller.write_dest_oplog_bytes"` + ControllerFrontendWriteLatencyHistogram1000Us string `json:"controller.frontend_write_latency_histogram_1000us"` + HypervisorNumReadIops string `json:"hypervisor_num_read_iops"` + ControllerSummaryReadSourceCacheBytesPerSec string `json:"controller.summary_read_source_cache_bytes_per_sec"` + ControllerWriteIoBandwidthKBps string `json:"controller_write_io_bandwidth_kBps"` + ControllerUserBytes string `json:"controller_user_bytes"` + HypervisorAvgWriteIoLatencyUsecs string `json:"hypervisor_avg_write_io_latency_usecs"` + ControllerStorageTierSsdPinnedUsageBytes string `json:"controller.storage_tier.ssd.pinned_usage_bytes"` + ReadIoBandwidthKBps string `json:"read_io_bandwidth_kBps"` + ControllerFrontendReadOps string `json:"controller.frontend_read_ops"` + HypervisorNumIops string `json:"hypervisor_num_iops"` + HypervisorIoBandwidthKBps string `json:"hypervisor_io_bandwidth_kBps"` + ControllerWss120SUnionMB string `json:"controller.wss_120s_union_MB"` + ControllerReadSourceEstoreHddBytes string `json:"controller.read_source_estore_hdd_bytes"` + ControllerRandomIoPpm string `json:"controller_random_io_ppm"` + ControllerCseTarget90PercentReadMB string `json:"controller.cse_target_90_percent_read_MB"` + ControllerStorageTierDasSataUsageBytes string `json:"controller.storage_tier.das-sata.usage_bytes"` + ControllerFrontendReadLatencyHistogram5000Us string `json:"controller.frontend_read_latency_histogram_5000us"` + ControllerAvgReadIoSizeKbytes string `json:"controller_avg_read_io_size_kbytes"` + WriteIoBandwidthKBps string `json:"write_io_bandwidth_kBps"` + ControllerRandomReadOpsPerSec string `json:"controller.random_read_ops_per_sec"` + ControllerReadSizeHistogram64KB string `json:"controller.read_size_histogram_64kB"` + ControllerWss3600SUnionMB string `json:"controller.wss_3600s_union_MB"` + RandomIoPpm string `json:"random_io_ppm"` + TotalUntransformedUsageBytes string `json:"total_untransformed_usage_bytes"` + ControllerFrontendReadLatencyHistogram0Us string `json:"controller.frontend_read_latency_histogram_0us"` + ControllerRandomWriteOps string `json:"controller.random_write_ops"` + ControllerAvgWriteIoSizeKbytes string `json:"controller_avg_write_io_size_kbytes"` + ControllerAvgReadIoLatencyUsecs string `json:"controller_avg_read_io_latency_usecs"` + TotalIoSizeKbytes string `json:"total_io_size_kbytes"` + ControllerStorageTierCloudUsageBytes string `json:"controller.storage_tier.cloud.usage_bytes"` + ControllerFrontendWriteLatencyHistogram50000Us string `json:"controller.frontend_write_latency_histogram_50000us"` + ControllerWriteSizeHistogram8KB string `json:"controller.write_size_histogram_8kB"` + ControllerTimespanUsecs string `json:"controller_timespan_usecs"` + NumSeqIo string `json:"num_seq_io"` + ControllerWriteSizeHistogram4KB string `json:"controller.write_size_histogram_4kB"` + SeqIoPpm string `json:"seq_io_ppm"` + ControllerWriteSizeHistogram0KB string `json:"controller.write_size_histogram_0kB"` +} + +type SDisk struct { + multicloud.STagBase + multicloud.SDisk + + storage *SStorage + + VirtualDiskID string `json:"virtual_disk_id"` + UUID string `json:"uuid"` + DeviceUUID string `json:"device_uuid"` + NutanixNfsfilePath string `json:"nutanix_nfsfile_path"` + DiskAddress string `json:"disk_address"` + AttachedVMID string `json:"attached_vm_id"` + AttachedVMUUID string `json:"attached_vm_uuid"` + AttachedVmname string `json:"attached_vmname"` + AttachedVolumeGroupID string `json:"attached_volume_group_id"` + DiskCapacityInBytes int64 `json:"disk_capacity_in_bytes"` + ClusterUUID string `json:"cluster_uuid"` + StorageContainerID string `json:"storage_container_id"` + StorageContainerUUID string `json:"storage_container_uuid"` + FlashModeEnabled string `json:"flash_mode_enabled"` + DataSourceURL string `json:"data_source_url"` + Stats DiskStats `json:"stats"` +} + +func (self *SDisk) GetName() string { + return self.DiskAddress +} + +func (self *SDisk) GetId() string { + return self.UUID +} + +func (self *SDisk) GetGlobalId() string { + return self.UUID +} + +func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SDisk) Delete(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SDisk) GetAccessPath() string { + return self.NutanixNfsfilePath +} + +func (self *SDisk) GetCacheMode() string { + return "none" +} + +func (self *SDisk) GetFsFormat() string { + return "" +} + +func (self *SDisk) GetIsNonPersistent() bool { + return false +} + +func (self *SDisk) GetDriver() string { + if info := strings.Split(self.DiskAddress, "."); len(info) > 0 { + return info[0] + } + return "scsi" +} + +func (self *SDisk) GetDiskType() string { + if strings.HasSuffix(self.DiskAddress, ".0") { + return api.DISK_TYPE_SYS + } + return api.DISK_TYPE_DATA +} + +func (self *SDisk) GetDiskFormat() string { + return "raw" +} + +func (self *SDisk) GetDiskSizeMB() int { + return int(self.DiskCapacityInBytes / 1024 / 1024) +} + +func (self *SDisk) GetIsAutoDelete() bool { + return true +} + +func (self *SDisk) GetMountpoint() string { + return "" +} + +func (self *SDisk) GetStatus() string { + return api.DISK_READY +} + +func (self *SDisk) Rebuild(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SDisk) GetTemplateId() string { + return "" +} + +func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return self.storage, nil +} + +func (self *SDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SRegion) GetDisks(storageId, vmId string) ([]SDisk, error) { + disks := []SDisk{} + params := url.Values{} + filter := []string{} + if len(storageId) > 0 { + filter = append(filter, "container_uuid=="+storageId) + } + if len(vmId) > 0 { + filter = append(filter, "vm_uuid=="+vmId) + filter = append(filter, "attach_vm_id=="+vmId) + } + if len(filter) > 0 { + params.Set("filter_criteria", strings.Join(filter, ",")) + } + return disks, self.listAll("virtual_disks", params, &disks) +} + +func (self *SRegion) GetDisk(id string) (*SDisk, error) { + disk := &SDisk{} + return disk, self.get("virtual_disks", id, url.Values{}, disk) +} diff --git a/pkg/multicloud/nutanix/doc.go b/pkg/multicloud/nutanix/doc.go new file mode 100644 index 0000000000..6cbe840ab5 --- /dev/null +++ b/pkg/multicloud/nutanix/doc.go @@ -0,0 +1 @@ +package nutanix // import "yunion.io/x/onecloud/pkg/multicloud/nutanix" diff --git a/pkg/multicloud/nutanix/host.go b/pkg/multicloud/nutanix/host.go new file mode 100644 index 0000000000..fd60294f5e --- /dev/null +++ b/pkg/multicloud/nutanix/host.go @@ -0,0 +1,414 @@ +// 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 nutanix + +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" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SHost struct { + multicloud.STagBase + multicloud.SHostBase + firstHost bool + + zone *SZone + + ServiceVmid string `json:"service_vmid"` + UUID string `json:"uuid"` + DiskHardwareConfigs DiskHardwareConfigs `json:"disk_hardware_configs"` + Name string `json:"name"` + ServiceVmexternalIP string `json:"service_vmexternal_ip"` + ServiceVmnatIP string `json:"service_vmnat_ip"` + ServiceVmnatPort string `json:"service_vmnat_port"` + OplogDiskPct float64 `json:"oplog_disk_pct"` + OplogDiskSize int64 `json:"oplog_disk_size"` + HypervisorKey string `json:"hypervisor_key"` + HypervisorAddress string `json:"hypervisor_address"` + HypervisorUsername string `json:"hypervisor_username"` + HypervisorPassword string `json:"hypervisor_password"` + BackplaneIP string `json:"backplane_ip"` + ControllerVMBackplaneIP string `json:"controller_vm_backplane_ip"` + RdmaBackplaneIps string `json:"rdma_backplane_ips"` + ManagementServerName string `json:"management_server_name"` + IpmiAddress string `json:"ipmi_address"` + IpmiUsername string `json:"ipmi_username"` + IpmiPassword string `json:"ipmi_password"` + Monitored bool `json:"monitored"` + Position Position `json:"position"` + Serial string `json:"serial"` + BlockSerial string `json:"block_serial"` + BlockModel string `json:"block_model"` + BlockModelName string `json:"block_model_name"` + BlockLocation string `json:"block_location"` + HostMaintenanceModeReason string `json:"host_maintenance_mode_reason"` + HypervisorState string `json:"hypervisor_state"` + AcropolisConnectionState string `json:"acropolis_connection_state"` + MetadataStoreStatus string `json:"metadata_store_status"` + MetadataStoreStatusMessage string `json:"metadata_store_status_message"` + State string `json:"state"` + DynamicRingChangingNode string `json:"dynamic_ring_changing_node"` + RemovalStatus []string `json:"removal_status"` + VzoneName string `json:"vzone_name"` + CPUModel string `json:"cpu_model"` + NumCPUCores int `json:"num_cpu_cores"` + NumCPUThreads int `json:"num_cpu_threads"` + NumCPUSockets int `json:"num_cpu_sockets"` + CPUFrequencyInHz int64 `json:"cpu_frequency_in_hz"` + CPUCapacityInHz int64 `json:"cpu_capacity_in_hz"` + MemoryCapacityInBytes int64 `json:"memory_capacity_in_bytes"` + HypervisorFullName string `json:"hypervisor_full_name"` + HypervisorType string `json:"hypervisor_type"` + NumVms int `json:"num_vms"` + BootTimeInUsecs int64 `json:"boot_time_in_usecs"` + IsDegraded bool `json:"is_degraded"` + IsSecureBooted bool `json:"is_secure_booted"` + IsHardwareVirtualized bool `json:"is_hardware_virtualized"` + FailoverClusterFqdn string `json:"failover_cluster_fqdn"` + FailoverClusterNodeState string `json:"failover_cluster_node_state"` + RebootPending bool `json:"reboot_pending"` + DefaultVMLocation string `json:"default_vm_location"` + DefaultVMStorageContainerID string `json:"default_vm_storage_container_id"` + DefaultVMStorageContainerUUID string `json:"default_vm_storage_container_uuid"` + DefaultVhdLocation string `json:"default_vhd_location"` + DefaultVhdStorageContainerID string `json:"default_vhd_storage_container_id"` + DefaultVhdStorageContainerUUID string `json:"default_vhd_storage_container_uuid"` + BiosVersion string `json:"bios_version"` + BiosModel string `json:"bios_model"` + BmcVersion string `json:"bmc_version"` + BmcModel string `json:"bmc_model"` + HbaFirmwaresList string `json:"hba_firmwares_list"` + ClusterUUID string `json:"cluster_uuid"` + Stats Stats `json:"stats"` + UsageStats UsageStats `json:"usage_stats"` + HasCsr bool `json:"has_csr"` + HostNicIds []string `json:"host_nic_ids"` + HostGpus string `json:"host_gpus"` + GpuDriverVersion string `json:"gpu_driver_version"` + HostType string `json:"host_type"` + KeyManagementDeviceToCertificateStatus KeyManagementDeviceToCertificateStatus `json:"key_management_device_to_certificate_status"` + HostInMaintenanceMode string `json:"host_in_maintenance_mode"` +} + +type Num1 struct { + SerialNumber string `json:"serial_number"` + DiskID string `json:"disk_id"` + DiskUUID string `json:"disk_uuid"` + Location int `json:"location"` + Bad bool `json:"bad"` + Mounted bool `json:"mounted"` + MountPath string `json:"mount_path"` + Model string `json:"model"` + Vendor string `json:"vendor"` + BootDisk bool `json:"boot_disk"` + OnlyBootDisk bool `json:"only_boot_disk"` + UnderDiagnosis bool `json:"under_diagnosis"` + BackgroundOperation string `json:"background_operation"` + CurrentFirmwareVersion string `json:"current_firmware_version"` + TargetFirmwareVersion string `json:"target_firmware_version"` + CanAddAsNewDisk bool `json:"can_add_as_new_disk"` + CanAddAsOldDisk bool `json:"can_add_as_old_disk"` +} +type Num2 struct { + SerialNumber string `json:"serial_number"` + DiskID string `json:"disk_id"` + DiskUUID string `json:"disk_uuid"` + Location int `json:"location"` + Bad bool `json:"bad"` + Mounted bool `json:"mounted"` + MountPath string `json:"mount_path"` + Model string `json:"model"` + Vendor string `json:"vendor"` + BootDisk bool `json:"boot_disk"` + OnlyBootDisk bool `json:"only_boot_disk"` + UnderDiagnosis bool `json:"under_diagnosis"` + BackgroundOperation string `json:"background_operation"` + CurrentFirmwareVersion string `json:"current_firmware_version"` + TargetFirmwareVersion string `json:"target_firmware_version"` + CanAddAsNewDisk bool `json:"can_add_as_new_disk"` + CanAddAsOldDisk bool `json:"can_add_as_old_disk"` +} +type DiskHardwareConfigs struct { + Num1 Num1 `json:"1"` + Num2 Num2 `json:"2"` +} +type Position struct { + Ordinal int `json:"ordinal"` + Name string `json:"name"` + PhysicalPosition string `json:"physical_position"` +} +type Stats struct { + HypervisorAvgIoLatencyUsecs string `json:"hypervisor_avg_io_latency_usecs"` + NumReadIops string `json:"num_read_iops"` + HypervisorWriteIoBandwidthKBps string `json:"hypervisor_write_io_bandwidth_kBps"` + TimespanUsecs string `json:"timespan_usecs"` + ControllerNumReadIops string `json:"controller_num_read_iops"` + ReadIoPpm string `json:"read_io_ppm"` + ControllerNumIops string `json:"controller_num_iops"` + TotalReadIoTimeUsecs string `json:"total_read_io_time_usecs"` + ControllerTotalReadIoTimeUsecs string `json:"controller_total_read_io_time_usecs"` + HypervisorNumIo string `json:"hypervisor_num_io"` + ControllerTotalTransformedUsageBytes string `json:"controller_total_transformed_usage_bytes"` + HypervisorCPUUsagePpm string `json:"hypervisor_cpu_usage_ppm"` + ControllerNumWriteIo string `json:"controller_num_write_io"` + AvgReadIoLatencyUsecs string `json:"avg_read_io_latency_usecs"` + ContentCacheLogicalSsdUsageBytes string `json:"content_cache_logical_ssd_usage_bytes"` + ControllerTotalIoTimeUsecs string `json:"controller_total_io_time_usecs"` + ControllerTotalReadIoSizeKbytes string `json:"controller_total_read_io_size_kbytes"` + ControllerNumSeqIo string `json:"controller_num_seq_io"` + ControllerReadIoPpm string `json:"controller_read_io_ppm"` + ContentCacheNumLookups string `json:"content_cache_num_lookups"` + ControllerTotalIoSizeKbytes string `json:"controller_total_io_size_kbytes"` + ContentCacheHitPpm string `json:"content_cache_hit_ppm"` + ControllerNumIo string `json:"controller_num_io"` + HypervisorAvgReadIoLatencyUsecs string `json:"hypervisor_avg_read_io_latency_usecs"` + ContentCacheNumDedupRefCountPph string `json:"content_cache_num_dedup_ref_count_pph"` + NumWriteIops string `json:"num_write_iops"` + ControllerNumRandomIo string `json:"controller_num_random_io"` + NumIops string `json:"num_iops"` + HypervisorNumReadIo string `json:"hypervisor_num_read_io"` + HypervisorTotalReadIoTimeUsecs string `json:"hypervisor_total_read_io_time_usecs"` + ControllerAvgIoLatencyUsecs string `json:"controller_avg_io_latency_usecs"` + NumIo string `json:"num_io"` + ControllerNumReadIo string `json:"controller_num_read_io"` + HypervisorNumWriteIo string `json:"hypervisor_num_write_io"` + ControllerSeqIoPpm string `json:"controller_seq_io_ppm"` + ControllerReadIoBandwidthKBps string `json:"controller_read_io_bandwidth_kBps"` + ControllerIoBandwidthKBps string `json:"controller_io_bandwidth_kBps"` + HypervisorNumReceivedBytes string `json:"hypervisor_num_received_bytes"` + HypervisorTimespanUsecs string `json:"hypervisor_timespan_usecs"` + HypervisorNumWriteIops string `json:"hypervisor_num_write_iops"` + TotalReadIoSizeKbytes string `json:"total_read_io_size_kbytes"` + HypervisorTotalIoSizeKbytes string `json:"hypervisor_total_io_size_kbytes"` + AvgIoLatencyUsecs string `json:"avg_io_latency_usecs"` + HypervisorNumReadIops string `json:"hypervisor_num_read_iops"` + ContentCacheSavedSsdUsageBytes string `json:"content_cache_saved_ssd_usage_bytes"` + ControllerWriteIoBandwidthKBps string `json:"controller_write_io_bandwidth_kBps"` + ControllerWriteIoPpm string `json:"controller_write_io_ppm"` + HypervisorAvgWriteIoLatencyUsecs string `json:"hypervisor_avg_write_io_latency_usecs"` + HypervisorNumTransmittedBytes string `json:"hypervisor_num_transmitted_bytes"` + HypervisorTotalReadIoSizeKbytes string `json:"hypervisor_total_read_io_size_kbytes"` + ReadIoBandwidthKBps string `json:"read_io_bandwidth_kBps"` + HypervisorMemoryUsagePpm string `json:"hypervisor_memory_usage_ppm"` + HypervisorNumIops string `json:"hypervisor_num_iops"` + HypervisorIoBandwidthKBps string `json:"hypervisor_io_bandwidth_kBps"` + ControllerNumWriteIops string `json:"controller_num_write_iops"` + TotalIoTimeUsecs string `json:"total_io_time_usecs"` + ContentCachePhysicalSsdUsageBytes string `json:"content_cache_physical_ssd_usage_bytes"` + ControllerRandomIoPpm string `json:"controller_random_io_ppm"` + ControllerAvgReadIoSizeKbytes string `json:"controller_avg_read_io_size_kbytes"` + TotalTransformedUsageBytes string `json:"total_transformed_usage_bytes"` + AvgWriteIoLatencyUsecs string `json:"avg_write_io_latency_usecs"` + NumReadIo string `json:"num_read_io"` + WriteIoBandwidthKBps string `json:"write_io_bandwidth_kBps"` + HypervisorReadIoBandwidthKBps string `json:"hypervisor_read_io_bandwidth_kBps"` + RandomIoPpm string `json:"random_io_ppm"` + TotalUntransformedUsageBytes string `json:"total_untransformed_usage_bytes"` + HypervisorTotalIoTimeUsecs string `json:"hypervisor_total_io_time_usecs"` + NumRandomIo string `json:"num_random_io"` + ControllerAvgWriteIoSizeKbytes string `json:"controller_avg_write_io_size_kbytes"` + ControllerAvgReadIoLatencyUsecs string `json:"controller_avg_read_io_latency_usecs"` + NumWriteIo string `json:"num_write_io"` + TotalIoSizeKbytes string `json:"total_io_size_kbytes"` + IoBandwidthKBps string `json:"io_bandwidth_kBps"` + ContentCachePhysicalMemoryUsageBytes string `json:"content_cache_physical_memory_usage_bytes"` + ControllerTimespanUsecs string `json:"controller_timespan_usecs"` + NumSeqIo string `json:"num_seq_io"` + ContentCacheSavedMemoryUsageBytes string `json:"content_cache_saved_memory_usage_bytes"` + SeqIoPpm string `json:"seq_io_ppm"` + WriteIoPpm string `json:"write_io_ppm"` + ControllerAvgWriteIoLatencyUsecs string `json:"controller_avg_write_io_latency_usecs"` + ContentCacheLogicalMemoryUsageBytes string `json:"content_cache_logical_memory_usage_bytes"` +} +type UsageStats struct { + StorageTierDasSataUsageBytes string `json:"storage_tier.das-sata.usage_bytes"` + StorageCapacityBytes string `json:"storage.capacity_bytes"` + StorageLogicalUsageBytes string `json:"storage.logical_usage_bytes"` + StorageTierDasSataCapacityBytes string `json:"storage_tier.das-sata.capacity_bytes"` + StorageFreeBytes string `json:"storage.free_bytes"` + StorageTierSsdUsageBytes string `json:"storage_tier.ssd.usage_bytes"` + StorageTierSsdCapacityBytes string `json:"storage_tier.ssd.capacity_bytes"` + StorageTierDasSataFreeBytes string `json:"storage_tier.das-sata.free_bytes"` + StorageUsageBytes string `json:"storage.usage_bytes"` + StorageTierSsdFreeBytes string `json:"storage_tier.ssd.free_bytes"` +} +type KeyManagementDeviceToCertificateStatus struct { +} + +func (self *SRegion) GetHosts() ([]SHost, error) { + hosts := []SHost{} + return hosts, self.listAll("hosts", nil, &hosts) +} + +func (self *SRegion) GetHost(id string) (*SHost, error) { + host := &SHost{} + return host, self.cli.get("hosts", id, nil, host) +} + +func (self *SHost) GetName() string { + return self.Name +} + +func (self *SHost) GetId() string { + return self.UUID +} + +func (self *SHost) GetGlobalId() string { + return self.UUID +} + +func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SHost) GetAccessIp() string { + return self.HypervisorAddress +} + +func (self *SHost) GetAccessMac() string { + return "" +} + +func (self *SHost) GetCpuCmtbound() float32 { + return 16.0 +} + +func (self *SHost) GetMemCmtbound() float32 { + return 1.5 +} + +func (self *SHost) GetCpuCount() int { + return self.NumCPUCores * self.NumCPUSockets +} + +func (self *SHost) GetNodeCount() int8 { + return int8(self.NumCPUSockets) +} + +func (self *SHost) GetEnabled() bool { + return true +} + +func (self *SHost) GetCpuDesc() string { + return self.CPUModel +} + +func (self *SHost) GetCpuMhz() int { + return int(self.CPUCapacityInHz / 1000 / 1000) +} + +func (self *SHost) GetMemSizeMB() int { + return int(self.MemoryCapacityInBytes / 1024 / 1024) +} + +func (self *SHost) GetStorageSizeMB() int { + sizeBytes, _ := strconv.Atoi(self.UsageStats.StorageCapacityBytes) + return sizeBytes / 1024 / 1024 +} + +func (self *SHost) GetStorageType() string { + return api.DISK_TYPE_HYBRID +} + +func (self *SHost) GetHostType() string { + return api.HOST_TYPE_NUTANIX +} + +func (self *SHost) GetHostStatus() string { + return api.HOST_ONLINE +} + +func (self *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { + return []cloudprovider.ICloudHostNetInterface{}, nil +} + +func (self *SHost) GetIsMaintenance() bool { + return false +} + +func (self *SHost) GetVersion() string { + return "" +} + +func (self *SHost) GetStatus() string { + return api.HOST_STATUS_RUNNING +} + +func (self *SHost) GetSN() string { + return "" +} + +func (self *SHost) GetSysInfo() jsonutils.JSONObject { + info := jsonutils.NewDict() + info.Add(jsonutils.NewString(CLOUD_PROVIDER_NUTANIX), "manufacture") + return info +} + +func (self *SHost) IsEmulated() bool { + return false +} + +func (self *SHost) Refresh() error { + host, err := self.zone.region.GetHost(self.UUID) + if err != nil { + return err + } + return jsonutils.Update(self, host) +} + +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) GetIVMs() ([]cloudprovider.ICloudVM, error) { + vms, err := self.zone.region.GetInstances() + if err != nil { + return nil, errors.Wrapf(err, "GetInstances") + } + ret := []cloudprovider.ICloudVM{} + for i := range vms { + if vms[i].HostUUID == self.UUID || (self.firstHost && len(vms[i].HostUUID) == 0) { + vms[i].host = self + ret = append(ret, &vms[i]) + } + } + return ret, nil +} + +func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) { + vm, err := self.zone.region.GetInstance(id) + if err != nil { + return nil, errors.Wrapf(err, "GetInstance") + } + if len(vm.HostUUID) > 0 && vm.HostUUID != self.UUID { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, "vm not locate host %s, it locate host %s", self.Name, vm.HostUUID) + } + vm.host = self + return vm, nil +} + +func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/multicloud/nutanix/image.go b/pkg/multicloud/nutanix/image.go new file mode 100644 index 0000000000..6191dac18c --- /dev/null +++ b/pkg/multicloud/nutanix/image.go @@ -0,0 +1,159 @@ +// 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 nutanix + +import ( + "context" + "net/url" + "time" + + "yunion.io/x/jsonutils" + + 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/imagetools" +) + +type SImage struct { + multicloud.STagBase + multicloud.SImageBase + + cache *SStoragecache + + UUID string `json:"uuid"` + Name string `json:"name"` + Deleted bool `json:"deleted"` + StorageContainerID int `json:"storage_container_id"` + StorageContainerUUID string `json:"storage_container_uuid"` + LogicalTimestamp int `json:"logical_timestamp"` + ImageType string `json:"image_type"` + VMDiskID string `json:"vm_disk_id"` + ImageState string `json:"image_state"` + CreatedTimeInUsecs int64 `json:"created_time_in_usecs"` + UpdatedTimeInUsecs int64 `json:"updated_time_in_usecs"` + VMDiskSize int64 `json:"vm_disk_size"` +} + +func (self *SImage) GetName() string { + return self.Name +} + +func (self *SImage) GetId() string { + return self.UUID +} + +func (self *SImage) GetGlobalId() string { + return self.UUID +} + +func (self *SImage) Refresh() error { + image, err := self.cache.region.GetImage(self.GetGlobalId()) + if err != nil { + return err + } + return jsonutils.Update(self, image) +} + +func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache { + return self.cache +} + +func (self *SImage) GetImageFormat() string { + if self.ImageType == "ISO_IMAGE" { + return "iso" + } + return "raw" +} + +func (self *SImage) GetStatus() string { + switch self.ImageState { + case "ACTIVE": + return api.CACHED_IMAGE_STATUS_ACTIVE + case "INACTIVE": + return api.CACHED_IMAGE_STATUS_SAVING + } + return self.ImageState +} + +func (self *SImage) GetImageStatus() string { + switch self.ImageState { + case "ACTIVE": + return cloudprovider.IMAGE_STATUS_ACTIVE + case "INACTIVE": + return cloudprovider.IMAGE_STATUS_QUEUED + } + return cloudprovider.IMAGE_STATUS_KILLED +} + +func (self *SImage) GetImageType() cloudprovider.TImageType { + return cloudprovider.ImageTypeSystem +} + +func (self *SImage) GetCreatedAt() time.Time { + return time.Unix(self.CreatedTimeInUsecs/1000, self.CreatedTimeInUsecs%1000) +} + +func (self *SImage) Delete(ctx context.Context) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SImage) GetMinOsDiskSizeGb() int { + return int(self.VMDiskSize / 1024 / 1024 / 1024) +} + +func (self *SImage) GetSizeByte() int64 { + return self.VMDiskSize +} + +func (self *SImage) GetOsType() cloudprovider.TOsType { + return cloudprovider.TOsType(imagetools.NormalizeImageInfo(self.Name, "x86_64", "", "", "").OsType) +} + +func (self *SImage) GetOsDist() string { + return imagetools.NormalizeImageInfo(self.Name, "x86_64", "", "", "").OsDistro +} + +func (self *SImage) GetOsVersion() string { + return imagetools.NormalizeImageInfo(self.Name, "x86_64", "", "", "").OsVersion +} + +func (self *SImage) UEFI() bool { + return false +} + +func (self *SImage) GetOsArch() string { + return "x86_64" +} + +func (self *SImage) GetMinRamSizeMb() int { + return 0 +} + +func (self *SRegion) GetImages() ([]SImage, error) { + images := []SImage{} + params := url.Values{} + params.Set("include_vm_disk_sizes", "true") + params.Set("include_vm_disk_paths", "true") + return images, self.listAll("images", nil, &images) +} + +func (self *SRegion) GetImage(id string) (*SImage, error) { + image := &SImage{} + params := url.Values{} + params.Set("include_vm_disk_sizes", "true") + params.Set("include_vm_disk_paths", "true") + return image, self.get("images", id, params, image) +} diff --git a/pkg/multicloud/nutanix/instance.go b/pkg/multicloud/nutanix/instance.go new file mode 100644 index 0000000000..b49d8d21f5 --- /dev/null +++ b/pkg/multicloud/nutanix/instance.go @@ -0,0 +1,276 @@ +// 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 nutanix + +import ( + "context" + "fmt" + "net/url" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "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 Boot struct { + UefiBoot bool `json:"uefi_boot"` +} + +type VMFeatures struct { + VGACONSOLE bool `json:"VGA_CONSOLE"` + AGENTVM bool `json:"AGENT_VM"` +} + +type SInstance struct { + multicloud.STagBase + multicloud.SInstanceBase + + host *SHost + + AllowLiveMigrate bool `json:"allow_live_migrate"` + GpusAssigned bool `json:"gpus_assigned"` + Boot Boot `json:"boot"` + HaPriority int `json:"ha_priority"` + HostUUID string `json:"host_uuid"` + MemoryMb int `json:"memory_mb"` + Name string `json:"name"` + NumCoresPerVcpu int `json:"num_cores_per_vcpu"` + NumVcpus int `json:"num_vcpus"` + PowerState string `json:"power_state"` + Timezone string `json:"timezone"` + UUID string `json:"uuid"` + VMFeatures VMFeatures `json:"vm_features"` + VMLogicalTimestamp int `json:"vm_logical_timestamp"` + MachineType string `json:"machine_type"` +} + +func (self *SRegion) GetInstances() ([]SInstance, error) { + vms := []SInstance{} + params := url.Values{} + params.Set("include_vm_disk_config", "true") + params.Set("include_vm_nic_config", "true") + return vms, self.listAll("vms", params, &vms) +} + +func (self *SRegion) GetInstance(id string) (*SInstance, error) { + vm := &SInstance{} + params := url.Values{} + return vm, self.get("vms", id, params, vm) +} + +func (self *SInstance) GetName() string { + return self.Name +} + +func (self *SInstance) GetId() string { + return self.UUID +} + +func (self *SInstance) GetGlobalId() string { + return self.UUID +} + +func (self *SInstance) AssignSecurityGroup(id string) error { + return cloudprovider.ErrNotSupported +} + +func (self *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { + return cloudprovider.ErrNotSupported +} + +func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) DeleteVM(ctx context.Context) 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) DetachDisk(ctx context.Context, diskId string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) GetBios() string { + if self.Boot.UefiBoot { + return "UEFI" + } + return "BIOS" +} + +func (self *SInstance) GetBootOrder() string { + return "dcn" +} + +func (self *SInstance) GetError() error { + return nil +} + +func (self *SInstance) GetHostname() string { + return self.Name +} + +func (self *SInstance) GetHypervisor() string { + return api.HYPERVISOR_NUTANIX +} + +func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + disks, err := self.host.zone.region.GetDisks("", self.GetGlobalId()) + if err != nil { + return nil, errors.Wrapf(err, "GetInstanceDisks") + } + ret := []cloudprovider.ICloudDisk{} + for i := range disks { + storage, err := self.host.zone.GetIStorageById(disks[i].StorageContainerUUID) + if err != nil { + log.Errorf("can not found disk %s storage %s", disks[i].DiskAddress, disks[i].StorageContainerUUID) + continue + } + disks[i].storage = storage.(*SStorage) + ret = append(ret, &disks[i]) + } + return ret, nil +} + +func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SInstance) GetIHost() cloudprovider.ICloudHost { + return self.host +} + +func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) { + nics, err := self.host.zone.region.GetInstanceNics(self.GetGlobalId()) + if err != nil { + return nil, errors.Wrapf(err, "GetInstanceNics") + } + ret := []cloudprovider.ICloudNic{} + for i := range nics { + nics[i].ins = self + ret = append(ret, &nics[i]) + } + return ret, nil +} + +func (self *SInstance) GetInstanceType() string { + return fmt.Sprintf("ecs.g1.c%dm%d", self.GetVcpuCount(), self.GetVmemSizeMB()/1024) +} + +func (self *SInstance) GetMachine() string { + return self.MachineType +} + +// "UNKNOWN", "OFF", "POWERING_ON", "ON", "SHUTTING_DOWN", "POWERING_OFF", "PAUSING", "PAUSED", "SUSPENDING", "SUSPENDED", "RESUMING", "RESETTING", "MIGRATING" +func (self *SInstance) GetStatus() string { + switch strings.ToUpper(self.PowerState) { + case "OFF": + return api.VM_READY + case "POWERING_ON": + return api.VM_START_START + case "ON": + return api.VM_RUNNING + case "SHUTTING_DOWN": + return api.VM_START_STOP + case "POWERING_OFF": + return api.VM_START_STOP + case "PAUSING", "PAUSED": + return api.VM_READY + case "SUSPENDING", "SUSPENDED": + return api.VM_SUSPEND + case "RESUMING": + return api.VM_RESUMING + case "RESETTING": + return api.VM_RUNNING + case "MIGRATING": + return api.VM_MIGRATING + } + return api.VM_UNKNOWN +} + +func (self *SInstance) GetOSName() string { + return "" +} + +func (self *SInstance) GetOsType() cloudprovider.TOsType { + if strings.Contains(strings.ToLower(self.Name), "win") { + return cloudprovider.OsTypeWindows + } + return cloudprovider.OsTypeLinux +} + +func (self *SInstance) GetProjectId() string { + return "" +} + +func (self *SInstance) GetSecurityGroupIds() ([]string, error) { + return []string{}, nil +} + +func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SInstance) GetVcpuCount() int { + return self.NumVcpus * self.NumVcpus +} + +func (self *SInstance) GetVmemSizeMB() int { + return self.MemoryMb +} + +func (self *SInstance) GetVga() string { + return "std" +} + +func (self *SInstance) GetVdi() string { + return "vnc" +} + +func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { + return "", cloudprovider.ErrNotImplemented +} + +func (self *SInstance) SetSecurityGroups(secgroupIds []string) error { + return cloudprovider.ErrNotSupported +} + +func (self *SInstance) StartVM(ctx context.Context) error { + return cloudprovider.ErrNotSupported +} + +func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { + return cloudprovider.ErrNotSupported +} + +func (self *SInstance) UpdateUserData(userData string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SInstance) UpdateVM(ctx context.Context, name string) error { + return cloudprovider.ErrNotSupported +} diff --git a/pkg/multicloud/nutanix/instancenic.go b/pkg/multicloud/nutanix/instancenic.go new file mode 100644 index 0000000000..e978723d55 --- /dev/null +++ b/pkg/multicloud/nutanix/instancenic.go @@ -0,0 +1,102 @@ +// 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 nutanix + +import ( + "fmt" + "net/url" + + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SInstanceNic struct { + cloudprovider.DummyICloudNic + ins *SInstance + + MacAddress string `json:"mac_address"` + NetworkUUID string `json:"network_uuid"` + NicUUID string `json:"nic_uuid"` + Model string `json:"model"` + IPAddress string `json:"ip_address"` + IPAddresses []string `json:"ip_addresses"` + VlanMode string `json:"vlan_mode"` + IsConnected bool `json:"is_connected"` +} + +func (self *SInstanceNic) GetId() string { + return self.NicUUID +} + +func (self *SInstanceNic) GetIP() string { + return self.IPAddress +} + +func (self *SInstanceNic) GetMAC() string { + return self.MacAddress +} + +func (self *SInstanceNic) GetDriver() string { + return "virtio" +} + +func (self *SInstanceNic) GetSubAddress() ([]string, error) { + ret := []string{} + for _, addr := range self.IPAddresses { + if addr != self.IPAddress { + ret = append(ret, addr) + } + } + return ret, nil +} + +func (self *SInstanceNic) GetINetworkId() string { + if len(self.IPAddress) == 0 { + return self.NetworkUUID + } + vpc, err := self.ins.host.zone.region.GetVpc(self.NetworkUUID) + if err != nil { + return self.NetworkUUID + } + wires, err := vpc.GetIWires() + if err != nil { + return self.NetworkUUID + } + for i := range wires { + networks, err := wires[i].GetINetworks() + if err != nil { + continue + } + for j := range networks { + network := networks[j].(*SNetwork) + if network.Contains(self.IPAddress) { + return network.GetGlobalId() + } + } + } + return self.NetworkUUID +} + +func (self *SInstanceNic) AssignAddress(ipAddrs []string) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SRegion) GetInstanceNics(id string) ([]SInstanceNic, error) { + nics := []SInstanceNic{} + res := fmt.Sprintf("vms/%s/nics", id) + params := url.Values{} + params.Set("include_address_assignments", "true") + _, err := self.list(res, params, &nics) + return nics, err +} diff --git a/pkg/multicloud/nutanix/network.go b/pkg/multicloud/nutanix/network.go new file mode 100644 index 0000000000..0555d83474 --- /dev/null +++ b/pkg/multicloud/nutanix/network.go @@ -0,0 +1,120 @@ +// 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 nutanix + +import ( + "strings" + + "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/multicloud" + "yunion.io/x/onecloud/pkg/util/rbacutils" +) + +type SNetwork struct { + multicloud.SResourceBase + multicloud.STagBase + wire *SWire + + Range string +} + +func (self *SNetwork) GetName() string { + if len(self.Range) > 0 { + return self.Range + } + return self.wire.GetName() +} + +func (self *SNetwork) GetId() string { + if len(self.Range) > 0 { + return self.Range + } + return self.wire.GetId() +} + +func (self *SNetwork) GetGlobalId() string { + if len(self.Range) > 0 { + return self.Range + } + return self.wire.GetGlobalId() +} + +func (self *SNetwork) IsEmulated() bool { + return len(self.Range) == 0 +} + +func (self *SNetwork) Delete() error { + if len(self.Range) == 0 { + return nil + } + return cloudprovider.ErrNotImplemented +} + +func (self *SNetwork) GetAllocTimeoutSeconds() int { + return 120 // 2 minutes +} + +func (self *SNetwork) GetGateway() string { + return self.wire.vpc.IPConfig.DefaultGateway +} + +func (self *SNetwork) GetIWire() cloudprovider.ICloudWire { + return self.wire +} + +func (self *SNetwork) GetIpStart() string { + if info := strings.Split(self.Range, " "); len(info) == 2 { + return info[0] + } + return "0.0.0.1" +} + +func (self *SNetwork) GetIpEnd() string { + if info := strings.Split(self.Range, " "); len(info) == 2 { + return info[1] + } + return "255.255.255.254" +} + +func (self *SNetwork) Contains(_ip string) bool { + start, _ := netutils.NewIPV4Addr(self.GetIpStart()) + end, _ := netutils.NewIPV4Addr(self.GetIpEnd()) + ip, _ := netutils.NewIPV4Addr(_ip) + return netutils.NewIPV4AddrRange(start, end).Contains(ip) +} + +func (self *SNetwork) GetIpMask() int8 { + pref, _ := netutils.NewIPV4Prefix(self.wire.vpc.GetCidrBlock()) + return pref.MaskLen +} + +func (self *SNetwork) GetProjectId() string { + return "" +} + +func (self *SNetwork) GetPublicScope() rbacutils.TRbacScope { + return rbacutils.ScopeDomain +} + +func (self *SNetwork) GetServerType() string { + return api.NETWORK_TYPE_GUEST +} + +func (self *SNetwork) GetStatus() string { + return api.NETWORK_STATUS_AVAILABLE +} diff --git a/pkg/multicloud/nutanix/nutanix.go b/pkg/multicloud/nutanix/nutanix.go new file mode 100644 index 0000000000..0ace991452 --- /dev/null +++ b/pkg/multicloud/nutanix/nutanix.go @@ -0,0 +1,212 @@ +// 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 nutanix + +import ( + "context" + "fmt" + "net/http" + "net/url" + + "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/httputils" +) + +const ( + NUTANIX_VERSION_V2 = "PrismGateway/services/rest/v2.0" + NUTANIX_VERSION_V3 = "api/nutanix/v3" + + CLOUD_PROVIDER_NUTANIX = api.CLOUD_PROVIDER_NUTANIX +) + +type NutanixClientConfig struct { + cpcfg cloudprovider.ProviderConfig + username string + password string + host string + port int + debug bool +} + +func NewNutanixClientConfig(host, username, password string, port int) *NutanixClientConfig { + cfg := &NutanixClientConfig{ + host: host, + username: username, + password: password, + port: port, + } + return cfg +} + +func (cfg *NutanixClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *NutanixClientConfig { + cfg.cpcfg = cpcfg + return cfg +} + +func (cfg *NutanixClientConfig) Debug(debug bool) *NutanixClientConfig { + cfg.debug = debug + return cfg +} + +func (cfg NutanixClientConfig) Copy() NutanixClientConfig { + return cfg +} + +type SNutanixClient struct { + *NutanixClientConfig +} + +func NewNutanixClient(cfg *NutanixClientConfig) (*SNutanixClient, error) { + client := &SNutanixClient{ + NutanixClientConfig: cfg, + } + return client, client.auth() +} + +func (self *SNutanixClient) GetRegion() (*SRegion, error) { + return &SRegion{cli: self}, nil +} + +func (self *SNutanixClient) GetAccountId() string { + return self.host +} + +func (self *SNutanixClient) GetCapabilities() []string { + return []string{ + cloudprovider.CLOUD_CAPABILITY_COMPUTE + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_NETWORK + cloudprovider.READ_ONLY_SUFFIX, + } +} + +func (self *SNutanixClient) auth() error { + _, err := self.list("clusters", nil, nil) + return err +} + +func (self *SNutanixClient) getBaseDomain() string { + return fmt.Sprintf("https://%s:%d/%s", self.host, self.port, NUTANIX_VERSION_V2) +} + +func (cli *SNutanixClient) getDefaultClient() *http.Client { + client := httputils.GetDefaultClient() + proxy := func(req *http.Request) (*url.URL, error) { + req.SetBasicAuth(cli.username, cli.password) + if cli.cpcfg.ProxyFunc != nil { + cli.cpcfg.ProxyFunc(req) + } + return nil, nil + } + httputils.SetClientProxyFunc(client, proxy) + return client +} + +func (self *SNutanixClient) _list(res string, params url.Values) (jsonutils.JSONObject, error) { + url := fmt.Sprintf("%s/%s", self.getBaseDomain(), res) + if len(params) > 0 { + url = fmt.Sprintf("%s?%s", url, params.Encode()) + } + return self.jsonRequest(httputils.GET, url, nil) +} + +func (self *SNutanixClient) list(res string, params url.Values, retVal interface{}) (int, error) { + resp, err := self._list(res, params) + if err != nil { + return 0, errors.Wrapf(err, "get %s", res) + } + if retVal != nil { + err = resp.Unmarshal(retVal, "entities") + if err != nil { + return 0, errors.Wrapf(err, "resp.Unmarshal") + } + } + total, err := resp.Int("metadata", "total_entities") + if err != nil { + return 0, errors.Wrapf(err, "get metadata total_entities") + } + return int(total), nil +} + +func (self *SNutanixClient) listAll(res string, params url.Values, retVal interface{}) error { + if len(params) == 0 { + params = url.Values{} + } + entities := []jsonutils.JSONObject{} + page, count := 1, 1024 + for { + params.Set("count", fmt.Sprintf("%d", count)) + params.Set("page", fmt.Sprintf("%d", page)) + resp, err := self._list(res, params) + if err != nil { + return errors.Wrapf(err, "list %s", res) + } + _entities, err := resp.GetArray("entities") + if err != nil { + return errors.Wrapf(err, "resp get entities") + } + entities = append(entities, _entities...) + totalEntities, err := resp.Int("metadata", "total_entities") + if err != nil { + return errors.Wrapf(err, "get resp total_entities") + } + if int64(page*count) >= totalEntities { + break + } + page++ + } + return jsonutils.Update(retVal, entities) +} + +func (self *SNutanixClient) get(res string, id string, params url.Values, retVal interface{}) error { + url := fmt.Sprintf("%s/%s/%s", self.getBaseDomain(), res, id) + if len(params) > 0 { + url = fmt.Sprintf("%s?%s", url, params.Encode()) + } + resp, err := self.jsonRequest(httputils.GET, url, nil) + if err != nil { + return errors.Wrapf(err, "get %s/%s", res, id) + } + if retVal != nil { + return resp.Unmarshal(retVal) + } + return nil +} + +func (self *SNutanixClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { + subAccount := cloudprovider.SSubAccount{ + Account: self.username, + Name: self.cpcfg.Name, + HealthStatus: api.CLOUD_PROVIDER_HEALTH_NORMAL, + } + return []cloudprovider.SSubAccount{subAccount}, nil +} + +func (self *SNutanixClient) jsonRequest(method httputils.THttpMethod, url string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { + client := self.getDefaultClient() + return _jsonRequest(client, method, url, nil, body, self.debug) +} + +func _jsonRequest(cli *http.Client, method httputils.THttpMethod, url string, header http.Header, body jsonutils.JSONObject, debug bool) (jsonutils.JSONObject, error) { + _, resp, err := httputils.JSONRequest(cli, context.Background(), method, url, header, body, debug) + return resp, err +} + +func (self *SNutanixClient) GetIRegions() []cloudprovider.ICloudRegion { + region := &SRegion{cli: self} + return []cloudprovider.ICloudRegion{region} +} diff --git a/pkg/multicloud/nutanix/provider/doc.go b/pkg/multicloud/nutanix/provider/doc.go new file mode 100644 index 0000000000..4bd4a51281 --- /dev/null +++ b/pkg/multicloud/nutanix/provider/doc.go @@ -0,0 +1 @@ +package provider // import "yunion.io/x/onecloud/pkg/multicloud/nutanix/provider" diff --git a/pkg/multicloud/nutanix/provider/provider.go b/pkg/multicloud/nutanix/provider/provider.go new file mode 100644 index 0000000000..3a8e815112 --- /dev/null +++ b/pkg/multicloud/nutanix/provider/provider.go @@ -0,0 +1,201 @@ +// 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" + "fmt" + "net/url" + "strconv" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/regutils" + + 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/nutanix" +) + +type SNutanixProviderFactory struct { + cloudprovider.SPrivateCloudBaseProviderFactory +} + +func (self *SNutanixProviderFactory) GetId() string { + return nutanix.CLOUD_PROVIDER_NUTANIX +} + +func (self *SNutanixProviderFactory) GetName() string { + return nutanix.CLOUD_PROVIDER_NUTANIX +} + +func (self *SNutanixProviderFactory) ValidateChangeBandwidth(instanceId string, bandwidth int64) error { + return fmt.Errorf("Changing %s bandwidth is not supported", nutanix.CLOUD_PROVIDER_NUTANIX) +} + +func (self *SNutanixProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential) (cloudprovider.SCloudaccount, error) { + output := cloudprovider.SCloudaccount{} + if len(input.Username) == 0 { + return output, errors.Wrap(httperrors.ErrMissingParameter, "username") + } + if len(input.Password) == 0 { + return output, errors.Wrap(httperrors.ErrMissingParameter, "password") + } + if len(input.Host) == 0 { + return output, errors.Wrap(httperrors.ErrMissingParameter, "host") + } + if !regutils.MatchIPAddr(input.Host) && !regutils.MatchDomainName(input.Host) { + return output, errors.Wrap(httperrors.ErrInputParameter, "host should be ip or domain name") + } + if input.Port == 0 { + input.Port = 9440 + } + output.AccessUrl = fmt.Sprintf("https://%s:%d", input.Host, input.Port) + output.Account = input.Username + output.Secret = input.Password + return output, nil +} + +func (self *SNutanixProviderFactory) ValidateUpdateCloudaccountCredential(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential, cloudaccount string) (cloudprovider.SCloudaccount, error) { + output := cloudprovider.SCloudaccount{} + if len(input.Username) == 0 { + return output, errors.Wrap(httperrors.ErrMissingParameter, "username") + } + if len(input.Password) == 0 { + return output, errors.Wrap(httperrors.ErrMissingParameter, "password") + } + output = cloudprovider.SCloudaccount{ + Account: input.Username, + Secret: input.Password, + } + return output, nil +} + +func parseHostPort(_url string) (string, int, error) { + urlParse, err := url.Parse(_url) + if err != nil { + return "", 0, errors.Wrapf(err, "parse %s", _url) + } + port := func() int { + if len(urlParse.Port()) > 0 { + _port, _ := strconv.Atoi(urlParse.Port()) + return _port + } + return 9440 + }() + return strings.TrimSuffix(urlParse.Host, fmt.Sprintf(":%d", port)), port, nil +} + +func (self *SNutanixProviderFactory) GetProvider(cfg cloudprovider.ProviderConfig) (cloudprovider.ICloudProvider, error) { + host, port, err := parseHostPort(cfg.URL) + if err != nil { + return nil, errors.Wrapf(err, "parseHostPort") + } + + client, err := nutanix.NewNutanixClient( + nutanix.NewNutanixClientConfig( + host, cfg.Account, cfg.Secret, port, + ).CloudproviderConfig(cfg), + ) + if err != nil { + return nil, err + } + return &SNutanixProvider{ + SBaseProvider: cloudprovider.NewBaseProvider(self), + client: client, + }, nil +} + +func (self *SNutanixProviderFactory) GetClientRC(info cloudprovider.SProviderInfo) (map[string]string, error) { + host, port, err := parseHostPort(info.Url) + if err != nil { + return nil, err + } + + return map[string]string{ + "NUTANIX_HOST": host, + "NUTANIX_PORT": fmt.Sprintf("%d", port), + "NUTANIX_USERNAME": info.Account, + "NUTANIX_PASSWORD": info.Secret, + }, nil +} + +func init() { + factory := SNutanixProviderFactory{} + cloudprovider.RegisterFactory(&factory) +} + +type SNutanixProvider struct { + cloudprovider.SBaseProvider + client *nutanix.SNutanixClient +} + +func (self *SNutanixProvider) GetSysInfo() (jsonutils.JSONObject, error) { + return jsonutils.NewDict(), nil +} + +func (self *SNutanixProvider) GetVersion() string { + return nutanix.NUTANIX_VERSION_V2 +} + +func (self *SNutanixProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { + return self.client.GetSubAccounts() +} + +func (self *SNutanixProvider) GetAccountId() string { + return self.client.GetAccountId() +} + +func (self *SNutanixProvider) GetIRegions() []cloudprovider.ICloudRegion { + return self.client.GetIRegions() +} + +func (self *SNutanixProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { + regions := self.GetIRegions() + for i := range regions { + if regions[i].GetGlobalId() == id { + return regions[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SNutanixProvider) GetBalance() (float64, string, error) { + return 0.0, api.CLOUD_PROVIDER_HEALTH_NORMAL, cloudprovider.ErrNotSupported +} + +func (self *SNutanixProvider) GetIProjects() ([]cloudprovider.ICloudProject, error) { + return []cloudprovider.ICloudProject{}, nil + //return self.client.GetIProjects() +} + +func (self *SNutanixProvider) GetStorageClasses(regionId string) []string { + return nil +} + +func (self *SNutanixProvider) GetBucketCannedAcls(regionId string) []string { + return nil +} + +func (self *SNutanixProvider) GetObjectCannedAcls(regionId string) []string { + return nil +} + +func (self *SNutanixProvider) GetCapabilities() []string { + return self.client.GetCapabilities() +} diff --git a/pkg/multicloud/nutanix/region.go b/pkg/multicloud/nutanix/region.go new file mode 100644 index 0000000000..8b0e0ad5d8 --- /dev/null +++ b/pkg/multicloud/nutanix/region.go @@ -0,0 +1,198 @@ +// 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 nutanix + +import ( + "fmt" + "net/url" + + "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 SRegion struct { + multicloud.SRegion + multicloud.SNoObjectStorageRegion + multicloud.SNoLbRegion + + cli *SNutanixClient +} + +func (self *SRegion) GetId() string { + return self.cli.cpcfg.Id +} + +func (self *SRegion) GetGlobalId() string { + return fmt.Sprintf("%s/%s", api.CLOUD_PROVIDER_NUTANIX, self.cli.cpcfg.Id) +} + +func (self *SRegion) GetName() string { + return self.cli.cpcfg.Name +} + +func (self *SRegion) GetI18n() cloudprovider.SModelI18nTable { + table := cloudprovider.SModelI18nTable{} + table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName()) + return table +} + +func (self *SRegion) CreateEIP(opts *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SRegion) GetISecurityGroupById(secgroupId string) (cloudprovider.ICloudSecurityGroup, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SRegion) GetISecurityGroupByName(opts *cloudprovider.SecurityGroupFilterOptions) (cloudprovider.ICloudSecurityGroup, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SRegion) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SRegion) GetCapabilities() []string { + return self.cli.GetCapabilities() +} + +func (self *SRegion) GetCloudEnv() string { + return "" +} + +func (self *SRegion) GetProvider() string { + return api.CLOUD_PROVIDER_NUTANIX +} + +func (self *SRegion) GetStatus() string { + return api.CLOUD_REGION_STATUS_INSERVER +} + +func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo { + return cloudprovider.SGeographicInfo{} +} + +func (self *SRegion) GetIEipById(id string) (cloudprovider.ICloudEIP, error) { + return nil, cloudprovider.ErrNotFound +} + +func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { + return []cloudprovider.ICloudEIP{}, nil +} + +func (self *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { + vpc, err := self.GetVpc(id) + if err != nil { + return nil, errors.Wrapf(err, "GetVpc(%s)", id) + } + return vpc, nil +} + +func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) { + vpcs, err := self.GetVpcs() + if err != nil { + return nil, errors.Wrapf(err, "GetVpcs") + } + ret := []cloudprovider.ICloudVpc{} + for i := range vpcs { + vpcs[i].region = self + ret = append(ret, &vpcs[i]) + } + return ret, nil +} + +func (self *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) { + clusters, err := self.GetClusters() + if err != nil { + return nil, errors.Wrapf(err, "GetClusters") + } + ret := []cloudprovider.ICloudZone{} + for i := range clusters { + ret = append(ret, &SZone{ + SCluster: clusters[i], + region: self, + }) + } + return ret, nil +} + +func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) { + zones, err := self.GetIZones() + if err != nil { + return nil, errors.Wrapf(err, "GetIZones") + } + for i := range zones { + if zones[i].GetGlobalId() == id { + return zones[i], nil + } + } + return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) +} + +func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { + zones, err := self.GetIZones() + if err != nil { + return nil, errors.Wrapf(err, "GetIZones") + } + ret := []cloudprovider.ICloudHost{} + for i := range zones { + part, err := zones[i].GetIHosts() + if err != nil { + return nil, errors.Wrapf(err, "GetIHost") + } + ret = append(ret, part...) + } + return ret, nil +} + +func (self *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) { + vm, err := self.GetInstance(id) + if err != nil { + return nil, err + } + return vm, nil +} + +func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) { + hosts, err := self.GetIHosts() + if err != nil { + return nil, errors.Wrapf(err, "GetIHosts") + } + for i := range hosts { + if hosts[i].GetGlobalId() == id { + return hosts[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SRegion) list(res string, params url.Values, retVal interface{}) (int, error) { + return self.cli.list(res, params, retVal) +} + +func (self *SRegion) get(res, id string, params url.Values, retVal interface{}) error { + return self.cli.get(res, id, params, retVal) +} + +func (self *SRegion) listAll(res string, params url.Values, retVal interface{}) error { + return self.cli.listAll(res, params, retVal) +} diff --git a/pkg/multicloud/nutanix/shell/cluster.go b/pkg/multicloud/nutanix/shell/cluster.go new file mode 100644 index 0000000000..7ce8673204 --- /dev/null +++ b/pkg/multicloud/nutanix/shell/cluster.go @@ -0,0 +1,47 @@ +// 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/nutanix" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type ClusterListOptions struct { + } + shellutils.R(&ClusterListOptions{}, "cluster-list", "list clusters", func(cli *nutanix.SRegion, args *ClusterListOptions) error { + clusters, err := cli.GetClusters() + if err != nil { + return err + } + printList(clusters, 0, 0, 0, []string{}) + return nil + }) + + type ClusterIdOptions struct { + ID string + } + + shellutils.R(&ClusterIdOptions{}, "cluster-show", "show clusters", func(cli *nutanix.SRegion, args *ClusterIdOptions) error { + cluster, err := cli.GetCluster(args.ID) + if err != nil { + return err + } + printObject(cluster) + return nil + }) + +} diff --git a/pkg/multicloud/nutanix/shell/disk.go b/pkg/multicloud/nutanix/shell/disk.go new file mode 100644 index 0000000000..c0828eda0c --- /dev/null +++ b/pkg/multicloud/nutanix/shell/disk.go @@ -0,0 +1,49 @@ +// 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/nutanix" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type DiskListOptions struct { + StorageId string + InstanceId string + } + shellutils.R(&DiskListOptions{}, "disk-list", "list disks", func(cli *nutanix.SRegion, args *DiskListOptions) error { + disks, err := cli.GetDisks(args.StorageId, args.InstanceId) + if err != nil { + return err + } + printList(disks, 0, 0, 0, []string{}) + return nil + }) + + type DiskIdOptions struct { + ID string + } + + shellutils.R(&DiskIdOptions{}, "disk-show", "show disk", func(cli *nutanix.SRegion, args *DiskIdOptions) error { + disk, err := cli.GetDisk(args.ID) + if err != nil { + return err + } + printObject(disk) + return nil + }) + +} diff --git a/pkg/multicloud/nutanix/shell/doc.go b/pkg/multicloud/nutanix/shell/doc.go new file mode 100644 index 0000000000..3820b66706 --- /dev/null +++ b/pkg/multicloud/nutanix/shell/doc.go @@ -0,0 +1 @@ +package shell // import "yunion.io/x/onecloud/pkg/multicloud/nutanix/shell" diff --git a/pkg/multicloud/nutanix/shell/host.go b/pkg/multicloud/nutanix/shell/host.go new file mode 100644 index 0000000000..4bab663fb3 --- /dev/null +++ b/pkg/multicloud/nutanix/shell/host.go @@ -0,0 +1,47 @@ +// 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/nutanix" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type HostListOptions struct { + } + shellutils.R(&HostListOptions{}, "host-list", "list hosts", func(cli *nutanix.SRegion, args *HostListOptions) error { + hosts, err := cli.GetHosts() + if err != nil { + return err + } + printList(hosts, 0, 0, 0, []string{}) + return nil + }) + + type HostIdOptions struct { + ID string + } + + shellutils.R(&HostIdOptions{}, "host-show", "show host", func(cli *nutanix.SRegion, args *HostIdOptions) error { + host, err := cli.GetHost(args.ID) + if err != nil { + return err + } + printObject(host) + return nil + }) + +} diff --git a/pkg/multicloud/nutanix/shell/image.go b/pkg/multicloud/nutanix/shell/image.go new file mode 100644 index 0000000000..0ff31484a0 --- /dev/null +++ b/pkg/multicloud/nutanix/shell/image.go @@ -0,0 +1,47 @@ +// 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/nutanix" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type ImageListOptions struct { + } + shellutils.R(&ImageListOptions{}, "image-list", "list hosts", func(cli *nutanix.SRegion, args *ImageListOptions) error { + images, err := cli.GetImages() + if err != nil { + return err + } + printList(images, 0, 0, 0, []string{}) + return nil + }) + + type ImageIdOptions struct { + ID string + } + + shellutils.R(&ImageIdOptions{}, "image-show", "show host", func(cli *nutanix.SRegion, args *ImageIdOptions) error { + image, err := cli.GetImage(args.ID) + if err != nil { + return err + } + printObject(image) + return nil + }) + +} diff --git a/pkg/multicloud/nutanix/shell/instance.go b/pkg/multicloud/nutanix/shell/instance.go new file mode 100644 index 0000000000..9a3bd12112 --- /dev/null +++ b/pkg/multicloud/nutanix/shell/instance.go @@ -0,0 +1,47 @@ +// 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/nutanix" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type InstanceListOptions struct { + } + shellutils.R(&InstanceListOptions{}, "instance-list", "list instances", func(cli *nutanix.SRegion, args *InstanceListOptions) error { + vms, err := cli.GetInstances() + if err != nil { + return err + } + printList(vms, 0, 0, 0, []string{}) + return nil + }) + + type InstanceIdOptions struct { + ID string + } + + shellutils.R(&InstanceIdOptions{}, "instance-show", "show instance", func(cli *nutanix.SRegion, args *InstanceIdOptions) error { + vm, err := cli.GetInstance(args.ID) + if err != nil { + return err + } + printObject(vm) + return nil + }) + +} diff --git a/pkg/multicloud/nutanix/shell/printutils.go b/pkg/multicloud/nutanix/shell/printutils.go new file mode 100644 index 0000000000..1b46ec704a --- /dev/null +++ b/pkg/multicloud/nutanix/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/nutanix/shell/storage.go b/pkg/multicloud/nutanix/shell/storage.go new file mode 100644 index 0000000000..3b7b5bd919 --- /dev/null +++ b/pkg/multicloud/nutanix/shell/storage.go @@ -0,0 +1,47 @@ +// 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/nutanix" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type StorageListOptions struct { + } + shellutils.R(&StorageListOptions{}, "storage-list", "list storage", func(cli *nutanix.SRegion, args *StorageListOptions) error { + storages, err := cli.GetStorages() + if err != nil { + return err + } + printList(storages, 0, 0, 0, []string{}) + return nil + }) + + type StorageIdOptions struct { + ID string + } + + shellutils.R(&StorageIdOptions{}, "storage-show", "show storage", func(cli *nutanix.SRegion, args *StorageIdOptions) error { + storage, err := cli.GetStorage(args.ID) + if err != nil { + return err + } + printObject(storage) + return nil + }) + +} diff --git a/pkg/multicloud/nutanix/shell/vpc.go b/pkg/multicloud/nutanix/shell/vpc.go new file mode 100644 index 0000000000..41e1dd2b2f --- /dev/null +++ b/pkg/multicloud/nutanix/shell/vpc.go @@ -0,0 +1,47 @@ +// 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/nutanix" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type VpcListOptions struct { + } + shellutils.R(&VpcListOptions{}, "vpc-list", "list vpc", func(cli *nutanix.SRegion, args *VpcListOptions) error { + vpcs, err := cli.GetVpcs() + if err != nil { + return err + } + printList(vpcs, 0, 0, 0, []string{}) + return nil + }) + + type VpcIdOptions struct { + ID string + } + + shellutils.R(&VpcIdOptions{}, "vpc-show", "show vpc", func(cli *nutanix.SRegion, args *VpcIdOptions) error { + vpc, err := cli.GetVpc(args.ID) + if err != nil { + return err + } + printObject(vpc) + return nil + }) + +} diff --git a/pkg/multicloud/nutanix/storage.go b/pkg/multicloud/nutanix/storage.go new file mode 100644 index 0000000000..8f71dddc83 --- /dev/null +++ b/pkg/multicloud/nutanix/storage.go @@ -0,0 +1,318 @@ +// 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 nutanix + +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 DownMigrateTimesInSecs struct { + SSDSATA int `json:"SSD-SATA"` + SSDPCIe int `json:"SSD-PCIe"` + DASSATA int `json:"DAS-SATA"` +} + +type MappedRemoteContainers struct { +} + +type StorageStats struct { + HypervisorAvgIoLatencyUsecs string `json:"hypervisor_avg_io_latency_usecs"` + NumReadIops string `json:"num_read_iops"` + HypervisorWriteIoBandwidthKBps string `json:"hypervisor_write_io_bandwidth_kBps"` + TimespanUsecs string `json:"timespan_usecs"` + ControllerNumReadIops string `json:"controller_num_read_iops"` + ReadIoPpm string `json:"read_io_ppm"` + ControllerNumIops string `json:"controller_num_iops"` + TotalReadIoTimeUsecs string `json:"total_read_io_time_usecs"` + ControllerTotalReadIoTimeUsecs string `json:"controller_total_read_io_time_usecs"` + HypervisorNumIo string `json:"hypervisor_num_io"` + ControllerTotalTransformedUsageBytes string `json:"controller_total_transformed_usage_bytes"` + ControllerNumWriteIo string `json:"controller_num_write_io"` + AvgReadIoLatencyUsecs string `json:"avg_read_io_latency_usecs"` + ControllerTotalIoTimeUsecs string `json:"controller_total_io_time_usecs"` + ControllerTotalReadIoSizeKbytes string `json:"controller_total_read_io_size_kbytes"` + ControllerNumSeqIo string `json:"controller_num_seq_io"` + ControllerReadIoPpm string `json:"controller_read_io_ppm"` + ControllerTotalIoSizeKbytes string `json:"controller_total_io_size_kbytes"` + ControllerNumIo string `json:"controller_num_io"` + HypervisorAvgReadIoLatencyUsecs string `json:"hypervisor_avg_read_io_latency_usecs"` + NumWriteIops string `json:"num_write_iops"` + ControllerNumRandomIo string `json:"controller_num_random_io"` + NumIops string `json:"num_iops"` + HypervisorNumReadIo string `json:"hypervisor_num_read_io"` + HypervisorTotalReadIoTimeUsecs string `json:"hypervisor_total_read_io_time_usecs"` + ControllerAvgIoLatencyUsecs string `json:"controller_avg_io_latency_usecs"` + NumIo string `json:"num_io"` + ControllerNumReadIo string `json:"controller_num_read_io"` + HypervisorNumWriteIo string `json:"hypervisor_num_write_io"` + ControllerSeqIoPpm string `json:"controller_seq_io_ppm"` + ControllerReadIoBandwidthKBps string `json:"controller_read_io_bandwidth_kBps"` + ControllerIoBandwidthKBps string `json:"controller_io_bandwidth_kBps"` + HypervisorTimespanUsecs string `json:"hypervisor_timespan_usecs"` + HypervisorNumWriteIops string `json:"hypervisor_num_write_iops"` + TotalReadIoSizeKbytes string `json:"total_read_io_size_kbytes"` + HypervisorTotalIoSizeKbytes string `json:"hypervisor_total_io_size_kbytes"` + AvgIoLatencyUsecs string `json:"avg_io_latency_usecs"` + HypervisorNumReadIops string `json:"hypervisor_num_read_iops"` + ControllerWriteIoBandwidthKBps string `json:"controller_write_io_bandwidth_kBps"` + ControllerWriteIoPpm string `json:"controller_write_io_ppm"` + HypervisorAvgWriteIoLatencyUsecs string `json:"hypervisor_avg_write_io_latency_usecs"` + HypervisorTotalReadIoSizeKbytes string `json:"hypervisor_total_read_io_size_kbytes"` + ReadIoBandwidthKBps string `json:"read_io_bandwidth_kBps"` + HypervisorNumIops string `json:"hypervisor_num_iops"` + HypervisorIoBandwidthKBps string `json:"hypervisor_io_bandwidth_kBps"` + ControllerNumWriteIops string `json:"controller_num_write_iops"` + TotalIoTimeUsecs string `json:"total_io_time_usecs"` + ControllerRandomIoPpm string `json:"controller_random_io_ppm"` + ControllerAvgReadIoSizeKbytes string `json:"controller_avg_read_io_size_kbytes"` + TotalTransformedUsageBytes string `json:"total_transformed_usage_bytes"` + AvgWriteIoLatencyUsecs string `json:"avg_write_io_latency_usecs"` + NumReadIo string `json:"num_read_io"` + WriteIoBandwidthKBps string `json:"write_io_bandwidth_kBps"` + HypervisorReadIoBandwidthKBps string `json:"hypervisor_read_io_bandwidth_kBps"` + RandomIoPpm string `json:"random_io_ppm"` + TotalUntransformedUsageBytes string `json:"total_untransformed_usage_bytes"` + HypervisorTotalIoTimeUsecs string `json:"hypervisor_total_io_time_usecs"` + NumRandomIo string `json:"num_random_io"` + ControllerAvgWriteIoSizeKbytes string `json:"controller_avg_write_io_size_kbytes"` + ControllerAvgReadIoLatencyUsecs string `json:"controller_avg_read_io_latency_usecs"` + NumWriteIo string `json:"num_write_io"` + TotalIoSizeKbytes string `json:"total_io_size_kbytes"` + IoBandwidthKBps string `json:"io_bandwidth_kBps"` + ControllerTimespanUsecs string `json:"controller_timespan_usecs"` + NumSeqIo string `json:"num_seq_io"` + SeqIoPpm string `json:"seq_io_ppm"` + WriteIoPpm string `json:"write_io_ppm"` + ControllerAvgWriteIoLatencyUsecs string `json:"controller_avg_write_io_latency_usecs"` +} +type StorageUsageStats struct { + StorageUserUnreservedOwnUsageBytes string `json:"storage.user_unreserved_own_usage_bytes"` + StorageReservedFreeBytes string `json:"storage.reserved_free_bytes"` + DataReductionOverallSavingRatioPpm string `json:"data_reduction.overall.saving_ratio_ppm"` + DataReductionUserSavedBytes string `json:"data_reduction.user_saved_bytes"` + StorageTierDasSataUsageBytes string `json:"storage_tier.das-sata.usage_bytes"` + DataReductionErasureCodingPostReductionBytes string `json:"data_reduction.erasure_coding.post_reduction_bytes"` + StorageReservedUsageBytes string `json:"storage.reserved_usage_bytes"` + StorageUserUnreservedSharedUsageBytes string `json:"storage.user_unreserved_shared_usage_bytes"` + StorageUserUnreservedUsageBytes int64 `json:"storage.user_unreserved_usage_bytes"` + StorageUsageBytes string `json:"storage.usage_bytes"` + DataReductionCompressionUserSavedBytes string `json:"data_reduction.compression.user_saved_bytes"` + DataReductionErasureCodingUserPreReductionBytes string `json:"data_reduction.erasure_coding.user_pre_reduction_bytes"` + StorageUserUnreservedCapacityBytes string `json:"storage.user_unreserved_capacity_bytes"` + StorageUserCapacityBytes int64 `json:"storage.user_capacity_bytes"` + StorageUserStoragePoolCapacityBytes string `json:"storage.user_storage_pool_capacity_bytes"` + DataReductionPreReductionBytes string `json:"data_reduction.pre_reduction_bytes"` + DataReductionUserPreReductionBytes string `json:"data_reduction.user_pre_reduction_bytes"` + StorageUserOtherContainersReservedCapacityBytes string `json:"storage.user_other_containers_reserved_capacity_bytes"` + DataReductionErasureCodingPreReductionBytes string `json:"data_reduction.erasure_coding.pre_reduction_bytes"` + StorageCapacityBytes int64 `json:"storage.capacity_bytes"` + StorageUserUnreservedFreeBytes string `json:"storage.user_unreserved_free_bytes"` + DataReductionCloneUserSavedBytes string `json:"data_reduction.clone.user_saved_bytes"` + DataReductionDedupPostReductionBytes string `json:"data_reduction.dedup.post_reduction_bytes"` + DataReductionCloneSavingRatioPpm string `json:"data_reduction.clone.saving_ratio_ppm"` + StorageLogicalUsageBytes string `json:"storage.logical_usage_bytes"` + DataReductionSavedBytes string `json:"data_reduction.saved_bytes"` + StorageUserDiskPhysicalUsageBytes string `json:"storage.user_disk_physical_usage_bytes"` + StorageFreeBytes string `json:"storage.free_bytes"` + DataReductionCompressionPostReductionBytes string `json:"data_reduction.compression.post_reduction_bytes"` + DataReductionCompressionUserPostReductionBytes string `json:"data_reduction.compression.user_post_reduction_bytes"` + StorageUserFreeBytes string `json:"storage.user_free_bytes"` + StorageUnreservedFreeBytes string `json:"storage.unreserved_free_bytes"` + StorageUserContainerOwnUsageBytes string `json:"storage.user_container_own_usage_bytes"` + DataReductionCompressionSavingRatioPpm string `json:"data_reduction.compression.saving_ratio_ppm"` + StorageUserUsageBytes int64 `json:"storage.user_usage_bytes"` + DataReductionErasureCodingUserSavedBytes string `json:"data_reduction.erasure_coding.user_saved_bytes"` + DataReductionDedupSavingRatioPpm string `json:"data_reduction.dedup.saving_ratio_ppm"` + StorageUnreservedCapacityBytes string `json:"storage.unreserved_capacity_bytes"` + StorageUserReservedUsageBytes string `json:"storage.user_reserved_usage_bytes"` + DataReductionCompressionUserPreReductionBytes string `json:"data_reduction.compression.user_pre_reduction_bytes"` + DataReductionUserPostReductionBytes string `json:"data_reduction.user_post_reduction_bytes"` + DataReductionOverallUserSavedBytes string `json:"data_reduction.overall.user_saved_bytes"` + DataReductionErasureCodingParityBytes string `json:"data_reduction.erasure_coding.parity_bytes"` + DataReductionSavingRatioPpm string `json:"data_reduction.saving_ratio_ppm"` + StorageUnreservedOwnUsageBytes string `json:"storage.unreserved_own_usage_bytes"` + DataReductionErasureCodingSavingRatioPpm string `json:"data_reduction.erasure_coding.saving_ratio_ppm"` + StorageUserReservedCapacityBytes string `json:"storage.user_reserved_capacity_bytes"` + DataReductionThinProvisionUserSavedBytes string `json:"data_reduction.thin_provision.user_saved_bytes"` + StorageDiskPhysicalUsageBytes string `json:"storage.disk_physical_usage_bytes"` + DataReductionErasureCodingUserPostReductionBytes string `json:"data_reduction.erasure_coding.user_post_reduction_bytes"` + DataReductionCompressionPreReductionBytes string `json:"data_reduction.compression.pre_reduction_bytes"` + DataReductionDedupPreReductionBytes string `json:"data_reduction.dedup.pre_reduction_bytes"` + DataReductionDedupUserSavedBytes string `json:"data_reduction.dedup.user_saved_bytes"` + StorageUnreservedUsageBytes string `json:"storage.unreserved_usage_bytes"` + StorageTierSsdUsageBytes string `json:"storage_tier.ssd.usage_bytes"` + DataReductionPostReductionBytes string `json:"data_reduction.post_reduction_bytes"` + DataReductionThinProvisionSavingRatioPpm string `json:"data_reduction.thin_provision.saving_ratio_ppm"` + StorageReservedCapacityBytes string `json:"storage.reserved_capacity_bytes"` + StorageUserReservedFreeBytes string `json:"storage.user_reserved_free_bytes"` +} + +type SStorage struct { + multicloud.SStorageBase + multicloud.STagBase + + zone *SZone + + StorageContainerUUID string `json:"storage_container_uuid"` + Name string `json:"name"` + ClusterUUID string `json:"cluster_uuid"` + MarkedForRemoval bool `json:"marked_for_removal"` + MaxCapacity int64 `json:"max_capacity"` + TotalExplicitReservedCapacity int `json:"total_explicit_reserved_capacity"` + TotalImplicitReservedCapacity int `json:"total_implicit_reserved_capacity"` + AdvertisedCapacity interface{} `json:"advertised_capacity"` + ReplicationFactor int `json:"replication_factor"` + OplogReplicationFactor int `json:"oplog_replication_factor"` + NfsWhitelist []interface{} `json:"nfs_whitelist"` + NfsWhitelistInherited bool `json:"nfs_whitelist_inherited"` + RandomIoPreference []string `json:"random_io_preference"` + SeqIoPreference []string `json:"seq_io_preference"` + IlmPolicy interface{} `json:"ilm_policy"` + DownMigrateTimesInSecs DownMigrateTimesInSecs `json:"down_migrate_times_in_secs"` + ErasureCode string `json:"erasure_code"` + InlineEcEnabled interface{} `json:"inline_ec_enabled"` + PreferHigherEcfaultDomain interface{} `json:"prefer_higher_ecfault_domain"` + ErasureCodeDelaySecs interface{} `json:"erasure_code_delay_secs"` + FingerPrintOnWrite string `json:"finger_print_on_write"` + OnDiskDedup string `json:"on_disk_dedup"` + CompressionEnabled bool `json:"compression_enabled"` + CompressionDelayInSecs int `json:"compression_delay_in_secs"` + IsNutanixManaged interface{} `json:"is_nutanix_managed"` + EnableSoftwareEncryption bool `json:"enable_software_encryption"` + VstoreNameList []string `json:"vstore_name_list"` + MappedRemoteContainers MappedRemoteContainers `json:"mapped_remote_containers"` + Stats StorageStats `json:"stats"` + UsageStats StorageUsageStats `json:"usage_stats"` + Encrypted interface{} `json:"encrypted"` +} + +func (self *SStorage) GetName() string { + return self.Name +} + +func (self *SStorage) GetId() string { + return self.StorageContainerUUID +} + +func (self *SStorage) GetGlobalId() string { + return self.StorageContainerUUID +} + +func (self *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + disks, err := self.zone.region.GetDisks(self.GetGlobalId(), "") + if err != nil { + return nil, errors.Wrapf(err, "GetDisks") + } + ret := []cloudprovider.ICloudDisk{} + for i := range disks { + disks[i].storage = self + ret = append(ret, &disks[i]) + } + return ret, nil +} + +func (self *SStorage) CreateIDisk(conf *cloudprovider.DiskCreateConfig) (cloudprovider.ICloudDisk, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SStorage) GetCapacityMB() int64 { + return self.UsageStats.StorageUserCapacityBytes / 1024 / 1024 +} + +func (self *SStorage) GetCapacityUsedMB() int64 { + return self.UsageStats.StorageUserUsageBytes / 1024 / 1024 +} + +func (self *SStorage) GetEnabled() bool { + return true +} + +func (self *SStorage) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) { + disk, err := self.zone.region.GetDisk(id) + if err != nil { + return nil, err + } + if disk.StorageContainerUUID != self.GetGlobalId() { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) + } + disk.storage = self + return disk, nil +} + +func (self *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { + return &SStoragecache{storage: self, region: self.zone.region} +} + +func (self *SRegion) GetStorages() ([]SStorage, error) { + storages := []SStorage{} + err := self.listAll("storage_containers", nil, &storages) + if err != nil { + return nil, err + } + ret := []SStorage{} + for i := range storages { + if storages[i].Name == "NutanixManagementShare" { // https://portal.nutanix.com/page/documents/details?targetId=Web-Console-Guide-Prism-v5_15-ZH:wc-container-create-wc-t.html + continue + } + ret = append(ret, storages[i]) + } + return ret, nil +} + +func (self *SRegion) GetStorage(id string) (*SStorage, error) { + storage := &SStorage{} + return storage, self.get("storage_containers", id, nil, storage) +} + +func (self *SStorage) GetIZone() cloudprovider.ICloudZone { + return self.zone +} + +func (self *SStorage) GetMediumType() string { + return api.DISK_TYPE_SSD +} + +func (self *SStorage) GetMountPoint() string { + return "" +} + +func (self *SStorage) GetStatus() string { + return api.STORAGE_ONLINE +} + +func (self *SStorage) Refresh() error { + storage, err := self.zone.region.GetStorage(self.GetGlobalId()) + if err != nil { + return err + } + return jsonutils.Update(self, storage) +} + +func (self *SStorage) GetStorageConf() jsonutils.JSONObject { + return jsonutils.NewDict() +} + +func (self *SStorage) GetStorageType() string { + return api.STORAGE_LOCAL +} + +func (self *SStorage) IsSysDiskStore() bool { + return true +} diff --git a/pkg/multicloud/nutanix/storagecache.go b/pkg/multicloud/nutanix/storagecache.go new file mode 100644 index 0000000000..8927c79951 --- /dev/null +++ b/pkg/multicloud/nutanix/storagecache.go @@ -0,0 +1,119 @@ +// 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 nutanix + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SStoragecache struct { + multicloud.SResourceBase + multicloud.STagBase + + storage *SStorage + region *SRegion +} + +func (self *SStoragecache) GetName() string { + return self.storage.GetName() +} + +func (self *SStoragecache) GetId() string { + return self.storage.GetId() +} + +func (self *SStoragecache) GetGlobalId() string { + return self.storage.GetGlobalId() +} + +func (self *SStoragecache) GetStatus() string { + return "available" +} + +func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) { + images, err := self.region.GetImages() + if err != nil { + return nil, errors.Wrapf(err, "GetImages") + } + ret := []cloudprovider.ICloudImage{} + for i := range images { + if images[i].StorageContainerUUID != self.storage.GetGlobalId() { + continue + } + images[i].cache = self + ret = append(ret, &images[i]) + } + return ret, nil +} + +func (self *SStoragecache) GetICustomizedCloudImages() ([]cloudprovider.ICloudImage, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SStoragecache) GetIImageById(id string) (cloudprovider.ICloudImage, error) { + image, err := self.region.GetImage(id) + if err != nil { + return nil, err + } + if image.StorageContainerUUID != self.storage.GetGlobalId() { + return nil, cloudprovider.ErrNotFound + } + image.cache = self + return image, nil +} + +func (self *SStoragecache) GetPath() string { + return "" +} + +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 (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) { + storages, err := self.GetStorages() + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudStoragecache{} + for i := range storages { + cache := &SStoragecache{storage: &storages[i], region: self} + ret = append(ret, cache) + } + return ret, nil +} + +func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { + storage, err := self.GetStorage(id) + if err != nil { + return nil, errors.Wrapf(err, "GetStorage") + } + return &SStoragecache{region: self, storage: storage}, nil +} diff --git a/pkg/multicloud/nutanix/vpc.go b/pkg/multicloud/nutanix/vpc.go new file mode 100644 index 0000000000..d56174d8bf --- /dev/null +++ b/pkg/multicloud/nutanix/vpc.go @@ -0,0 +1,127 @@ +// 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 nutanix + +import ( + "fmt" + "net/url" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type DhcpOptions struct { +} + +type IPConfig struct { + NetworkAddress string `json:"network_address"` + PrefixLength int `json:"prefix_length"` + DefaultGateway string `json:"default_gateway"` + DhcpOptions DhcpOptions `json:"dhcp_options"` + Pool []struct { + Range string `json:"range"` + } `json:"pool"` + DhcpServerAddress string `json:"dhcp_server_address"` +} + +type SVpc struct { + multicloud.SVpc + multicloud.STagBase + + region *SRegion + + LogicalTimestamp int `json:"logical_timestamp"` + VlanID int `json:"vlan_id"` + UUID string `json:"uuid"` + Name string `json:"name"` + IPConfig IPConfig `json:"ip_config,omitempty"` +} + +func (self *SVpc) GetName() string { + return self.Name +} + +func (self *SVpc) GetId() string { + return self.UUID +} + +func (self *SVpc) GetGlobalId() string { + return self.UUID +} + +func (self *SVpc) Delete() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SVpc) GetCidrBlock() string { + if len(self.IPConfig.NetworkAddress) > 0 { + return fmt.Sprintf("%s/%d", self.IPConfig.NetworkAddress, self.IPConfig.PrefixLength) + } + return "" +} + +func (self *SVpc) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) { + return []cloudprovider.ICloudRouteTable{}, nil +} + +func (self *SVpc) GetIRouteTableById(routeTableId string) (cloudprovider.ICloudRouteTable, error) { + return nil, cloudprovider.ErrNotFound +} + +func (self *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) { + return []cloudprovider.ICloudSecurityGroup{}, nil +} + +func (self *SRegion) GetVpcs() ([]SVpc, error) { + vpcs := []SVpc{} + _, err := self.list("networks", url.Values{}, &vpcs) + return vpcs, err +} + +func (self *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) { + wire := &SWire{vpc: self} + return []cloudprovider.ICloudWire{wire}, nil +} + +func (self *SVpc) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) { + wires, err := self.GetIWires() + if err != nil { + return nil, err + } + for i := range wires { + if wires[i].GetGlobalId() == wireId { + return wires[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SVpc) GetIsDefault() bool { + return len(self.GetCidrBlock()) > 0 +} + +func (self *SVpc) GetRegion() cloudprovider.ICloudRegion { + return self.region +} + +func (self *SVpc) GetStatus() string { + return api.VPC_STATUS_AVAILABLE +} + +func (self *SRegion) GetVpc(id string) (*SVpc, error) { + vpc := &SVpc{region: self} + return vpc, self.get("networks", id, url.Values{}, vpc) +} diff --git a/pkg/multicloud/nutanix/wire.go b/pkg/multicloud/nutanix/wire.go new file mode 100644 index 0000000000..ad2c6193f3 --- /dev/null +++ b/pkg/multicloud/nutanix/wire.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 nutanix + +import ( + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SWire struct { + multicloud.SResourceBase + multicloud.STagBase + + vpc *SVpc +} + +func (self *SWire) GetName() string { + return self.vpc.GetName() +} + +func (self *SWire) GetId() string { + return self.vpc.GetId() +} + +func (self *SWire) GetGlobalId() string { + return self.vpc.GetGlobalId() +} + +func (self *SWire) CreateINetwork(opts *cloudprovider.SNetworkCreateOptions) (cloudprovider.ICloudNetwork, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SWire) GetBandwidth() int { + return 10000 +} + +func (self *SWire) GetINetworks() ([]cloudprovider.ICloudNetwork, error) { + ret := []cloudprovider.ICloudNetwork{} + if len(self.vpc.IPConfig.Pool) == 0 { + network := &SNetwork{wire: self} + ret = append(ret, network) + } + for _, pool := range self.vpc.IPConfig.Pool { + network := &SNetwork{wire: self} + network.Range = pool.Range + ret = append(ret, network) + } + return ret, nil +} + +func (self *SWire) GetINetworkById(netid string) (cloudprovider.ICloudNetwork, error) { + networks, err := self.GetINetworks() + if err != nil { + return nil, err + } + + for i := range networks { + if networks[i].GetGlobalId() == netid { + return networks[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SWire) GetIVpc() cloudprovider.ICloudVpc { + return self.vpc +} + +func (self *SWire) GetIZone() cloudprovider.ICloudZone { + cluster := SCluster{} + err := self.vpc.region.get("cluster", "", nil, &cluster) + if err != nil { + return nil + } + return &SZone{SCluster: cluster, region: self.vpc.region} +} + +func (self *SWire) GetStatus() string { + return api.WIRE_STATUS_AVAILABLE +} diff --git a/pkg/multicloud/nutanix/zone.go b/pkg/multicloud/nutanix/zone.go new file mode 100644 index 0000000000..419490a5de --- /dev/null +++ b/pkg/multicloud/nutanix/zone.go @@ -0,0 +1,118 @@ +// 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 nutanix + +import ( + "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 SZone struct { + multicloud.STagBase + multicloud.SResourceBase + + SCluster + region *SRegion +} + +func (self *SZone) GetName() string { + return self.Name +} + +func (self *SZone) GetId() string { + return self.UUID +} + +func (self *SZone) GetGlobalId() string { + return self.UUID +} + +func (self *SZone) GetI18n() cloudprovider.SModelI18nTable { + table := cloudprovider.SModelI18nTable{} + table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName()) + return table +} + +func (self *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) { + hosts, err := self.region.GetHosts() + if err != nil { + return nil, errors.Wrapf(err, "GetIHosts") + } + firstHost := true + ret := []cloudprovider.ICloudHost{} + for i := range hosts { + if hosts[i].ClusterUUID != self.UUID { + continue + } + hosts[i].zone = self + hosts[i].firstHost = firstHost + ret = append(ret, &hosts[i]) + if firstHost { + firstHost = false + } + } + return ret, nil +} + +func (self *SZone) GetIHostById(id string) (cloudprovider.ICloudHost, error) { + host, err := self.region.GetHost(id) + if err != nil { + return nil, errors.Wrapf(err, "GetIHostById(%s)", id) + } + if host.ClusterUUID != self.UUID { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) + } + host.zone = self + return host, nil +} + +func (self *SZone) GetIRegion() cloudprovider.ICloudRegion { + return self.region +} + +func (self *SZone) GetStatus() string { + return api.ZONE_ENABLE +} + +func (self *SZone) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + storages, err := self.region.GetStorages() + if err != nil { + return nil, errors.Wrapf(err, "GetStorages") + } + ret := []cloudprovider.ICloudStorage{} + for i := range storages { + if storages[i].ClusterUUID != self.UUID { + continue + } + storages[i].zone = self + ret = append(ret, &storages[i]) + } + return ret, nil +} + +func (self *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + storage, err := self.region.GetStorage(id) + if err != nil { + return nil, errors.Wrapf(err, "GetStorage", id) + } + if storage.ClusterUUID != self.UUID { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) + } + storage.zone = self + return storage, nil +}