From 9eb41b392f41c6e70210f441bf14d3aa9865bb69 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Tue, 7 Aug 2018 15:19:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=EF=BC=9A1.=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0vmware=20cloud=20provider=20driver=E5=88=9D=E6=AD=A5?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=202.=20=E5=A2=9E=E5=8A=A0vmware=20vnc?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E8=8E=B7=E5=8F=96=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gopkg.lock | 25 +- Gopkg.toml | 5 + cmd/aliyuncli/main.go | 8 +- cmd/climc/shell/utils.go | 8 +- cmd/esxicli/main.go | 118 ++++++ pkg/cloudprovider/consts.go | 2 + pkg/cloudprovider/resources.go | 2 +- pkg/compute/guestdrivers/aliyun.go | 22 -- pkg/compute/guestdrivers/esxi.go | 9 - pkg/compute/guestdrivers/managedvirtual.go | 25 +- pkg/compute/models/cloudproviders.go | 60 ++- pkg/compute/models/initdb.go | 1 + pkg/util/aliyun/aliyun.go | 3 +- pkg/util/aliyun/instance.go | 9 +- pkg/util/aliyun/shell/disk.go | 3 +- pkg/util/aliyun/shell/image.go | 5 +- pkg/util/aliyun/shell/instance.go | 13 +- pkg/util/aliyun/shell/instancetype.go | 5 +- pkg/util/aliyun/shell/keypair.go | 5 +- pkg/util/aliyun/shell/oss.go | 13 +- pkg/util/aliyun/shell/printutils.go | 11 + pkg/util/aliyun/shell/region.go | 3 +- pkg/util/aliyun/shell/routetable.go | 5 +- pkg/util/aliyun/shell/secgroup.go | 5 +- pkg/util/aliyun/shell/task.go | 7 +- pkg/util/aliyun/shell/vpc.go | 3 +- pkg/util/aliyun/shell/vrouter.go | 3 +- pkg/util/aliyun/shell/vswitch.go | 3 +- pkg/util/aliyun/shell/zone.go | 3 +- pkg/util/esxi/datacenter.go | 78 ++++ pkg/util/esxi/host.go | 360 ++++++++++++++++++ pkg/util/esxi/manager.go | 226 +++++++++++ pkg/util/esxi/manager_test.go | 22 ++ pkg/util/esxi/mobase.go | 124 ++++++ pkg/util/esxi/mobase_test.go | 14 + pkg/util/esxi/provider/provider.go | 119 ++++++ pkg/util/esxi/shell/datacenter.go | 19 + pkg/util/esxi/shell/host.go | 36 ++ pkg/util/esxi/shell/store.go | 24 ++ pkg/util/esxi/shell/utils.go | 12 + pkg/util/esxi/shell/virtualmachine.go | 59 +++ pkg/util/esxi/storage.go | 89 +++++ pkg/util/esxi/virtualmachine.go | 208 ++++++++++ pkg/util/printutils/printgetter.go | 60 +++ .../{printjson => printutils}/printjson.go | 12 +- .../shell/utils.go => printutils/printobj.go} | 11 +- .../{aliyun/shell => shellutils}/shell.go | 2 +- 47 files changed, 1760 insertions(+), 99 deletions(-) create mode 100644 cmd/esxicli/main.go create mode 100644 pkg/util/aliyun/shell/printutils.go create mode 100644 pkg/util/esxi/datacenter.go create mode 100644 pkg/util/esxi/host.go create mode 100644 pkg/util/esxi/manager.go create mode 100644 pkg/util/esxi/manager_test.go create mode 100644 pkg/util/esxi/mobase.go create mode 100644 pkg/util/esxi/mobase_test.go create mode 100644 pkg/util/esxi/provider/provider.go create mode 100644 pkg/util/esxi/shell/datacenter.go create mode 100644 pkg/util/esxi/shell/host.go create mode 100644 pkg/util/esxi/shell/store.go create mode 100644 pkg/util/esxi/shell/utils.go create mode 100644 pkg/util/esxi/shell/virtualmachine.go create mode 100644 pkg/util/esxi/storage.go create mode 100644 pkg/util/esxi/virtualmachine.go create mode 100644 pkg/util/printutils/printgetter.go rename pkg/util/{printjson => printutils}/printjson.go (88%) rename pkg/util/{aliyun/shell/utils.go => printutils/printobj.go} (68%) rename pkg/util/{aliyun/shell => shellutils}/shell.go (93%) diff --git a/Gopkg.lock b/Gopkg.lock index 059474b2cb..9af3b846ea 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -258,6 +258,29 @@ version = "v1.1.1" [[projects]] + branch = "master" + name = "github.com/vmware/govmomi" + packages = [ + ".", + "nfc", + "object", + "property", + "session", + "task", + "view", + "vim25", + "vim25/debug", + "vim25/methods", + "vim25/mo", + "vim25/progress", + "vim25/soap", + "vim25/types", + "vim25/xml" + ] + revision = "bbd99532a768d2fe369079ceda730e30726ae1a6" + +[[projects]] + branch = "master" name = "github.com/yunionio/jsonutils" packages = ["."] revision = "6dd39f8579af6c6b61b971eb3a1ccf55e724b0ca" @@ -369,6 +392,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "7b33757daccd7b7b9fc876fd74890fc9c7e638b9ee0f68d665559165716eb70d" + inputs-digest = "66245a09bfe41c9921a519539ddf9c585a75079e1ed418a90a0f075b0e097805" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index d03efbe115..abb660d54b 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -52,3 +52,8 @@ [prune] go-tests = true unused-packages = true + +[[constraint]] + branch = "master" + name = "github.com/vmware/govmomi" + diff --git a/cmd/aliyuncli/main.go b/cmd/aliyuncli/main.go index d12f480ac1..c5a134392d 100644 --- a/cmd/aliyuncli/main.go +++ b/cmd/aliyuncli/main.go @@ -7,7 +7,9 @@ import ( "github.com/yunionio/log" "github.com/yunionio/structarg" "github.com/yunionio/onecloud/pkg/util/aliyun" - "github.com/yunionio/onecloud/pkg/util/aliyun/shell" + "github.com/yunionio/onecloud/pkg/util/shellutils" + + _ "github.com/yunionio/onecloud/pkg/util/aliyun/shell" ) type BaseOptions struct { @@ -35,7 +37,7 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) { type HelpOptions struct { SUBCOMMAND string `help:"sub-command name"` } - shell.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error { + shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error { helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND) if e != nil { return e @@ -44,7 +46,7 @@ func getSubcommandParser() (*structarg.ArgumentParser, error) { return nil } }) - for _, v := range shell.CommandTable { + for _, v := range shellutils.CommandTable { _, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback) if e != nil { return nil, e diff --git a/cmd/climc/shell/utils.go b/cmd/climc/shell/utils.go index 8c323615a7..124e9ab92d 100644 --- a/cmd/climc/shell/utils.go +++ b/cmd/climc/shell/utils.go @@ -4,17 +4,17 @@ import ( "github.com/yunionio/jsonutils" "github.com/yunionio/onecloud/pkg/mcclient/modules" - "github.com/yunionio/onecloud/pkg/util/printjson" + "github.com/yunionio/onecloud/pkg/util/printutils" ) func printList(list *modules.ListResult, columns []string) { - printjson.PrintList(list, columns) + printutils.PrintJSONList(list, columns) } func printObject(obj jsonutils.JSONObject) { - printjson.PrintObject(obj) + printutils.PrintJSONObject(obj) } func printBatchResults(results []modules.SubmitResult, columns []string) { - printjson.PrintBatchResults(results, columns) + printutils.PrintJSONBatchResults(results, columns) } diff --git a/cmd/esxicli/main.go b/cmd/esxicli/main.go new file mode 100644 index 0000000000..87377e52e6 --- /dev/null +++ b/cmd/esxicli/main.go @@ -0,0 +1,118 @@ +package main + +import ( + "fmt" + "os" + + "github.com/yunionio/log" + "github.com/yunionio/structarg" + + "github.com/yunionio/onecloud/pkg/util/esxi" + "github.com/yunionio/onecloud/pkg/util/shellutils" + + _ "github.com/yunionio/onecloud/pkg/util/esxi/shell" +) + +type BaseOptions struct { + Help bool `help:"Show help"` + Host string `help:"Host IP or NAME" default:"$VMWARE_HOST"` + Port int `help:"Service port" default:"$VMWARE_PORT"` + Account string `help:"VCenter or ESXi Account" default:"$VMWARE_ACCOUNT"` + Password string `help:"Password" default:"$VMWARE_PASSWORD"` + SUBCOMMAND string `help:"aliyuncli subcommand" subcommand:"true"` +} + +func getSubcommandParser() (*structarg.ArgumentParser, error) { + parse, e := structarg.NewArgumentParser(&BaseOptions{}, + "govmcli", + "Command-line interface to VMware VSphere Webservice API.", + `See "govmcli 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) { + log.Errorf("%s", e) + os.Exit(1) +} + +func newClient(options *BaseOptions) (*esxi.SESXiClient, error) { + if len(options.Host) == 0 { + return nil, fmt.Errorf("Missing host") + } + + if len(options.Account) == 0 { + return nil, fmt.Errorf("Missing account") + } + + if len(options.Password) == 0 { + return nil, fmt.Errorf("Missing password") + } + + return esxi.NewESXiClient("", "", options.Host, options.Port, options.Account, options.Password) +} + +func main() { + parser, e := getSubcommandParser() + if e != nil { + showErrorAndExit(e) + } + e = parser.ParseArgs(os.Args[1:], false) + options := parser.Options().(*BaseOptions) + + if options.Help { + fmt.Print(parser.HelpString()) + } else { + subcmd := parser.GetSubcommand() + subparser := subcmd.GetSubParser() + if e != nil { + if subparser != nil { + fmt.Print(subparser.Usage()) + } else { + fmt.Print(parser.Usage()) + } + showErrorAndExit(e) + } else { + suboptions := subparser.Options() + if options.SUBCOMMAND == "help" { + e = subcmd.Invoke(suboptions) + } else { + var esxicli *esxi.SESXiClient + esxicli, e = newClient(options) + if e != nil { + showErrorAndExit(e) + } + e = subcmd.Invoke(esxicli, suboptions) + } + if e != nil { + showErrorAndExit(e) + } + } + } +} diff --git a/pkg/cloudprovider/consts.go b/pkg/cloudprovider/consts.go index 96076233cf..d8fde2e064 100644 --- a/pkg/cloudprovider/consts.go +++ b/pkg/cloudprovider/consts.go @@ -15,3 +15,5 @@ var ErrNotFound = errors.New("id not found") var ErrDuplicateId = errors.New("duplicate id") var ErrInvalidStatus = errors.New("invalid status") var ErrTimeout = errors.New("timeout") +var ErrNotImplemented = errors.New("Not implemented") +var ErrNotSupported = errors.New("Not supported") diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index c486e55ea6..65bd784eb6 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -130,7 +130,7 @@ type ICloudVM interface { GetEIP() ICloudEIP // GetStatus() string - GetRemoteStatus() string + // GetRemoteStatus() string GetVcpuCount() int8 GetVmemSizeMB() int //MB diff --git a/pkg/compute/guestdrivers/aliyun.go b/pkg/compute/guestdrivers/aliyun.go index caf92f83ec..ef81f0b312 100644 --- a/pkg/compute/guestdrivers/aliyun.go +++ b/pkg/compute/guestdrivers/aliyun.go @@ -240,25 +240,3 @@ func (self *SAliyunGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Contex guest.SaveDeployInfo(ctx, task.GetUserCred(), data) return nil } - -func (self *SAliyunGuestDriver) GetGuestVncInfo(userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) (*jsonutils.JSONDict, error) { - ihost, err := host.GetIHost() - if err != nil { - return nil, err - } - - iVM, err := ihost.GetIVMById(guest.ExternalId) - if err != nil { - log.Errorf("cannot find vm %s %s", iVM, err) - return nil, err - } - - data, err := iVM.GetVNCInfo() - if err != nil { - return nil, err - } - - dataDict := data.(*jsonutils.JSONDict) - - return dataDict, nil -} diff --git a/pkg/compute/guestdrivers/esxi.go b/pkg/compute/guestdrivers/esxi.go index 1eb748d14c..4029074698 100644 --- a/pkg/compute/guestdrivers/esxi.go +++ b/pkg/compute/guestdrivers/esxi.go @@ -1,9 +1,6 @@ package guestdrivers import ( - "github.com/yunionio/jsonutils" - "github.com/yunionio/onecloud/pkg/mcclient" - "github.com/yunionio/onecloud/pkg/compute/models" ) @@ -19,9 +16,3 @@ func init() { func (self *SESXiGuestDriver) GetHypervisor() string { return models.HYPERVISOR_ESXI } - -func (self *SESXiGuestDriver) GetGuestVncInfo(userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) (*jsonutils.JSONDict, error) { - data := jsonutils.NewDict() - // TODO - return data, nil -} diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index 270aa9e74d..fc63c861c7 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -112,6 +112,29 @@ func (self *SManagedVirtualizedGuestDriver) RequestSyncstatusOnHost(ctx context. return nil, err } body := jsonutils.NewDict() - body.Add(jsonutils.NewString(ivm.GetRemoteStatus()), "status") + body.Add(jsonutils.NewString(ivm.GetStatus()), "status") return body, nil } + + +func (self *SManagedVirtualizedGuestDriver) GetGuestVncInfo(userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) (*jsonutils.JSONDict, error) { + ihost, err := host.GetIHost() + if err != nil { + return nil, err + } + + iVM, err := ihost.GetIVMById(guest.ExternalId) + if err != nil { + log.Errorf("cannot find vm %s %s", iVM, err) + return nil, err + } + + data, err := iVM.GetVNCInfo() + if err != nil { + return nil, err + } + + dataDict := data.(*jsonutils.JSONDict) + + return dataDict, nil +} \ No newline at end of file diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 823a79aa53..712ffe014a 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "time" + "database/sql" "github.com/yunionio/jsonutils" "github.com/yunionio/log" @@ -24,8 +25,8 @@ const ( CLOUD_PROVIDER_START_SYNC = "start_sync" CLOUD_PROVIDER_SYNCING = "syncing" - CLOUD_PROVIDER_DRIVER_VMWARE = "VMware" - CLOUD_PROVIDER_DRIVER_ALIYUN = "Aliyun" + CLOUD_PROVIDER_VMWARE = "VMware" + CLOUD_PROVIDER_ALIYUN = "Aliyun" ) type SCloudproviderManager struct { @@ -187,10 +188,9 @@ func (self *SCloudprovider) PerformUpdateCredential(ctx context.Context, userCre q := self.GetModelManager().Query() q = q.Equals("access_url", accessUrl) q = q.Equals("account", account) - q = q.Equals("secret", secret) q = q.NotEquals("id", self.Id) if q.Count() > 0 { - return nil, httperrors.NewConflictError("Access url and account and ") + return nil, httperrors.NewConflictError("Access url and account conflict") } } if len(secret) > 0 { @@ -284,13 +284,6 @@ func (manager *SCloudproviderManager) FetchCloudproviderByIdOrName(providerId st return providerObj.(*SCloudprovider) } -/*func (manager *SCloudproviderManager) GetDriverByManagerId(managerId string) (cloudprovider.ICloudProvider, error) { - provider := manager.FetchCloudproviderById(managerId) - if provider == nil { - return nil, fmt.Errorf("no valid cloud provider") - } - return provider.GetDriver() -}*/ type SCloudproviderUsage struct { HostCount int @@ -339,3 +332,48 @@ func (self *SCloudprovider) GetExtraDetails(ctx context.Context, userCred mcclie extra := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query) return self.getMoreDetails(extra) } + +func (manager *SCloudproviderManager) InitializeData() error { + // move vmware info from vcenter to cloudprovider + vcenters := make([]SVCenter, 0) + q := VCenterManager.Query() + err := db.FetchModelObjects(manager, q, &vcenters) + if err != nil { + return err + } + for _, vc := range vcenters { + _, err := CloudproviderManager.FetchById(vc.Id) + if err != nil { + if err == sql.ErrNoRows { + err = manager.migrateVCenterInfo(&vc) + if err != nil { + log.Errorf("migrateVcenterInfo fail %s", err) + return err + } + } else { + log.Errorf("fetch cloudprovider fail %s", err) + return err + } + } else { + log.Debugf("vcenter info has been migrate into cloudprovider") + } + } + return nil +} + +func (manager *SCloudproviderManager) migrateVCenterInfo(vc *SVCenter) error { + cp := SCloudprovider{} + cp.SetModelManager(manager) + + cp.Id = vc.Id + cp.Name = db.GenerateName(manager, "", vc.Name) + cp.Status = vc.Status + cp.AccessUrl = fmt.Sprintf("https://%s:%d", vc.Hostname, vc.Password) + cp.Account = vc.Account + cp.Secret = vc.Password + cp.LastSync = vc.LastSync + cp.Sysinfo = vc.Sysinfo + cp.Provider = CLOUD_PROVIDER_VMWARE + + return manager.TableSpec().Insert(&cp) +} \ No newline at end of file diff --git a/pkg/compute/models/initdb.go b/pkg/compute/models/initdb.go index c6a401a233..eb1e827d92 100644 --- a/pkg/compute/models/initdb.go +++ b/pkg/compute/models/initdb.go @@ -7,6 +7,7 @@ import ( func InitDB() error { for _, manager := range []db.IModelManager{ + CloudproviderManager, CloudregionManager, ZoneManager, VpcManager, diff --git a/pkg/util/aliyun/aliyun.go b/pkg/util/aliyun/aliyun.go index 30ed66a5e6..d33b2d686b 100644 --- a/pkg/util/aliyun/aliyun.go +++ b/pkg/util/aliyun/aliyun.go @@ -6,10 +6,11 @@ import ( "github.com/yunionio/jsonutils" "github.com/yunionio/log" "github.com/yunionio/onecloud/pkg/cloudprovider" + "github.com/yunionio/onecloud/pkg/compute/models" ) const ( - CLOUD_PROVIDER_ALIYUN = "Aliyun" + CLOUD_PROVIDER_ALIYUN = models.CLOUD_PROVIDER_ALIYUN CLOUD_PROVIDER_ALIYUN_CN = "阿里云" ALIYUN_DEFAULT_REGION = "cn-hangzhou" diff --git a/pkg/util/aliyun/instance.go b/pkg/util/aliyun/instance.go index 31c0e2b17b..a061eb5fcd 100644 --- a/pkg/util/aliyun/instance.go +++ b/pkg/util/aliyun/instance.go @@ -1,16 +1,17 @@ package aliyun import ( - "fmt" "time" + "fmt" "github.com/yunionio/jsonutils" "github.com/yunionio/log" - "github.com/yunionio/onecloud/pkg/cloudprovider" - "github.com/yunionio/onecloud/pkg/compute/models" "github.com/yunionio/pkg/util/osprofile" "github.com/yunionio/pkg/util/seclib" "github.com/yunionio/pkg/utils" + + "github.com/yunionio/onecloud/pkg/cloudprovider" + "github.com/yunionio/onecloud/pkg/compute/models" ) const ( @@ -291,6 +292,7 @@ func (self *SInstance) Refresh() error { return jsonutils.Update(self, new) } +/* func (self *SInstance) GetRemoteStatus() string { // Running:运行中 //Starting:启动中 @@ -309,6 +311,7 @@ func (self *SInstance) GetRemoteStatus() string { return cloudprovider.CloudVMStatusOther } } +*/ func (self *SInstance) GetHypervisor() string { return models.HYPERVISOR_ALIYUN diff --git a/pkg/util/aliyun/shell/disk.go b/pkg/util/aliyun/shell/disk.go index eb6692a88c..85cd78492c 100644 --- a/pkg/util/aliyun/shell/disk.go +++ b/pkg/util/aliyun/shell/disk.go @@ -2,6 +2,7 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -12,7 +13,7 @@ func init() { Offset int `help:"List offset"` Limit int `help:"List limit"` } - R(&DiskListOptions{}, "disk-list", "List disks", func(cli *aliyun.SRegion, args *DiskListOptions) error { + shellutils.R(&DiskListOptions{}, "disk-list", "List disks", func(cli *aliyun.SRegion, args *DiskListOptions) error { disks, total, e := cli.GetDisks(args.Instance, args.Zone, args.Category, nil, args.Offset, args.Limit) if e != nil { return e diff --git a/pkg/util/aliyun/shell/image.go b/pkg/util/aliyun/shell/image.go index b9d8c059db..e339853a58 100644 --- a/pkg/util/aliyun/shell/image.go +++ b/pkg/util/aliyun/shell/image.go @@ -2,6 +2,7 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -13,7 +14,7 @@ func init() { Limit int `help:"page size"` Offset int `help:"page offset"` } - R(&ImageListOptions{}, "image-list", "List images", func(cli *aliyun.SRegion, args *ImageListOptions) error { + shellutils.R(&ImageListOptions{}, "image-list", "List images", func(cli *aliyun.SRegion, args *ImageListOptions) error { images, total, e := cli.GetImages(aliyun.ImageStatusType(args.Status), aliyun.ImageOwnerType(args.Owner), args.Id, args.Name, args.Offset, args.Limit) if e != nil { return e @@ -25,7 +26,7 @@ func init() { type ImageDeleteOptions struct { ID string `help:"ID or Name to delete"` } - R(&ImageDeleteOptions{}, "image-delete", "Delete image", func(cli *aliyun.SRegion, args *ImageDeleteOptions) error { + shellutils.R(&ImageDeleteOptions{}, "image-delete", "Delete image", func(cli *aliyun.SRegion, args *ImageDeleteOptions) error { return cli.DeleteImage(args.ID) }) } diff --git a/pkg/util/aliyun/shell/instance.go b/pkg/util/aliyun/shell/instance.go index 181e078d8c..d57dfa7231 100644 --- a/pkg/util/aliyun/shell/instance.go +++ b/pkg/util/aliyun/shell/instance.go @@ -3,6 +3,7 @@ package shell import ( "fmt" "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -12,7 +13,7 @@ func init() { Limit int `help:"page size"` Offset int `help:"page offset"` } - R(&InstanceListOptions{}, "instance-list", "List intances", func(cli *aliyun.SRegion, args *InstanceListOptions) error { + shellutils.R(&InstanceListOptions{}, "instance-list", "List intances", func(cli *aliyun.SRegion, args *InstanceListOptions) error { instances, total, e := cli.GetInstances(args.Zone, args.Id, args.Offset, args.Limit) if e != nil { return e @@ -32,7 +33,7 @@ func init() { PASSWD string `help:"password"` PublicKey string `help:"PublicKey"` } - R(&InstanceCrateOptions{}, "instance-create", "Create a instance", func(cli *aliyun.SRegion, args *InstanceCrateOptions) error { + shellutils.R(&InstanceCrateOptions{}, "instance-create", "Create a instance", func(cli *aliyun.SRegion, args *InstanceCrateOptions) error { instance, e := cli.CreateInstanceSimple(args.NAME, args.IMAGE, args.CPU, args.MEMORYGB, args.STORAGE, args.Disk, args.VSWITCH, args.PASSWD, args.PublicKey) if e != nil { return e @@ -44,7 +45,7 @@ func init() { type InstanceOperationOptions struct { ID string `help:"instance ID"` } - R(&InstanceOperationOptions{}, "instance-start", "Start a instance", func(cli *aliyun.SRegion, args *InstanceOperationOptions) error { + shellutils.R(&InstanceOperationOptions{}, "instance-start", "Start a instance", func(cli *aliyun.SRegion, args *InstanceOperationOptions) error { err := cli.StartVM(args.ID) if err != nil { return err @@ -52,7 +53,7 @@ func init() { return nil }) - R(&InstanceOperationOptions{}, "instance-vnc", "Get a instance VNC url", func(cli *aliyun.SRegion, args *InstanceOperationOptions) error { + shellutils.R(&InstanceOperationOptions{}, "instance-vnc", "Get a instance VNC url", func(cli *aliyun.SRegion, args *InstanceOperationOptions) error { url, err := cli.GetInstanceVNCUrl(args.ID) if err != nil { return err @@ -65,14 +66,14 @@ func init() { ID string `help:"instance ID"` Force bool `help:"Force stop instance"` } - R(&InstanceStopOptions{}, "instance-stop", "Stop a instance", func(cli *aliyun.SRegion, args *InstanceStopOptions) error { + shellutils.R(&InstanceStopOptions{}, "instance-stop", "Stop a instance", func(cli *aliyun.SRegion, args *InstanceStopOptions) error { err := cli.StopVM(args.ID, args.Force) if err != nil { return err } return nil }) - R(&InstanceOperationOptions{}, "instance-delete", "Delete a instance", func(cli *aliyun.SRegion, args *InstanceOperationOptions) error { + shellutils.R(&InstanceOperationOptions{}, "instance-delete", "Delete a instance", func(cli *aliyun.SRegion, args *InstanceOperationOptions) error { err := cli.DeleteVM(args.ID) if err != nil { return err diff --git a/pkg/util/aliyun/shell/instancetype.go b/pkg/util/aliyun/shell/instancetype.go index 3861e38d5a..a127e95e2c 100644 --- a/pkg/util/aliyun/shell/instancetype.go +++ b/pkg/util/aliyun/shell/instancetype.go @@ -2,12 +2,13 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { type InstanceTypeListOptions struct { } - R(&InstanceTypeListOptions{}, "instance-type-list", "List intance types", func(cli *aliyun.SRegion, args *InstanceTypeListOptions) error { + shellutils.R(&InstanceTypeListOptions{}, "instance-type-list", "List intance types", func(cli *aliyun.SRegion, args *InstanceTypeListOptions) error { instanceTypes, e := cli.GetInstanceTypes() if e != nil { return e @@ -22,7 +23,7 @@ func init() { GPU int `help:"GPU size"` Zone string `help:"Test in zone"` } - R(&InstanceMatchOptions{}, "instance-type-select", "Select matching instance types", func(cli *aliyun.SRegion, args *InstanceMatchOptions) error { + shellutils.R(&InstanceMatchOptions{}, "instance-type-select", "Select matching instance types", func(cli *aliyun.SRegion, args *InstanceMatchOptions) error { instanceTypes, e := cli.GetMatchInstanceTypes(args.CPU, args.MEM, args.GPU, args.Zone) if e != nil { return e diff --git a/pkg/util/aliyun/shell/keypair.go b/pkg/util/aliyun/shell/keypair.go index 2429855535..b35c79e5c1 100644 --- a/pkg/util/aliyun/shell/keypair.go +++ b/pkg/util/aliyun/shell/keypair.go @@ -2,6 +2,7 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -9,7 +10,7 @@ func init() { Limit int `help:"page size"` Offset int `help:"page offset"` } - R(&KeyPairListOptions{}, "keypair-list", "List keypairs", func(cli *aliyun.SRegion, args *KeyPairListOptions) error { + shellutils.R(&KeyPairListOptions{}, "keypair-list", "List keypairs", func(cli *aliyun.SRegion, args *KeyPairListOptions) error { keypairs, total, e := cli.GetKeypairs("", "", args.Offset, args.Limit) if e != nil { return e @@ -22,7 +23,7 @@ func init() { NAME string `help:"Name of new keypair"` PUBKEY string `help:"Public key string"` } - R(&KeyPairImportOptions{}, "keypair-import", "Import a keypair", func(cli *aliyun.SRegion, args *KeyPairImportOptions) error { + shellutils.R(&KeyPairImportOptions{}, "keypair-import", "Import a keypair", func(cli *aliyun.SRegion, args *KeyPairImportOptions) error { keypair, err := cli.ImportKeypair(args.NAME, args.PUBKEY) if err != nil { return err diff --git a/pkg/util/aliyun/shell/oss.go b/pkg/util/aliyun/shell/oss.go index 6f04073fc8..5b40d5c8a7 100644 --- a/pkg/util/aliyun/shell/oss.go +++ b/pkg/util/aliyun/shell/oss.go @@ -6,6 +6,7 @@ import ( osslib "github.com/aliyun/aliyun-oss-go-sdk/oss" "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) type progressListener struct { @@ -40,7 +41,7 @@ func str2AclType(aclStr string) osslib.ACLType { func init() { type OssListOptions struct { } - R(&OssListOptions{}, "oss-list", "List OSS buckets", func(cli *aliyun.SRegion, args *OssListOptions) error { + shellutils.R(&OssListOptions{}, "oss-list", "List OSS buckets", func(cli *aliyun.SRegion, args *OssListOptions) error { oss, err := cli.GetOssClient() if err != nil { return err @@ -57,7 +58,7 @@ func init() { BUCKET string `help:"bucket name"` } - R(&OssListBucketOptions{}, "oss-list-bucket", "List content of a OSS bucket", func(cli *aliyun.SRegion, args *OssListBucketOptions) error { + shellutils.R(&OssListBucketOptions{}, "oss-list-bucket", "List content of a OSS bucket", func(cli *aliyun.SRegion, args *OssListBucketOptions) error { oss, err := cli.GetOssClient() if err != nil { return err @@ -74,7 +75,7 @@ func init() { return nil }) - R(&OssListBucketOptions{}, "oss-create-bucket", "Create a OSS bucket", func(cli *aliyun.SRegion, args *OssListBucketOptions) error { + shellutils.R(&OssListBucketOptions{}, "oss-create-bucket", "Create a OSS bucket", func(cli *aliyun.SRegion, args *OssListBucketOptions) error { oss, err := cli.GetOssClient() if err != nil { return err @@ -93,7 +94,7 @@ func init() { Progress bool `help:"show progress"` Acl string `help:"Object ACL" choices:"private|public-read|public-rw"` } - R(&OssUploadOptions{}, "oss-upload", "Upload a file to a OSS bucket", func(cli *aliyun.SRegion, args *OssUploadOptions) error { + shellutils.R(&OssUploadOptions{}, "oss-upload", "Upload a file to a OSS bucket", func(cli *aliyun.SRegion, args *OssUploadOptions) error { oss, err := cli.GetOssClient() if err != nil { return err @@ -120,7 +121,7 @@ func init() { KEY string `help:"object key"` ACL string `help:"ACL" choices:"private|public-read|public-rw"` } - R(&OssObjectAclOptions{}, "oss-set-acl", "Set acl for a object", func(cli *aliyun.SRegion, args *OssObjectAclOptions) error { + shellutils.R(&OssObjectAclOptions{}, "oss-set-acl", "Set acl for a object", func(cli *aliyun.SRegion, args *OssObjectAclOptions) error { oss, err := cli.GetOssClient() if err != nil { return err @@ -138,7 +139,7 @@ func init() { KEY string `help:"Object key"` } - R(&OssDeleteOptions{}, "oss-delete", "Delete a file from a OSS bucket", func(cli *aliyun.SRegion, args *OssDeleteOptions) error { + shellutils.R(&OssDeleteOptions{}, "oss-delete", "Delete a file from a OSS bucket", func(cli *aliyun.SRegion, args *OssDeleteOptions) error { oss, err := cli.GetOssClient() if err != nil { return err diff --git a/pkg/util/aliyun/shell/printutils.go b/pkg/util/aliyun/shell/printutils.go new file mode 100644 index 0000000000..26996d39f3 --- /dev/null +++ b/pkg/util/aliyun/shell/printutils.go @@ -0,0 +1,11 @@ +package shell + +import "github.com/yunionio/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) +} \ No newline at end of file diff --git a/pkg/util/aliyun/shell/region.go b/pkg/util/aliyun/shell/region.go index ca5c02cedf..c1def72da8 100644 --- a/pkg/util/aliyun/shell/region.go +++ b/pkg/util/aliyun/shell/region.go @@ -2,12 +2,13 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { type RegionListOptions struct { } - R(&RegionListOptions{}, "region-list", "List regions", func(cli *aliyun.SRegion, args *RegionListOptions) error { + shellutils.R(&RegionListOptions{}, "region-list", "List regions", func(cli *aliyun.SRegion, args *RegionListOptions) error { regions := cli.GetClient().GetRegions() printList(regions, 0, 0, 0, nil) return nil diff --git a/pkg/util/aliyun/shell/routetable.go b/pkg/util/aliyun/shell/routetable.go index c883c19206..6cfaa25a00 100644 --- a/pkg/util/aliyun/shell/routetable.go +++ b/pkg/util/aliyun/shell/routetable.go @@ -3,6 +3,7 @@ package shell import ( "fmt" "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -10,7 +11,7 @@ func init() { Limit int `help:"page size"` Offset int `help:"page offset"` } - R(&RouteTableListOptions{}, "routetable-list", "List routetables", func(cli *aliyun.SRegion, args *RouteTableListOptions) error { + shellutils.R(&RouteTableListOptions{}, "routetable-list", "List routetables", func(cli *aliyun.SRegion, args *RouteTableListOptions) error { routetables, total, e := cli.GetRouteTables(nil, args.Offset, args.Limit) if e != nil { return e @@ -22,7 +23,7 @@ func init() { type RouteTableShowOptions struct { ID string `help:"ID or name of routetable"` } - R(&RouteTableShowOptions{}, "routetable-show", "Show routetable", func(cli *aliyun.SRegion, args *RouteTableShowOptions) error { + shellutils.R(&RouteTableShowOptions{}, "routetable-show", "Show routetable", func(cli *aliyun.SRegion, args *RouteTableShowOptions) error { routetables, _, e := cli.GetRouteTables([]string{args.ID}, 0, 1) if e != nil { return e diff --git a/pkg/util/aliyun/shell/secgroup.go b/pkg/util/aliyun/shell/secgroup.go index 4ea5c42ad0..0442e91a21 100644 --- a/pkg/util/aliyun/shell/secgroup.go +++ b/pkg/util/aliyun/shell/secgroup.go @@ -2,6 +2,7 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -10,7 +11,7 @@ func init() { Limit int `help:"page size"` Offset int `help:"page offset"` } - R(&SecurityGroupListOptions{}, "security-group-list", "List security group", func(cli *aliyun.SRegion, args *SecurityGroupListOptions) error { + shellutils.R(&SecurityGroupListOptions{}, "security-group-list", "List security group", func(cli *aliyun.SRegion, args *SecurityGroupListOptions) error { secgrps, total, e := cli.GetSecurityGroups(args.VpcId, args.Offset, args.Limit) if e != nil { return e @@ -22,7 +23,7 @@ func init() { type SecurityGroupShowOptions struct { ID string `help:"ID or name of security group"` } - R(&SecurityGroupShowOptions{}, "security-group-show", "Show details of a security group", func(cli *aliyun.SRegion, args *SecurityGroupShowOptions) error { + shellutils.R(&SecurityGroupShowOptions{}, "security-group-show", "Show details of a security group", func(cli *aliyun.SRegion, args *SecurityGroupShowOptions) error { secgrp, err := cli.GetSecurityGroupDetails(args.ID) if err != nil { return err diff --git a/pkg/util/aliyun/shell/task.go b/pkg/util/aliyun/shell/task.go index fe00e5d4f2..754d4e8bba 100644 --- a/pkg/util/aliyun/shell/task.go +++ b/pkg/util/aliyun/shell/task.go @@ -1,6 +1,9 @@ package shell -import "github.com/yunionio/onecloud/pkg/util/aliyun" +import ( + "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" +) func init() { type TaskListOptions struct { @@ -9,7 +12,7 @@ func init() { Limit int `help:"page size"` Offset int `help:"page offset"` } - R(&TaskListOptions{}, "task-list", "List tasks", func(cli *aliyun.SRegion, args *TaskListOptions) error { + shellutils.R(&TaskListOptions{}, "task-list", "List tasks", func(cli *aliyun.SRegion, args *TaskListOptions) error { tasks, total, err := cli.GetTasks(aliyun.TaskActionType(args.TYPE), args.Task, args.Offset, args.Limit) if err != nil { return err diff --git a/pkg/util/aliyun/shell/vpc.go b/pkg/util/aliyun/shell/vpc.go index 21b8a3aa46..3146e1b653 100644 --- a/pkg/util/aliyun/shell/vpc.go +++ b/pkg/util/aliyun/shell/vpc.go @@ -2,6 +2,7 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -9,7 +10,7 @@ func init() { Limit int `help:"page size"` Offset int `help:"page offset"` } - R(&VpcListOptions{}, "vpc-list", "List vpcs", func(cli *aliyun.SRegion, args *VpcListOptions) error { + shellutils.R(&VpcListOptions{}, "vpc-list", "List vpcs", func(cli *aliyun.SRegion, args *VpcListOptions) error { vpcs, total, e := cli.GetVpcs(nil, args.Offset, args.Limit) if e != nil { return e diff --git a/pkg/util/aliyun/shell/vrouter.go b/pkg/util/aliyun/shell/vrouter.go index e16f70ee26..ef38c07da1 100644 --- a/pkg/util/aliyun/shell/vrouter.go +++ b/pkg/util/aliyun/shell/vrouter.go @@ -2,6 +2,7 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -9,7 +10,7 @@ func init() { Limit int `help:"page size"` Offset int `help:"page offset"` } - R(&VRouterListOptions{}, "vrouter-list", "List vrouters", func(cli *aliyun.SRegion, args *VRouterListOptions) error { + shellutils.R(&VRouterListOptions{}, "vrouter-list", "List vrouters", func(cli *aliyun.SRegion, args *VRouterListOptions) error { vrouters, total, e := cli.GetVRouters(args.Offset, args.Limit) if e != nil { return e diff --git a/pkg/util/aliyun/shell/vswitch.go b/pkg/util/aliyun/shell/vswitch.go index 04216c8564..974a569e88 100644 --- a/pkg/util/aliyun/shell/vswitch.go +++ b/pkg/util/aliyun/shell/vswitch.go @@ -2,6 +2,7 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -9,7 +10,7 @@ func init() { Limit int `help:"page size"` Offset int `help:"page offset"` } - R(&VSwitchListOptions{}, "vswitch-list", "List vswitches", func(cli *aliyun.SRegion, args *VSwitchListOptions) error { + shellutils.R(&VSwitchListOptions{}, "vswitch-list", "List vswitches", func(cli *aliyun.SRegion, args *VSwitchListOptions) error { vswitches, total, e := cli.GetVSwitches(nil, "", args.Offset, args.Limit) if e != nil { return e diff --git a/pkg/util/aliyun/shell/zone.go b/pkg/util/aliyun/shell/zone.go index 1cd8913a5d..c1bc333d10 100644 --- a/pkg/util/aliyun/shell/zone.go +++ b/pkg/util/aliyun/shell/zone.go @@ -2,6 +2,7 @@ package shell import ( "github.com/yunionio/onecloud/pkg/util/aliyun" + "github.com/yunionio/onecloud/pkg/util/shellutils" ) func init() { @@ -10,7 +11,7 @@ func init() { // ChargeType string `help:"charge type" choices:"PrePaid|PostPaid" default:"PrePaid"` // SpotStrategy string `help:"Spot strategy, NoSpot|SpotWithPriceLimit|SpotAsPriceGo" choices:"NoSpot|SpotWithPriceLimit|SpotAsPriceGo" default:"NoSpot"` } - R(&ZoneListOptions{}, "zone-list", "List zones", func(cli *aliyun.SRegion, args *ZoneListOptions) error { + shellutils.R(&ZoneListOptions{}, "zone-list", "List zones", func(cli *aliyun.SRegion, args *ZoneListOptions) error { zones, e := cli.GetIZones() if e != nil { return e diff --git a/pkg/util/esxi/datacenter.go b/pkg/util/esxi/datacenter.go new file mode 100644 index 0000000000..05a6f9e032 --- /dev/null +++ b/pkg/util/esxi/datacenter.go @@ -0,0 +1,78 @@ +package esxi + +import ( + "github.com/vmware/govmomi/vim25/mo" + "github.com/yunionio/onecloud/pkg/cloudprovider" +) + +var DATACENTER_PROPS = []string {"name", "parent", "datastore"} + +type SDatacenter struct { + SManagedObject + + ihosts []cloudprovider.ICloudHost + istorages []cloudprovider.ICloudStorage + + Name string +} + +func newDatacenter(manager *SESXiClient, dc *mo.Datacenter) *SDatacenter { + obj := SDatacenter{SManagedObject: newManagedObject(manager, dc, nil)} + obj.datacenter = &obj + return &obj +} + +func (dc *SDatacenter) getDatacenter() *mo.Datacenter { + return dc.object.(*mo.Datacenter) +} + +func (dc *SDatacenter) scanHosts() error { + if dc.ihosts == nil { + var hosts []mo.HostSystem + err := dc.manager.scanMObjects(dc.object.Entity().Self, HOST_SYSTEM_PROPS, &hosts) + if err != nil { + return err + } + dc.ihosts = make([]cloudprovider.ICloudHost, len(hosts)) + for i := 0; i < len(hosts); i += 1 { + dc.ihosts[i] = NewHost(dc.manager, &hosts[i], dc) + } + } + return nil +} + +func (dc *SDatacenter) GetIHosts() ([]cloudprovider.ICloudHost, error) { + err := dc.scanHosts() + if err != nil { + return nil, err + } + return dc.ihosts, nil +} + +func (dc *SDatacenter) scanDatastores() error { + if dc.istorages == nil { + stores := make([]mo.Datastore, 0) + dsList := dc.getDatacenter().Datastore + for i := 0; i < len(dsList); i += 1 { + var ds mo.Datastore + err := dc.manager.reference2Object(dsList[i], DATASTORE_PROPS, &ds) + if err != nil { + return err + } + stores = append(stores, ds) + } + dc.istorages = make([]cloudprovider.ICloudStorage, len(stores)) + for i := 0; i < len(stores); i += 1 { + dc.istorages[i] = NewDatastore(dc.manager, &stores[i], dc) + } + } + return nil +} + +func (dc *SDatacenter) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + err := dc.scanDatastores() + if err != nil { + return nil, err + } + return dc.istorages, nil +} \ No newline at end of file diff --git a/pkg/util/esxi/host.go b/pkg/util/esxi/host.go new file mode 100644 index 0000000000..fd6d135fc3 --- /dev/null +++ b/pkg/util/esxi/host.go @@ -0,0 +1,360 @@ +package esxi + +import ( + "github.com/vmware/govmomi/vim25/mo" + + "github.com/yunionio/jsonutils" + + "github.com/yunionio/onecloud/pkg/compute/models" + "github.com/yunionio/onecloud/pkg/cloudprovider" + "github.com/vmware/govmomi/vim25/types" + "github.com/yunionio/pkg/util/netutils" + "github.com/yunionio/log" +) + +var HOST_SYSTEM_PROPS = []string {"name", "parent", "summary", "config", "hardware", "vm"} + +type SHostNicInfo struct { + Dev string + Driver string + Mac string + Index int + LinkUp bool + IpAddr string + Mtu int + NicType string +} + +type SHostStorageAdapterInfo struct { + Device string + Model string + Driver string + Pci string + Drivers []SHostStorageDriverInfo + Enclosure int +} + +type SHostStorageDriverInfo struct { + CN string + Name string + Model string + Vendor string + Revision string + Status string + SSD bool + Dev string + Size int +} + +type SHostStorageEnclosureInfo struct { + CN string + Name string + Model string + Vendor string + Revision string + Status string +} + +type SHost struct { + SManagedObject + + nicInfo []SHostNicInfo + storageInfo []SHostStorageAdapterInfo + + vms []cloudprovider.ICloudVM +} + +func NewHost(manager *SESXiClient, host *mo.HostSystem, dc *SDatacenter) *SHost { + return &SHost{SManagedObject: newManagedObject(manager, host, dc)} +} + +func (self *SHost) getHostSystem() *mo.HostSystem { + return self.object.(*mo.HostSystem) +} + + +func (self *SHost) GetGlobalId() string { + return self.GetAccessIp() +} + +func (self *SHost) GetStatus() string { + /* + HostSystemPowerStatePoweredOn = HostSystemPowerState("poweredOn") + HostSystemPowerStatePoweredOff = HostSystemPowerState("poweredOff") + HostSystemPowerStateStandBy = HostSystemPowerState("standBy") + HostSystemPowerStateUnknown = HostSystemPowerState("unknown") + */ + switch self.getHostSystem().Summary.Runtime.PowerState { + case types.HostSystemPowerStatePoweredOn: + return models.HOST_STATUS_RUNNING + case types.HostSystemPowerStatePoweredOff: + return models.HOST_STATUS_READY + default: + return models.HOST_STATUS_UNKNOWN + } +} + +func (self *SHost) Refresh() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SHost) IsEmulated() bool { + return false +} + +func (self *SHost) fetchVMs() error { + if self.vms != nil { + return nil + } + var vms []mo.VirtualMachine + err := self.manager.references2Objects(self.getHostSystem().Vm, VIRTUAL_MACHINE_PROPS, &vms) + if err != nil { + return err + } + + dc, err := self.GetDatacenter() + if err != nil { + return err + } + + self.vms = make([]cloudprovider.ICloudVM, len(vms)) + for i := 0; i < len(vms); i += 1 { + self.vms[i] = NewVirtualMachine(self.manager, &vms[i], dc, self) + } + return nil +} + +func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) { + err := self.fetchVMs() + if err != nil { + return nil, err + } + return self.vms, nil +} + +func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) { + vms, err := self.GetIVMs() + if err != nil { + return nil, err + } + for i := 0; i < len(vms); i += 1 { + if vms[i].GetGlobalId() == id { + return vms[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SHost) GetEnabled() bool { + return true +} + +func (self *SHost) GetHostStatus() string { + /* + HostSystemConnectionStateConnected = HostSystemConnectionState("connected") + HostSystemConnectionStateNotResponding = HostSystemConnectionState("notResponding") + HostSystemConnectionStateDisconnected = HostSystemConnectionState("disconnected") + */ + switch self.getHostSystem().Summary.Runtime.ConnectionState { + case types.HostSystemConnectionStateConnected: + return models.HOST_ONLINE + default: + return models.HOST_OFFLINE + } +} + + +func findHostNicByMac(nicInfoList []SHostNicInfo, mac string) *SHostNicInfo { + for i := 0; i < len(nicInfoList); i += 1 { + if nicInfoList[i].Mac == mac { + return &nicInfoList[i] + } + } + return nil +} + +func (self *SHost) getAdminNic() *SHostNicInfo { + nics := self.getNicInfo() + for i := 0; i < len(nics); i += 1 { + if nics[i].NicType == models.NIC_TYPE_ADMIN { + return &nics[i] + } + } + for i := 0; i < len(nics); i += 1 { + if len(nics[i].IpAddr) > 0 { + return &nics[i] + } + } + return nil +} + +func (self *SHost) getNicInfo() []SHostNicInfo { + if self.nicInfo == nil { + self.nicInfo = self.fetchNicInfo() + } + return self.nicInfo +} + +func (self *SHost) fetchNicInfo() []SHostNicInfo { + moHost := self.getHostSystem() + + nicInfoList := make([]SHostNicInfo, 0) + + for i, nic := range moHost.Config.Network.Pnic { + info := SHostNicInfo{} + info.Dev = nic.Device + info.Driver = nic.Driver + info.Mac = netutils.FormatMacAddr(nic.Mac) + info.Index = i + info.LinkUp = false + nicInfoList = append(nicInfoList, info) + } + + for _, nic := range moHost.Config.Network.Vnic { + mac := netutils.FormatMacAddr(nic.Spec.Mac) + pnic := findHostNicByMac(nicInfoList, mac) + if pnic != nil { + pnic.IpAddr = nic.Spec.Ip.IpAddress + if nic.Spec.Portgroup == "Management Network" { + pnic.NicType = models.NIC_TYPE_ADMIN + } + pnic.LinkUp = true + } + } + + return nicInfoList +} + +func (self *SHost) GetAccessIp() string { + adminNic := self.getAdminNic() + if adminNic != nil { + return adminNic.IpAddr + } + return "" +} + +func (self *SHost) GetAccessMac() string { + adminNic := self.getAdminNic() + if adminNic != nil { + return adminNic.Mac + } + return "" +} + +type SSysInfo struct { + Manufacture string + Model string + SerialNumber string +} + +func (self *SHost) GetSysInfo() jsonutils.JSONObject { + sysinfo := SSysInfo{} + sysinfo.Manufacture = self.getHostSystem().Summary.Hardware.Vendor + sysinfo.Model = self.getHostSystem().Summary.Hardware.Model + sysinfo.SerialNumber = self.getHostSystem().Hardware.SystemInfo.SerialNumber + return jsonutils.Marshal(&sysinfo) +} + +func (self *SHost) GetSN() string { + return self.getHostSystem().Hardware.SystemInfo.SerialNumber +} + +func (self *SHost) GetCpuCount() int8 { + return int8(self.getHostSystem().Summary.Hardware.NumCpuThreads) +} + +func (self *SHost) GetNodeCount() int8 { + return int8(self.getHostSystem().Summary.Hardware.NumCpuPkgs) +} + +func (self *SHost) GetCpuDesc() string { + return self.getHostSystem().Summary.Hardware.CpuModel +} + +func (self *SHost) GetCpuMhz() int { + return int(self.getHostSystem().Summary.Hardware.CpuMhz) +} + +func (self *SHost) GetMemSizeMB() int { + return int(self.getHostSystem().Summary.Hardware.MemorySize/1024/1024) +} + +/*func (self *SHost) fetchStorageInfo() { + adapterList := make([]SHostStorageAdapterInfo, 0) + driversTable := make(map[string]SHostStorageDriverInfo, 0) + enclosuresTable := make(map[string]SHostStorageEnclosureInfo, 0) + moHost := self.getHostSystem() + + for i, ad := range moHost.Config.StorageDevice.HostBusAdapter { + adinfo := ad.GetHostHostBusAdapter() + if adinfo == nil { + log.Errorf("Fail to GetHostHostBusAdapter") + continue + } + info := SHostStorageAdapterInfo{} + info.Device = adinfo.Device + info.Model = adinfo.Model + info.Driver = adinfo.Driver + info.Pci = adinfo.Pci + info.Drivers = make([]SHostStorageDriverInfo, 0) + info.Enclosure = -1 + adapterList = append(adapterList, info) + } + + for i, drv := range moHost.Config.StorageDevice.ScsiLun { + lunInfo := drv.GetScsiLun() + if lunInfo == nil { + log.Errorf("fail to GetScsiLun") + continue + } + if lunInfo.DeviceType == "disk" { + info := SHostStorageDriverInfo{} + info.CN = lunInfo.CanonicalName + info.Name = lunInfo.DisplayName + info.Model = lunInfo.Model + info.Vendor = lunInfo.Vendor + info.Revision = lunInfo.Revision + info.Status = lunInfo.OperationalState[0] + // info.SSD = lunInfo. + // info.Dev = + // info.Size = lunInfo.S + } else if lunInfo.DeviceType == "enclosure" { + + } + } +} +*/ + +func (self *SHost) GetStorageSizeMB() int { + return 0 +} + +func (self *SHost) GetStorageType() string { + return "" +} + +func (self *SHost) GetHostType() string { + return models.HOST_TYPE_ESXI +} + +func (self *SHost) GetManagerId() string { + return self.manager.providerId +} + +func (self *SHost) CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int, vswitchId string, ipAddr string, desc string, +passwd string, storageType string, diskSizes []int, publicKey string) (cloudprovider.ICloudVM, error) { + log.Debugf("CreateVM") + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/esxi/manager.go b/pkg/util/esxi/manager.go new file mode 100644 index 0000000000..b7122e3f70 --- /dev/null +++ b/pkg/util/esxi/manager.go @@ -0,0 +1,226 @@ +package esxi + +import ( + "context" + "fmt" + "net/url" + "reflect" + + "github.com/vmware/govmomi" + "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" + "github.com/vmware/govmomi/property" + "github.com/vmware/govmomi/object" + + "github.com/yunionio/jsonutils" + "github.com/yunionio/log" + + "github.com/yunionio/onecloud/pkg/cloudprovider" + "github.com/vmware/govmomi/session" + "github.com/yunionio/onecloud/pkg/compute/models" +) + +const ( + CLOUD_PROVIDER_VMWARE = models.CLOUD_PROVIDER_VMWARE +) + +type SESXiClient struct { + providerId string + providerName string + host string + port int + account string + password string + client *govmomi.Client + context context.Context + + datacenters []*SDatacenter +} + +func NewESXiClient(providerId string, providerName string, host string, port int, account string, passwd string) (*SESXiClient, error) { + cli := &SESXiClient{providerId: providerId, providerName: providerName, + host: host, port: port, account: account, password: passwd, context: context.Background()} + + err := cli.connect() + if err != nil { + return nil, err + } + return cli, nil +} + +func (cli *SESXiClient) url() string { + if cli.port == 443 || cli.port == 0 { + return fmt.Sprintf("https://%s/sdk", cli.host) + } else { + return fmt.Sprintf("https://%s:%d/sdk", cli.host, cli.port) + } +} + +func (cli *SESXiClient) connect() error { + u, err := url.Parse(cli.url()) + if err != nil { + return fmt.Errorf("Illegal url %s: %s", cli.url(), err) + } + + govmcli, err := govmomi.NewClient(cli.context, u, true) + if err != nil { + return err + } + + userinfo := url.UserPassword(cli.account, cli.password) + + err = govmcli.Login(cli.context, userinfo) + if err != nil { + return err + } + + cli.client = govmcli + + return nil +} + +func (cli *SESXiClient) disconnect() error { + if cli.client != nil { + return cli.client.Logout(cli.context) + } + return nil +} + +func (cli *SESXiClient) About() jsonutils.JSONObject { + return jsonutils.Marshal(&cli.client.ServiceContent.About) +} + +func (cli *SESXiClient) GetUUID() string { + about := cli.client.ServiceContent.About + return about.InstanceUuid +} + +func (cli *SESXiClient) fetchDatacenters() error { + var dcs []mo.Datacenter + err := cli.scanAllMObjects(DATACENTER_PROPS, &dcs) + if err != nil { + return err + } + cli.datacenters = make([]*SDatacenter, len(dcs)) + for i := 0; i < len(dcs); i += 1 { + cli.datacenters[i] = newDatacenter(cli, &dcs[i]) + } + return nil +} + +func (cli *SESXiClient) scanAllMObjects(props []string, dst interface{}) error { + return cli.scanMObjects(cli.client.ServiceContent.RootFolder, props, dst) +} + +func (cli *SESXiClient) scanMObjects(folder types.ManagedObjectReference, props []string, dst interface{}) error { + dstValue := reflect.Indirect(reflect.ValueOf(dst)) + dstType := dstValue.Type() + dstEleType := dstType.Elem() + + resType := dstEleType.Name() + m := view.NewManager(cli.client.Client) + + v, err := m.CreateContainerView(cli.context, folder, []string{resType}, true) + if err != nil { + log.Fatalf("%s", err) + return err + } + + defer v.Destroy(cli.context) + + err = v.Retrieve(cli.context, []string{resType}, props, dst) + if err != nil { + log.Fatalf("%s", err) + return err + } + + return nil +} + +/* +func getStructFields(dst interface{}) []string { + dataValue := reflect.Indirect(reflect.ValueOf(dst)) + dataType := dataValue.Type() + if dataType.Kind() != reflect.Struct { + log.Warningf("GetStructFeilds for non-struct data") + return nil + } + return _getStructFields(dataType) +} + +func _getStructFields(dataType reflect.Type) []string { + ret := make([]string, 0) + for i := 1; i < dataType.NumField(); i += 1 { + field := dataType.Field(i) + if field.Type.Kind() == reflect.Struct && field.Anonymous { + subfields := _getStructFields(field.Type) + ret = append(ret, subfields...) + } else if gotypes.IsFieldExportable(field.Name) { + log.Debugf("%s: %s", field.Name, field.Type.Name()) + ret = append(ret, utils.CamelSplit(field.Name, "_")) + } + } + return ret +} +*/ + +func (cli *SESXiClient) references2Objects(refs []types.ManagedObjectReference, props []string, dst interface{}) error { + pc := property.DefaultCollector(cli.client.Client) + err := pc.Retrieve(cli.context, refs, []string{"name", "config", "summary"}, dst) + if err != nil { + return err + } + return nil +} + +func (cli *SESXiClient) reference2Object(ref types.ManagedObjectReference, props []string, dst interface{}) error { + pc := property.DefaultCollector(cli.client.Client) + return pc.RetrieveOne(cli.context, ref, props, dst) +} + +func (cli *SESXiClient) GetDatacenters() ([]*SDatacenter, error) { + if cli.datacenters == nil { + err := cli.fetchDatacenters() + if err != nil { + return nil, err + } + } + return cli.datacenters, nil +} + +func (cli *SESXiClient) FindDatacenterById(dcId string) (*SDatacenter, error) { + dcs, err := cli.GetDatacenters() + if err != nil { + return nil, err + } + for i := 0; i < len(dcs); i += 1 { + if dcs[i].GetId() == dcId { + return dcs[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (cli *SESXiClient) FindHostByIp(hostIp string) (*SHost, error) { + searchIndex := object.NewSearchIndex(cli.client.Client) + hostRef, err := searchIndex.FindByIp(cli.context, nil, hostIp, false) + if err != nil { + log.Errorf("searchIndex.FindByIp fail %s", err) + return nil, err + } + + var host mo.HostSystem + err = cli.reference2Object(hostRef.Reference(), HOST_SYSTEM_PROPS, &host) + if err != nil { + log.Errorf("reference2Object fail %s", err) + return nil, err + } + + return NewHost(cli, &host, nil), nil +} + +func (cli *SESXiClient) acquireCloneTicket() (string, error) { + manager := session.NewManager(cli.client.Client) + return manager.AcquireCloneTicket(cli.context) +} \ No newline at end of file diff --git a/pkg/util/esxi/manager_test.go b/pkg/util/esxi/manager_test.go new file mode 100644 index 0000000000..56ba124a54 --- /dev/null +++ b/pkg/util/esxi/manager_test.go @@ -0,0 +1,22 @@ +package esxi + +import "testing" + +func TestNewClient(t *testing.T) { + cli, err := NewESXiClient("", "", "10.168.222.104", 443, "root", "123@Vmware") + + if err != nil { + t.Errorf("%s", err) + } else { + t.Logf("%s", cli.About()) + + cli.fetchDatacenters() + + host, err := cli.FindHostByIp("10.168.222.104") + if err != nil { + t.Errorf("find_host_by_ip %s", err) + } else { + t.Logf("host %s %s", host.GetAccessIp(), host.GetName()) + } + } +} diff --git a/pkg/util/esxi/mobase.go b/pkg/util/esxi/mobase.go new file mode 100644 index 0000000000..7daa855d4f --- /dev/null +++ b/pkg/util/esxi/mobase.go @@ -0,0 +1,124 @@ +package esxi + +import ( + "github.com/vmware/govmomi/vim25/mo" + "github.com/yunionio/log" + "reflect" + "github.com/yunionio/onecloud/pkg/cloudprovider" +) + +type SManagedObject struct { + manager *SESXiClient + datacenter *SDatacenter + object mo.Entity + + path []string +} + +func newManagedObject(manager *SESXiClient, moobj mo.Entity, dc *SDatacenter) SManagedObject { + return SManagedObject{manager: manager, object: moobj, datacenter: dc} +} + +func (self *SManagedObject) GetName() string { + return self.object.Entity().Name +} + +func (self *SManagedObject) GetId() string { + return self.object.Entity().Self.Value +} + +func (self *SManagedObject) GetType() string { + return self.object.Entity().Self.Type +} + +func (self *SManagedObject) getCurrentParentEntity() *mo.ManagedEntity { + return self.getParentEntity(self.object.Entity()) +} + +func (self *SManagedObject) getParentEntity(obj *mo.ManagedEntity) *mo.ManagedEntity { + parent := obj.Parent + if parent != nil { + var entity mo.ManagedEntity + err := self.manager.reference2Object(*parent, []string{"name", "parent"}, &entity) + if err != nil { + log.Errorf("%s", err) + return nil + } + log.Debugf("getParentEntity %s %s %s", entity.Self.Type, entity.Self.Value, entity.Name) + return &entity + } + return nil +} + +func reverseArray(array interface{}) { + arrayValue := reflect.Indirect(reflect.ValueOf(array)) + if arrayValue.Kind() != reflect.Slice && arrayValue.Kind() != reflect.Array { + log.Errorf("reverse non array or slice") + return + } + tmp := reflect.Indirect(reflect.New(arrayValue.Type().Elem())) + for i, j := 0, arrayValue.Len() - 1; i < j; i, j = i + 1, j - 1 { + tmpi := arrayValue.Index(i) + tmpj := arrayValue.Index(j) + tmp.Set(tmpi) + tmpi.Set(tmpj) + tmpj.Set(tmp) + } +} + +func (self *SManagedObject) fetchPath() []string { + path := make([]string, 0) + obj := self.object.Entity() + for obj != nil { + path = append(path, obj.Name) + obj = self.getParentEntity(obj) + } + reverseArray(path) + return path +} + +func (self *SManagedObject) GetPath() []string { + if self.path == nil { + self.path = self.fetchPath() + } + return self.path +} + +func (self *SManagedObject) findInParents(objType string) *mo.ManagedEntity { + obj := self.object.Entity() + + for obj != nil && obj.Self.Type != objType { + obj = self.getParentEntity(obj) + } + + return obj +} + +func (self *SManagedObject) fetchDatacenter() (*SDatacenter, error) { + me := self.findInParents("Datacenter") + if me == nil { + return nil, cloudprovider.ErrNotFound + } + return self.manager.FindDatacenterById(me.Self.Value) +} + +func (self *SManagedObject) GetDatacenter() (*SDatacenter, error) { + if self.datacenter == nil { + dc, err := self.fetchDatacenter() + if err != nil { + return nil, err + } + self.datacenter = dc + } + return self.datacenter, nil +} + +func (self *SManagedObject) GetDatacenterPath() []string { + dc, err := self.GetDatacenter() + if err != nil { + log.Errorf("cannot find datacenter") + return nil + } + path := dc.GetPath() + return path[1:] +} \ No newline at end of file diff --git a/pkg/util/esxi/mobase_test.go b/pkg/util/esxi/mobase_test.go new file mode 100644 index 0000000000..f42a48ecbb --- /dev/null +++ b/pkg/util/esxi/mobase_test.go @@ -0,0 +1,14 @@ +package esxi + +import "testing" + +func TestReverseArray(t *testing.T) { + a := []int{1, 2, 3, 4, 5} + + reverseArray(a) + t.Logf("%#v", a) + + b := []string{"1", "2", "3", "4", "5"} + reverseArray(b) + t.Logf("%#v", b) +} diff --git a/pkg/util/esxi/provider/provider.go b/pkg/util/esxi/provider/provider.go new file mode 100644 index 0000000000..696801fd81 --- /dev/null +++ b/pkg/util/esxi/provider/provider.go @@ -0,0 +1,119 @@ +package provider + +import ( + "net/url" + "strings" + "strconv" + + "github.com/yunionio/jsonutils" + "github.com/yunionio/log" + + "github.com/yunionio/onecloud/pkg/util/esxi" + "github.com/yunionio/onecloud/pkg/cloudprovider" + +) + +type SESXiProviderFactory struct { + providerTable map[string]*SESXiProvider +} + +func (self *SESXiProviderFactory) GetId() string { + return esxi.CLOUD_PROVIDER_VMWARE +} + +func parseHostPort(host string, defPort int) (string, int, error) { + colonPos := strings.IndexByte(host, ':') + if colonPos > 0 { + h := host[:colonPos] + p, err := strconv.Atoi(host[colonPos+1:]) + if err != nil { + log.Errorf("Invalid host %s", host) + return "", 0, err + } + if p == 0 { + p = defPort + } + return h, p, nil + } else { + return host, defPort, nil + } +} + +func (self *SESXiProviderFactory) GetProvider(providerId, providerName, urlStr, account, secret string) (cloudprovider.ICloudProvider, error) { + provider, ok := self.providerTable[providerId] + if ok { + return provider, nil + } + parts, err := url.Parse(urlStr) + if err != nil { + return nil, err + } + host, port, err := parseHostPort(parts.Host, 443) + if err != nil { + return nil, err + } + + client, err := esxi.NewESXiClient(providerId, providerName, host, port, account, secret) + if err != nil { + return nil, err + } + self.providerTable[providerId] = &SESXiProvider{client: client} + return self.providerTable[providerId], nil +} + +func init() { + factory := SESXiProviderFactory{ + providerTable: make(map[string]*SESXiProvider), + } + cloudprovider.RegisterFactory(&factory) +} + +type SESXiProvider struct { + client *esxi.SESXiClient +} + +func (self *SESXiProvider) IsPublicCloud() bool { + return false +} + +func (self *SESXiProvider) GetId() string { + return esxi.CLOUD_PROVIDER_VMWARE +} + +func (self *SESXiProvider) GetName() string { + return esxi.CLOUD_PROVIDER_VMWARE +} + +func (self *SESXiProvider) GetSysInfo() (jsonutils.JSONObject, error) { + return self.client.About(), nil +} + +func (self *SESXiProvider) GetIRegions() []cloudprovider.ICloudRegion { + return nil +} + +func (self *SESXiProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SESXiProvider) GetIHostById(id string) (cloudprovider.ICloudHost, error) { + host, err := self.client.FindHostByIp(id) + if err != nil { + return nil, err + } else { + return host, nil + } +} + +func (self *SESXiProvider) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (self *SESXiProvider) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SESXiProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { + return nil, cloudprovider.ErrNotImplemented +} + diff --git a/pkg/util/esxi/shell/datacenter.go b/pkg/util/esxi/shell/datacenter.go new file mode 100644 index 0000000000..30ee3f2cf7 --- /dev/null +++ b/pkg/util/esxi/shell/datacenter.go @@ -0,0 +1,19 @@ +package shell + +import ( + "github.com/yunionio/onecloud/pkg/util/shellutils" + "github.com/yunionio/onecloud/pkg/util/esxi" +) + +func init() { + type DatacenterListOptions struct { + } + shellutils.R(&DatacenterListOptions{}, "dc-list", "List all datacenters", func(cli *esxi.SESXiClient, args *DatacenterListOptions) error { + dcs, err := cli.GetDatacenters() + if err != nil { + return err + } + printList(dcs, nil) + return nil + }) +} diff --git a/pkg/util/esxi/shell/host.go b/pkg/util/esxi/shell/host.go new file mode 100644 index 0000000000..bf0254bd7e --- /dev/null +++ b/pkg/util/esxi/shell/host.go @@ -0,0 +1,36 @@ +package shell + +import ( + "github.com/yunionio/onecloud/pkg/util/shellutils" + "github.com/yunionio/onecloud/pkg/util/esxi" +) + +func init() { + type HostListOptions struct { + DATACENTER string `help:"List hosts in datacenter"` + } + shellutils.R(&HostListOptions{}, "host-list", "List hosts in datacenter", func(cli *esxi.SESXiClient, args *HostListOptions) error { + dc, err := cli.FindDatacenterById(args.DATACENTER) + if err != nil { + return err + } + hosts, err := dc.GetIHosts() + if err != nil { + return err + } + printList(hosts, nil) + return nil + }) + + type HostShowOptions struct { + IP string `help:"Host IP"` + } + shellutils.R(&HostShowOptions{}, "host-show", "Show details of a host by IP", func(cli *esxi.SESXiClient, args *HostShowOptions) error { + host, err := cli.FindHostByIp(args.IP) + if err != nil { + return err + } + printObject(host) + return nil + }) +} \ No newline at end of file diff --git a/pkg/util/esxi/shell/store.go b/pkg/util/esxi/shell/store.go new file mode 100644 index 0000000000..2198704509 --- /dev/null +++ b/pkg/util/esxi/shell/store.go @@ -0,0 +1,24 @@ +package shell + +import ( + "github.com/yunionio/onecloud/pkg/util/shellutils" + "github.com/yunionio/onecloud/pkg/util/esxi" +) + +func init() { + type DatastoreListOptions struct { + DATACENTER string `help:"List datastores in datacenter"` + } + shellutils.R(&DatastoreListOptions{}, "ds-list", "List datastores in datacenter", func(cli *esxi.SESXiClient, args *DatastoreListOptions) error { + dc, err := cli.FindDatacenterById(args.DATACENTER) + if err != nil { + return err + } + ds, err := dc.GetIStorages() + if err != nil { + return err + } + printList(ds, nil) + return nil + }) +} \ No newline at end of file diff --git a/pkg/util/esxi/shell/utils.go b/pkg/util/esxi/shell/utils.go new file mode 100644 index 0000000000..255ce645d8 --- /dev/null +++ b/pkg/util/esxi/shell/utils.go @@ -0,0 +1,12 @@ +package shell + +import "github.com/yunionio/onecloud/pkg/util/printutils" + + +func printList(data interface{}, columns []string) { + printutils.PrintGetterList(data, columns) +} + +func printObject(obj interface{}) { + printutils.PrintGetterObject(obj) +} diff --git a/pkg/util/esxi/shell/virtualmachine.go b/pkg/util/esxi/shell/virtualmachine.go new file mode 100644 index 0000000000..e8af328777 --- /dev/null +++ b/pkg/util/esxi/shell/virtualmachine.go @@ -0,0 +1,59 @@ +package shell + +import ( + "github.com/yunionio/onecloud/pkg/util/shellutils" + "github.com/yunionio/onecloud/pkg/util/esxi" + "github.com/yunionio/onecloud/pkg/util/printutils" +) + +func init() { + type VirtualMachineListOptions struct { + HOSTIP string `help:"Host IP"` + } + shellutils.R(&VirtualMachineListOptions{}, "vm-list", "List vms of a host", func(cli *esxi.SESXiClient, args *VirtualMachineListOptions) error { + host, err := cli.FindHostByIp(args.HOSTIP) + if err != nil { + return err + } + vms, err := host.GetIVMs() + if err != nil { + return err + } + printList(vms, []string{}) + return nil + }) + + type VirtualMachineShowOptions struct { + HOSTIP string `help:"Host IP"` + VMID string `help:"VM ID"` + } + shellutils.R(&VirtualMachineShowOptions{}, "vm-show", "Show vm details", func(cli *esxi.SESXiClient, args *VirtualMachineShowOptions) error { + host, err := cli.FindHostByIp(args.HOSTIP) + if err != nil { + return err + } + vm, err := host.GetIVMById(args.VMID) + if err != nil { + return err + } + printObject(vm) + return nil + }) + + shellutils.R(&VirtualMachineShowOptions{}, "vm-vnc", "Show vm VNC details", func(cli *esxi.SESXiClient, args *VirtualMachineShowOptions) error { + host, err := cli.FindHostByIp(args.HOSTIP) + if err != nil { + return err + } + vm, err := host.GetIVMById(args.VMID) + if err != nil { + return err + } + info, err := vm.GetVNCInfo() + if err != nil { + return err + } + printutils.PrintJSONObject(info) + return nil + }) +} diff --git a/pkg/util/esxi/storage.go b/pkg/util/esxi/storage.go new file mode 100644 index 0000000000..340d8235db --- /dev/null +++ b/pkg/util/esxi/storage.go @@ -0,0 +1,89 @@ +package esxi + +import ( + "github.com/vmware/govmomi/vim25/mo" + + "github.com/yunionio/jsonutils" + "github.com/yunionio/onecloud/pkg/cloudprovider" + "github.com/yunionio/onecloud/pkg/compute/models" +) + +var DATASTORE_PROPS = []string {"name", "parent"} + + +type SDatastore struct { + SManagedObject +} + +func NewDatastore(manager *SESXiClient, ds *mo.Datastore, dc *SDatacenter) *SDatastore { + return &SDatastore{SManagedObject: newManagedObject(manager, ds, dc)} +} + +func (self *SDatastore) getDatastore() *mo.Datastore { + return self.object.(*mo.Datastore) +} + +func (self *SDatastore) GetGlobalId() string { + return "" +} + +func (self *SDatastore) GetStatus() string { + if self.getDatastore().Summary.Accessible { + return models.STORAGE_ONLINE + } else { + return models.STORAGE_OFFLINE + } +} + +func (self *SDatastore) Refresh() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SDatastore) IsEmulated() bool { + return false +} + +func (self *SDatastore) getVolumeId() string { + return self.getDatastore().Summary.Type +} + +func (self *SDatastore) GetIStoragecache() cloudprovider.ICloudStoragecache { + return nil +} + +func (self *SDatastore) GetIZone() cloudprovider.ICloudZone { + return nil +} + +func (self *SDatastore) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SDatastore) GetStorageType() string { + return self.getDatastore().Summary.Type +} + +func (self *SDatastore) GetMediumType() string { + return "" +} + +func (self *SDatastore) GetCapacityMB() int { + return 0 +} + +func (self *SDatastore) GetStorageConf() jsonutils.JSONObject { + conf := jsonutils.NewDict() + return conf +} + +func (self *SDatastore) GetEnabled() bool { + return true +} + +func (self *SDatastore) GetManagerId() string { + return self.manager.providerId +} + +func (self *SDatastore) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) { + return nil, cloudprovider.ErrNotImplemented +} \ No newline at end of file diff --git a/pkg/util/esxi/virtualmachine.go b/pkg/util/esxi/virtualmachine.go new file mode 100644 index 0000000000..1cbbfd6fa2 --- /dev/null +++ b/pkg/util/esxi/virtualmachine.go @@ -0,0 +1,208 @@ +package esxi + +import ( + "time" + + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" + + "github.com/yunionio/jsonutils" + + "github.com/yunionio/onecloud/pkg/cloudprovider" + "github.com/yunionio/onecloud/pkg/compute/models" + + "github.com/vmware/govmomi/object" + "fmt" +) + +var VIRTUAL_MACHINE_PROPS = []string{"name", "parent", "runtime", "summary"} + +type SVirtualMachine struct { + SManagedObject + + host *SHost +} + +func NewVirtualMachine(manager *SESXiClient, vm *mo.VirtualMachine, dc *SDatacenter, host *SHost) *SVirtualMachine { + return &SVirtualMachine{SManagedObject: newManagedObject(manager, vm, dc), host: host} +} + +func (self *SVirtualMachine) getVirtualMachine() *mo.VirtualMachine { + return self.object.(*mo.VirtualMachine) +} + + +func (self *SVirtualMachine) GetGlobalId() string { + return self.getUuid() +} + +func (self *SVirtualMachine) GetStatus() string { + vm := object.NewVirtualMachine(self.manager.client.Client, self.getVirtualMachine().Self) + state, err := vm.PowerState(self.manager.context) + if err != nil { + return models.VM_UNKNOWN + } + switch state { + case types.VirtualMachinePowerStatePoweredOff: + return models.VM_READY + case types.VirtualMachinePowerStatePoweredOn: + return models.VM_RUNNING + case types.VirtualMachinePowerStateSuspended: + return models.VM_SUSPEND + default: + return models.VM_UNKNOWN + } +} + +func (self *SVirtualMachine) Refresh() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SVirtualMachine) IsEmulated() bool { + return false +} + +func (self *SVirtualMachine) getUuid() string { + return self.getVirtualMachine().Summary.Config.Uuid +} + +func (self *SVirtualMachine) GetCreateTime() time.Time { + return time.Time{} +} + +func (self *SVirtualMachine) GetIHost() cloudprovider.ICloudHost { + return self.host +} + +func (self *SVirtualMachine) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SVirtualMachine) GetINics() ([]cloudprovider.ICloudNic, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SVirtualMachine) GetEIP() cloudprovider.ICloudEIP { + return nil +} + +func (self *SVirtualMachine) GetVcpuCount() int8 { + // ret = self.obj.summary.config.numCpu + return int8(self.getVirtualMachine().Summary.Config.NumCpu) +} + +func (self *SVirtualMachine) GetVmemSizeMB() int { + // self.obj.summary.config.memorySizeMB + return int(self.getVirtualMachine().Summary.Config.MemorySizeMB) +} + +func (self *SVirtualMachine) GetBootOrder() string { + return "cdn" +} + +func (self *SVirtualMachine) GetVga() string { + return "vga" +} + +func (self *SVirtualMachine) GetVdi() string { + return "vmrc" +} + +func (self *SVirtualMachine) GetOSType() string { + return "" +} + +func (self *SVirtualMachine) GetOSName() string { + return "" +} + +func (self *SVirtualMachine) GetBios() string { + // self.obj.config.firmware + switch self.getVirtualMachine().Config.Firmware { + case "efi": + return "UEFI" + case "bios": + return "BIOS" + default: + return "BIOS" + } +} + +func (self *SVirtualMachine) GetMachine() string { + return "pc" +} + +func (self *SVirtualMachine) GetHypervisor() string { + return models.HYPERVISOR_ESXI +} + +// GetSecurityGroup() ICloudSecurityGroup + +func (self *SVirtualMachine) StartVM() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SVirtualMachine) StopVM(isForce bool) error { + return cloudprovider.ErrNotImplemented +} + +func (self *SVirtualMachine) DeleteVM() error { + return cloudprovider.ErrNotImplemented +} + +func (self *SVirtualMachine) GetVNCInfo() (jsonutils.JSONObject, error) { + info, err := self.acquireWebmksTicket("webmks") + if err != nil { + info, err = self.acquireVmrcUrl() + } + return info, err +} + +func (self *SVirtualMachine) acquireWebmksTicket(ticketType string) (jsonutils.JSONObject, error) { + vm := object.NewVirtualMachine(self.manager.client.Client, self.getVirtualMachine().Self) + ticket, err := vm.AcquireTicket(self.manager.context, ticketType) + if err != nil { + return nil, err + } + ret := jsonutils.NewDict() + + host := ticket.Host + if len(host) == 0 { + host = self.manager.host + } + port := ticket.Port + if port == 0 { + port = int32(self.manager.port) + } + if port == 0 { + port = 443 + } + /* + ret.Add(jsonutils.NewString(ticketType), "type") + ret.Add(jsonutils.NewString(ticket.Host), "host") + ret.Add(jsonutils.NewInt(int64(ticket.Port)), "port") + ret.Add(jsonutils.NewString(ticket.Ticket), "ticket") + ret.Add(jsonutils.NewString(ticket.SslThumbprint), "slThumbprint") + ret.Add(jsonutils.NewString(ticket.CfgFile), "cfgFile") + */ + url := fmt.Sprintf("wss://%s:%d/ticket/%s", host, port, ticket.Ticket) + ret.Add(jsonutils.NewString("wmks"), "protocol") + ret.Add(jsonutils.NewString(url), "url") + return ret, nil +} + +func (self *SVirtualMachine) acquireVmrcUrl() (jsonutils.JSONObject, error) { + ticket, err := self.manager.acquireCloneTicket() + if err != nil { + return nil, err + } + ret := jsonutils.NewDict() + ret.Add(jsonutils.NewString("vmrc"), "protocol") + port := self.manager.port + if port == 0 { + port = 443 + } + url := fmt.Sprintf("vmrc://clone:%s@%s:%d/?moid=%s", ticket, self.manager.host, port, self.GetId()) + ret.Add(jsonutils.NewString(url), "url") + return ret, nil +} \ No newline at end of file diff --git a/pkg/util/printutils/printgetter.go b/pkg/util/printutils/printgetter.go new file mode 100644 index 0000000000..902e09163c --- /dev/null +++ b/pkg/util/printutils/printgetter.go @@ -0,0 +1,60 @@ +package printutils + +import ( + "reflect" + "strings" + "fmt" + + "github.com/yunionio/jsonutils" + "github.com/yunionio/pkg/utils" + "github.com/yunionio/onecloud/pkg/mcclient/modules" +) + +func getter2json(obj interface{}) jsonutils.JSONObject { + jsonDict := jsonutils.NewDict() + + objValue := reflect.ValueOf(obj) + objType := reflect.TypeOf(obj) + + // log.Debugf("getter2json %d", objValue.NumMethod()) + + for i := 0; i < objValue.NumMethod(); i += 1 { + methodValue := objValue.Method(i) + method := objType.Method(i) + methodName := method.Name + methodType := methodValue.Type() + + if strings.HasPrefix(methodName, "Get") && methodType.NumIn() == 0 && methodType.NumOut() == 1 { + fieldName := utils.CamelSplit(methodName[3:], "_") + out := methodValue.Call([]reflect.Value{}) + if len(out) == 1 { + jsonDict.Add(jsonutils.Marshal(out[0].Interface()), fieldName) + } + } + } + + return jsonDict +} + +func PrintGetterList(data interface{}, columns []string) { + dataValue := reflect.ValueOf(data) + if dataValue.Kind() != reflect.Slice { + fmt.Println("Invalid list data") + return + } + jsonList := make([]jsonutils.JSONObject, dataValue.Len()) + for i := 0; i < dataValue.Len(); i += 1 { + jsonList[i] = getter2json(dataValue.Index(i).Interface()) + } + list := &modules.ListResult{ + Data: jsonList, + Total: dataValue.Len(), + Limit: 0, + Offset: 0, + } + PrintJSONList(list, columns) +} + +func PrintGetterObject(obj interface{}) { + PrintJSONObject(getter2json(obj)) +} \ No newline at end of file diff --git a/pkg/util/printjson/printjson.go b/pkg/util/printutils/printjson.go similarity index 88% rename from pkg/util/printjson/printjson.go rename to pkg/util/printutils/printjson.go index 4fe62fd319..86ae7d5bcc 100644 --- a/pkg/util/printjson/printjson.go +++ b/pkg/util/printutils/printjson.go @@ -1,4 +1,4 @@ -package printjson +package printutils import ( "fmt" @@ -10,7 +10,7 @@ import ( "github.com/yunionio/onecloud/pkg/mcclient/modules" ) -func PrintList(list *modules.ListResult, columns []string) { +func PrintJSONList(list *modules.ListResult, columns []string) { colsWithData := make([]string, 0) if columns == nil || len(columns) == 0 { colsWithDataMap := make(map[string]bool) @@ -75,7 +75,7 @@ func PrintList(list *modules.ListResult, columns []string) { fmt.Println("*** ", title, " ***") } -func PrintObject(obj jsonutils.JSONObject) { +func PrintJSONObject(obj jsonutils.JSONObject) { dict, ok := obj.(*jsonutils.JSONDict) if !ok { fmt.Println("Not a valid JSON object:", obj.String()) @@ -98,7 +98,7 @@ func PrintObject(obj jsonutils.JSONObject) { fmt.Println(pt.GetString(rows)) } -func PrintBatchResults(results []modules.SubmitResult, columns []string) { +func PrintJSONBatchResults(results []modules.SubmitResult, columns []string) { objs := make([]jsonutils.JSONObject, 0) errs := make([]jsonutils.JSONObject, 0) for _, r := range results { @@ -113,9 +113,9 @@ func PrintBatchResults(results []modules.SubmitResult, columns []string) { } } if len(objs) > 0 { - PrintList(&modules.ListResult{Data: objs}, columns) + PrintJSONList(&modules.ListResult{Data: objs}, columns) } if len(errs) > 0 { - PrintList(&modules.ListResult{Data: errs}, []string{"status", "id", "error"}) + PrintJSONList(&modules.ListResult{Data: errs}, []string{"status", "id", "error"}) } } diff --git a/pkg/util/aliyun/shell/utils.go b/pkg/util/printutils/printobj.go similarity index 68% rename from pkg/util/aliyun/shell/utils.go rename to pkg/util/printutils/printobj.go index 568c0a4eb1..756388c349 100644 --- a/pkg/util/aliyun/shell/utils.go +++ b/pkg/util/printutils/printobj.go @@ -1,4 +1,4 @@ -package shell +package printutils import ( "fmt" @@ -7,10 +7,9 @@ import ( "github.com/yunionio/jsonutils" "github.com/yunionio/onecloud/pkg/mcclient/modules" - "github.com/yunionio/onecloud/pkg/util/printjson" ) -func printList(data interface{}, total, offset, limit int, columns []string) { +func PrintInterfaceList(data interface{}, total, offset, limit int, columns []string) { dataValue := reflect.ValueOf(data) if dataValue.Kind() != reflect.Slice { fmt.Println("Invalid list data") @@ -29,9 +28,9 @@ func printList(data interface{}, total, offset, limit int, columns []string) { Limit: limit, Offset: offset, } - printjson.PrintList(list, columns) + PrintJSONList(list, columns) } -func printObject(obj interface{}) { - printjson.PrintObject(jsonutils.Marshal(obj)) +func PrintInterfaceObject(obj interface{}) { + PrintJSONObject(jsonutils.Marshal(obj)) } diff --git a/pkg/util/aliyun/shell/shell.go b/pkg/util/shellutils/shell.go similarity index 93% rename from pkg/util/aliyun/shell/shell.go rename to pkg/util/shellutils/shell.go index ee42a74e4c..a8da90b43d 100644 --- a/pkg/util/aliyun/shell/shell.go +++ b/pkg/util/shellutils/shell.go @@ -1,4 +1,4 @@ -package shell +package shellutils type CMD struct { Options interface{}