diff --git a/build/docker/Dockerfile.esxi-agent b/build/docker/Dockerfile.esxi-agent
new file mode 100644
index 0000000000..38bac16e5f
--- /dev/null
+++ b/build/docker/Dockerfile.esxi-agent
@@ -0,0 +1,4 @@
+FROM registry.cn-beijing.aliyuncs.com/yunionio/onecloud-base:latest
+
+COPY ./build/esxi-agent/root/opt/ /opt/
+ADD ./_output/bin/esxi-agent /opt/yunion/bin/esxi-agent
diff --git a/build/esxi-agent/root/opt/yunion/share/vmware/ovf.xml b/build/esxi-agent/root/opt/yunion/share/vmware/ovf.xml
new file mode 100644
index 0000000000..a62536bfe8
--- /dev/null
+++ b/build/esxi-agent/root/opt/yunion/share/vmware/ovf.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+ Virtual disk information
+
+
+
+ A virtual machine
+ {{ .ImportName }}
+
+ The kind of installed guest operating system
+
+
+ Virtual hardware requirements
+
+ Virtual Hardware Family
+ 0
+ {{ .ImportName }}
+ vmx-07
+
+ -
+ hertz * 10^6
+ Number of Virtual CPUs
+ 1 virtual CPU(s)
+ 1
+ 3
+ 1
+
+ -
+ byte * 2^20
+ Memory Size
+ 1024MB of memory
+ 2
+ 4
+ 1024
+
+ -
+ 0
+ SCSI Controller
+ SCSI Controller 0
+ 3
+ VirtualSCSI
+ 6
+
+ -
+ 0
+ Hard Disk 1
+ ovf:/disk/vmdisk1
+ 9
+ 3
+ 17
+
+
+
+
+
diff --git a/build/esxi-agent/vars b/build/esxi-agent/vars
new file mode 100644
index 0000000000..3aedc96281
--- /dev/null
+++ b/build/esxi-agent/vars
@@ -0,0 +1,2 @@
+DESCRIPTION="Esxi Agent"
+# SERVICE="yes"
diff --git a/cmd/esxi-agent/main.go b/cmd/esxi-agent/main.go
new file mode 100644
index 0000000000..2cdf1e65ec
--- /dev/null
+++ b/cmd/esxi-agent/main.go
@@ -0,0 +1,26 @@
+// 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 (
+ "yunion.io/x/onecloud/pkg/esxi/service"
+ "yunion.io/x/onecloud/pkg/util/atexit"
+)
+
+func main() {
+ defer atexit.Handle()
+
+ service.New().StartService()
+}
diff --git a/pkg/cloudcommon/agent/agent.go b/pkg/cloudcommon/agent/agent.go
index a208cbb62c..19047305c1 100644
--- a/pkg/cloudcommon/agent/agent.go
+++ b/pkg/cloudcommon/agent/agent.go
@@ -24,6 +24,7 @@ import (
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/version"
+ "yunion.io/x/onecloud/pkg/cloudcommon/agent/iagent"
"yunion.io/x/onecloud/pkg/cloudcommon/object"
"yunion.io/x/onecloud/pkg/hostman/storageman"
"yunion.io/x/onecloud/pkg/mcclient"
@@ -31,19 +32,6 @@ import (
"yunion.io/x/onecloud/pkg/util/httputils"
)
-type IAgent interface {
- GetAgentType() string
- GetAccessIP() (net.IP, error)
- GetListenIP() (net.IP, error)
- GetPort() int
- GetEnableSsl() bool
- GetZoneName() string
- GetAdminSession() *mcclient.ClientSession
- TuneSystem() error
- StartService() error
- StopService() error
-}
-
type SZoneInfo struct {
Name string `json:"name"`
Id string `json:"id"`
@@ -80,11 +68,11 @@ func getIfaceIPs(iface *net.Interface) ([]net.IP, error) {
return ips, nil
}
-func (agent *SBaseAgent) IAgent() IAgent {
- return agent.GetVirtualObject().(IAgent)
+func (agent *SBaseAgent) IAgent() iagent.IAgent {
+ return agent.GetVirtualObject().(iagent.IAgent)
}
-func (agent *SBaseAgent) Init(iagent IAgent, ifname string, cachePath string) error {
+func (agent *SBaseAgent) Init(iagent iagent.IAgent, ifname string, cachePath string) error {
iface, err := net.InterfaceByName(ifname)
if err != nil {
return err
diff --git a/pkg/cloudcommon/agent/iagent/doc.go b/pkg/cloudcommon/agent/iagent/doc.go
new file mode 100644
index 0000000000..586cf249d4
--- /dev/null
+++ b/pkg/cloudcommon/agent/iagent/doc.go
@@ -0,0 +1,15 @@
+// Copyright 2019 Yunion
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package iagent // import "yunion.io/x/onecloud/pkg/cloudcommon/agent/iagent"
diff --git a/pkg/cloudcommon/agent/iagent/interface.go b/pkg/cloudcommon/agent/iagent/interface.go
new file mode 100644
index 0000000000..47e66c22ee
--- /dev/null
+++ b/pkg/cloudcommon/agent/iagent/interface.go
@@ -0,0 +1,34 @@
+// Copyright 2019 Yunion
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package iagent
+
+import (
+ "net"
+
+ "yunion.io/x/onecloud/pkg/mcclient"
+)
+
+type IAgent interface {
+ GetAgentType() string
+ GetAccessIP() (net.IP, error)
+ GetListenIP() (net.IP, error)
+ GetPort() int
+ GetEnableSsl() bool
+ GetZoneName() string
+ GetAdminSession() *mcclient.ClientSession
+ TuneSystem() error
+ StartService() error
+ StopService() error
+}
diff --git a/pkg/cloudcommon/workmanager/manager.go b/pkg/cloudcommon/workmanager/manager.go
index 7f90d65c7c..1863fc3584 100644
--- a/pkg/cloudcommon/workmanager/manager.go
+++ b/pkg/cloudcommon/workmanager/manager.go
@@ -65,6 +65,8 @@ func (w *SWorkManager) delayTask(ctx context.Context, task DelayTaskFunc, params
w.delayTaskWithoutReqctx(ctx, task, params, worker)
return
} else {
+ // delayTask should have a new context.Context with value 'taskid'
+ ctx = context.WithValue(context.Background(), appctx.APP_CONTEXT_KEY_TASK_ID, ctx.Value(appctx.APP_CONTEXT_KEY_TASK_ID))
w.add()
worker.Run(func() {
defer w.done()
@@ -109,6 +111,8 @@ func (w *SWorkManager) DelayTaskWithoutReqctx(ctx context.Context, task DelayTas
func (w *SWorkManager) delayTaskWithoutReqctx(
ctx context.Context, task DelayTaskFunc, params interface{}, worker *appsrv.SWorkerManager,
) {
+ // delayTaskWithReqctx should have a new context.Context
+ ctx = context.Background()
w.add()
w.worker.Run(func() {
defer w.done()
diff --git a/pkg/esxi/agent.go b/pkg/esxi/agent.go
new file mode 100644
index 0000000000..06cccc21c6
--- /dev/null
+++ b/pkg/esxi/agent.go
@@ -0,0 +1,175 @@
+// 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 esxi
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "net/http"
+
+ "yunion.io/x/log"
+
+ "yunion.io/x/onecloud/pkg/apis/compute"
+ "yunion.io/x/onecloud/pkg/appsrv"
+ "yunion.io/x/onecloud/pkg/cloudcommon/agent"
+ "yunion.io/x/onecloud/pkg/cloudcommon/workmanager"
+ "yunion.io/x/onecloud/pkg/esxi/options"
+ "yunion.io/x/onecloud/pkg/hostman/hostutils"
+ "yunion.io/x/onecloud/pkg/hostman/storageman"
+ "yunion.io/x/onecloud/pkg/httperrors"
+ "yunion.io/x/onecloud/pkg/mcclient"
+ "yunion.io/x/onecloud/pkg/mcclient/auth"
+ "yunion.io/x/onecloud/pkg/util/netutils2"
+)
+
+const (
+ _ = compute.AgentTypeEsxi
+)
+
+var (
+ EsxiAgent *SEsxiAgent
+)
+
+type SEsxiAgent struct {
+ agent.SBaseAgent
+
+ agentImageCache *storageman.SAgentImageCacheManager
+ AgentStorage *storageman.SAgentStorage
+ ListenNic netutils2.SNetInterface
+}
+
+func NewEsxiAgent() (*SEsxiAgent, error) {
+ agent := &SEsxiAgent{}
+ err := agent.Init(agent, options.Options.ListenInterface, options.Options.ImageCachePath)
+ if err != nil {
+ return nil, err
+ }
+ return agent, nil
+}
+
+func (ea *SEsxiAgent) GetAgentType() string {
+ return string(compute.AgentTypeEsxi)
+}
+
+func (ea *SEsxiAgent) GetAccessIP() (net.IP, error) {
+ return ea.GetListenIP()
+}
+
+func (ea *SEsxiAgent) GetListenIP() (net.IP, error) {
+ return ea.FindListenIP(options.Options.ListenAddress)
+}
+
+func (ea *SEsxiAgent) GetPort() int {
+ return options.Options.Port
+}
+
+func (ea *SEsxiAgent) GetEnableSsl() bool {
+ return options.Options.EnableSsl
+}
+
+func (ea *SEsxiAgent) GetZoneName() string {
+ return options.Options.Zone
+}
+
+func (ea *SEsxiAgent) GetAdminSession() *mcclient.ClientSession {
+ return auth.GetAdminSession(context.TODO(), options.Options.Region, "v2")
+}
+
+func (ea *SEsxiAgent) TuneSystem() error {
+ return nil
+}
+
+func (ea *SEsxiAgent) StartService() error {
+
+ ea.DoOnline(ea.GetAdminSession())
+ return nil
+}
+
+func (ea *SEsxiAgent) StopService() error {
+ return nil
+}
+
+func (ea *SEsxiAgent) Start() error {
+ err := ea.SBaseAgent.Start()
+ if err != nil {
+ return err
+ }
+ // add agent image cache
+ ea.agentImageCache = storageman.NewAgentImageCacheManager(ea.CacheManager)
+ ea.AgentStorage = storageman.NewAgentStorage(&storageman.SStorageManager{LocalStorageImagecacheManager: ea.CacheManager},
+ ea, options.Options.AgentTempPath)
+ return nil
+}
+
+func Start(app *appsrv.Application) error {
+ var err error
+ if EsxiAgent != nil {
+ log.Warningf("Global EsxiAgent already start")
+ return nil
+ }
+ EsxiAgent, err = NewEsxiAgent()
+ if err != nil {
+ return err
+ }
+ err = EsxiAgent.Start()
+ if err != nil {
+ return err
+ }
+ EsxiAgent.AddImageCacheHandler("", app)
+ return nil
+}
+
+func (agent *SEsxiAgent) AddImageCacheHandler(prefix string, app *appsrv.Application) {
+ hostutils.InitWorkerManager()
+ app.AddHandler("POST",
+ fmt.Sprintf("%s/disks/image_cache", prefix),
+ auth.Authenticate(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ performImageCache(ctx, w, r, agent.agentImageCache.PrefetchImageCache)
+ }))
+ app.AddHandler("DELETE",
+ fmt.Sprintf("%s/disks/image_cache", prefix),
+ auth.Authenticate(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ performImageCache(ctx, w, r, agent.agentImageCache.DeleteImageCache)
+ }))
+}
+
+func performImageCache(
+ ctx context.Context,
+ w http.ResponseWriter,
+ r *http.Request,
+ performTask workmanager.DelayTaskFunc,
+) {
+ _, _, body := appsrv.FetchEnv(ctx, w, r)
+
+ disk, err := body.Get("disk")
+ if err != nil {
+ httperrors.MissingParameterError(w, "disk")
+ return
+ }
+
+ hostutils.DelayTask(ctx, performTask, disk)
+ hostutils.ResponseOk(ctx, w)
+}
+
+func Stop() error {
+ if EsxiAgent != nil {
+ log.Infof("EsxiAgent stop...")
+ tmpAgent := EsxiAgent
+ EsxiAgent = nil
+ tmpAgent.Stop()
+ }
+ return nil
+}
diff --git a/pkg/esxi/doc.go b/pkg/esxi/doc.go
new file mode 100644
index 0000000000..683f7a5876
--- /dev/null
+++ b/pkg/esxi/doc.go
@@ -0,0 +1,15 @@
+// Copyright 2019 Yunion
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package esxi // import "yunion.io/x/onecloud/pkg/esxi"
diff --git a/pkg/esxi/handler/doc.go b/pkg/esxi/handler/doc.go
new file mode 100644
index 0000000000..7b1bdec7a8
--- /dev/null
+++ b/pkg/esxi/handler/doc.go
@@ -0,0 +1,15 @@
+// Copyright 2019 Yunion
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package handler // import "yunion.io/x/onecloud/pkg/esxi/handler"
diff --git a/pkg/esxi/handler/handlers.go b/pkg/esxi/handler/handlers.go
new file mode 100644
index 0000000000..1fb955ac17
--- /dev/null
+++ b/pkg/esxi/handler/handlers.go
@@ -0,0 +1,181 @@
+// 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 handler
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/log"
+
+ "yunion.io/x/onecloud/pkg/appctx"
+ "yunion.io/x/onecloud/pkg/appsrv"
+ "yunion.io/x/onecloud/pkg/esxi"
+ "yunion.io/x/onecloud/pkg/hostman/hostutils"
+ "yunion.io/x/onecloud/pkg/hostman/storageman"
+ "yunion.io/x/onecloud/pkg/httperrors"
+ "yunion.io/x/onecloud/pkg/mcclient/auth"
+)
+
+const (
+ AGENT_PREFIX = "disks/agent"
+)
+
+func InitHandlers(app *appsrv.Application) {
+ initESXIHandler(app)
+}
+
+var defaultHandler = func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ httperrors.NotImplementedError(w, "")
+ return
+}
+
+func IdAgentPrefix(action string) string {
+ return fmt.Sprintf("%s/%s/", AGENT_PREFIX, action)
+}
+
+func AgentPrefix(action string) string {
+ return fmt.Sprintf("%s/%s", AGENT_PREFIX, action)
+}
+
+func initESXIHandler(app *appsrv.Application) {
+
+ app.AddHandler("POST", AgentPrefix("upload"), auth.Authenticate(uploadHandler))
+ app.AddHandler("POST", AgentPrefix("deploy"), auth.Authenticate(deployHandler))
+ app.AddHandler("POST", IdAgentPrefix("delete"), auth.Authenticate(deleteHandler))
+ app.AddHandler("POST", IdAgentPrefix("create"), auth.Authenticate(createHandler))
+ app.AddHandler("POST", IdAgentPrefix("save-prepare"), auth.Authenticate(savePrepareHandler))
+ app.AddHandler("POST", IdAgentPrefix("resize"), auth.Authenticate(resizeHandler))
+ app.AddHandler("POST", IdAgentPrefix("clone"), auth.Authenticate(defaultHandler))
+ app.AddHandler("POST", IdAgentPrefix("fetch"), auth.Authenticate(defaultHandler))
+ app.AddHandler("POST", IdAgentPrefix("post-migrate"), auth.Authenticate(defaultHandler))
+ app.AddHandler("POST", IdAgentPrefix("snapshot"), auth.Authenticate(defaultHandler))
+ app.AddHandler("POST", IdAgentPrefix("reset"), auth.Authenticate(defaultHandler))
+ app.AddHandler("POST", IdAgentPrefix("cleanup-snapshots"), auth.Authenticate(defaultHandler))
+}
+
+func uploadHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ _, _, body := appsrv.FetchEnv(ctx, w, r)
+ disk, err := body.Get("disk")
+ if err != nil {
+ httperrors.MissingParameterError(w, "miss disk")
+ return
+ }
+ hostutils.DelayTask(ctx, esxi.EsxiAgent.AgentStorage.SaveToGlance, disk)
+ hostutils.ResponseOk(ctx, w)
+}
+
+func deployHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ log.Debugf("enter deployHandler")
+ _, _, body := appsrv.FetchEnv(ctx, w, r)
+ disk, err := body.Get("disk")
+ if err != nil {
+ httperrors.MissingParameterError(w, "miss disk")
+ return
+ }
+ hostutils.DelayTask(ctx, esxi.EsxiAgent.AgentStorage.AgentDeployGuest, disk)
+ hostutils.ResponseOk(ctx, w)
+
+}
+
+func deleteHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ params, _, _ := appsrv.FetchEnv(ctx, w, r)
+ diskId := params[""]
+ disk := esxi.EsxiAgent.AgentStorage.GetDiskById(diskId)
+ if taskId := ctx.Value(appctx.APP_CONTEXT_KEY_TASK_ID); taskId == nil {
+ if disk != nil {
+ _, err := disk.Delete(ctx, nil)
+ if err != nil {
+ httperrors.GeneralServerError(w, err)
+ return
+ }
+ hostutils.ResponseOk(ctx, w)
+ return
+ }
+ }
+ hostutils.ResponseOk(ctx, w)
+ hostutils.DelayTask(ctx, disk.Delete, nil)
+}
+
+func createHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ disk, diskInfo, err := diskAndDiskInfo(ctx, w, r)
+ if err != nil {
+ httperrors.GeneralServerError(w, err)
+ return
+ }
+ hostutils.DelayTask(ctx, esxi.EsxiAgent.AgentStorage.CreateDiskByDiskInfo,
+ storageman.SDiskCreateByDiskinfo{DiskId: disk.GetId(), Disk: disk, DiskInfo: diskInfo})
+ hostutils.ResponseOk(ctx, w)
+}
+
+func savePrepareHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ taskId := ctx.Value(appctx.APP_CONTEXT_KEY_TASK_ID).(string)
+ disk, diskInfo, err := diskAndDiskInfo(ctx, w, r)
+ if err != nil {
+ httperrors.GeneralServerError(w, err)
+ return
+ }
+ hostutils.DelayTask(ctx, disk.PrepareSaveToGlance, storageman.PrepareSaveToGlanceParams{taskId, diskInfo})
+ hostutils.ResponseOk(ctx, w)
+}
+
+func resizeHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ disk, diskInfo, err := diskAndDiskInfo(ctx, w, r)
+ if err != nil {
+ httperrors.GeneralServerError(w, err)
+ return
+ }
+ hostutils.DelayTask(ctx, disk.Resize, diskInfo)
+ hostutils.ResponseOk(ctx, w)
+}
+
+/*
+func fetchHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ params, _, body := appsrv.FetchEnv(ctx, w, r)
+ diskId := params[""]
+ disk := esxi.EsxiAgent.AgentStorage.GetDiskById(diskId)
+ if disk != nil {
+ httperrors.GeneralServerError(w, httperrors.NewDuplicateResourceError("disk '%s'", diskId))
+ return
+ }
+ disk := esxi.EsxiAgent.AgentStorage.CreateDisk(diskId)
+ diskInfo, err := body.Get("disk")
+ if err != nil {
+ httperrors.InputParameterError(w, "miss disk")
+ }
+ url, err := diskInfo.GetString("url")
+ if err != nil {
+ httperrors.InputParameterError(w, "miss disk.url")
+ }
+ hostutils.DelayTask(ctx, disk.CreateFromUrl)
+ hostutils.ResponseOk(ctx, w)
+}
+*/
+
+func diskAndDiskInfo(ctx context.Context, w http.ResponseWriter, r *http.Request) (storageman.IDisk, jsonutils.JSONObject, error) {
+ params, _, body := appsrv.FetchEnv(ctx, w, r)
+ diskId := params[""]
+ disk := esxi.EsxiAgent.AgentStorage.GetDiskById(diskId)
+ if disk == nil {
+ return nil, nil, httperrors.NewNotFoundError("disk '%s'", diskId)
+ }
+ diskInfo, err := body.Get("disk")
+ if err != nil {
+ return nil, nil, httperrors.NewMissingParameterError("miss disk")
+ }
+ return disk, diskInfo, nil
+}
diff --git a/pkg/esxi/options/doc.go b/pkg/esxi/options/doc.go
new file mode 100644
index 0000000000..63c1c9c607
--- /dev/null
+++ b/pkg/esxi/options/doc.go
@@ -0,0 +1,15 @@
+// Copyright 2019 Yunion
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package options // import "yunion.io/x/onecloud/pkg/esxi/options"
diff --git a/pkg/esxi/options/options.go b/pkg/esxi/options/options.go
new file mode 100644
index 0000000000..1f52f27b31
--- /dev/null
+++ b/pkg/esxi/options/options.go
@@ -0,0 +1,38 @@
+// 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 options
+
+import common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
+
+type EsxiOptions struct {
+ common_options.CommonOptions
+
+ ListenInterface string `help:"Master address of host server" default:"br0"`
+ ListenAddress string `help:"Host serve IP address to select when multiple address bind to ListenInterface"`
+ EsxiAgentPath string `default:"/opt/cloud/workspace/esxi_agent" help:"Path for esxi agent configuration files"`
+ ImageCachePath string `help:"Path for storing image caches"`
+ ImageCacheLimit int `help:"Maximal storage space for image caching, in GB" default:"20"`
+ AgentTempPath string `help:"Path for ESXI Agent"`
+ AgentTempLimit int `help:"Maximal storage space for ESXi agent, in GB" default:"20"`
+ LinuxDefaultRootUser bool `help:"Default account for Linux system is root" default:"false"`
+ WindowsDefaultAdminUser bool `help:"Default account for Windows system is Administrator" default:"true"`
+ DefaultImageSaveFormat string `help:"Default image save format, default is vmdk, canbe qcow2" default:"vmdk"`
+ Zone string `help:"Zone where the agent locates"`
+ DeployServerSocketPath string `help:"Deploy server listen socket path" default:"/var/run/deploy.sock"`
+}
+
+var (
+ Options EsxiOptions
+)
diff --git a/pkg/esxi/service/doc.go b/pkg/esxi/service/doc.go
new file mode 100644
index 0000000000..a13c4606fe
--- /dev/null
+++ b/pkg/esxi/service/doc.go
@@ -0,0 +1,15 @@
+// Copyright 2019 Yunion
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package service // import "yunion.io/x/onecloud/pkg/esxi/service"
diff --git a/pkg/esxi/service/esxi_agent_service.go b/pkg/esxi/service/esxi_agent_service.go
new file mode 100644
index 0000000000..46d9dc125b
--- /dev/null
+++ b/pkg/esxi/service/esxi_agent_service.go
@@ -0,0 +1,94 @@
+// 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 service
+
+import (
+ "os"
+ "path/filepath"
+
+ "yunion.io/x/log"
+
+ "yunion.io/x/onecloud/pkg/appsrv"
+ app_common "yunion.io/x/onecloud/pkg/cloudcommon/app"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
+ options_common "yunion.io/x/onecloud/pkg/cloudcommon/options"
+ "yunion.io/x/onecloud/pkg/cloudcommon/service"
+ "yunion.io/x/onecloud/pkg/esxi"
+ "yunion.io/x/onecloud/pkg/esxi/handler"
+ "yunion.io/x/onecloud/pkg/esxi/options"
+ "yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
+ "yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient"
+)
+
+type SExsiAgentService struct {
+ service.SServiceBase
+}
+
+func New() *SExsiAgentService {
+ return &SExsiAgentService{}
+}
+
+func (s *SExsiAgentService) StartService() {
+ options_common.ParseOptions(&options.Options, os.Args, "esxiagent.conf", "esxiagent")
+
+ if len(options.Options.ImageCachePath) == 0 {
+ options.Options.ImageCachePath = filepath.Join(filepath.Dir(options.Options.EsxiAgentPath), "image_cache")
+ log.Infof("No cachepath, use default %s", options.Options.ImageCachePath)
+ err := os.MkdirAll(options.Options.ImageCachePath, 0760)
+ if err != nil {
+ log.Fatalf("fail to create ImageCachePath %s", options.Options.ImageCachePath)
+ }
+ }
+ if len(options.Options.AgentTempPath) == 0 {
+ options.Options.AgentTempPath = filepath.Join(filepath.Dir(options.Options.EsxiAgentPath), "agent_tmp")
+ log.Infof("No agent temp path, use default %s", options.Options.AgentTempPath)
+ err := os.MkdirAll(options.Options.AgentTempPath, 0760)
+ if err != nil {
+ log.Fatalf("fail to create AgentTempPath %s", options.Options.AgentTempPath)
+ }
+ }
+
+ // init lockman
+ s.InitLockman()
+
+ app_common.InitAuth(&options.Options.CommonOptions, func() {
+ log.Infof("auth complete")
+ })
+
+ fsdriver.Init(nil)
+ deployclient.Init(options.Options.DeployServerSocketPath)
+
+ app := app_common.InitApp(&options.Options.BaseOptions, false)
+ handler.InitHandlers(app)
+
+ s.startAgent(app)
+
+ app_common.ServeForeverWithCleanup(app, &options.Options.BaseOptions, func() {
+ esxi.Stop()
+ })
+}
+
+func (s *SExsiAgentService) startAgent(app *appsrv.Application) {
+ err := esxi.Start(app)
+ if err != nil {
+ log.Fatalf("Start agent error: %v", err)
+ }
+}
+
+func (s *SExsiAgentService) InitLockman() {
+ log.Infof("using inmemory lockman")
+ lm := lockman.NewInMemoryLockManager()
+ lockman.Init(lm)
+}
diff --git a/pkg/hostman/diskutils/disk.go b/pkg/hostman/diskutils/disk.go
new file mode 100644
index 0000000000..e755dccbef
--- /dev/null
+++ b/pkg/hostman/diskutils/disk.go
@@ -0,0 +1,26 @@
+package diskutils
+
+import (
+ comapi "yunion.io/x/onecloud/pkg/apis/compute"
+ "yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
+ "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
+)
+
+type IDisk interface {
+ Connect() bool
+ Disconnect() bool
+ MountRootfs() fsdriver.IRootFsDriver
+ UmountRootfs(driver fsdriver.IRootFsDriver)
+}
+
+func GetIDisk(params *apis.DeployParams) IDisk {
+ hypervisor := params.GuestDesc.Hypervisor
+ switch hypervisor {
+ case comapi.HYPERVISOR_KVM:
+ return NewKVMGuestDisk(params.DiskPath)
+ case comapi.HYPERVISOR_ESXI:
+ return NewVDDKDisk(params.VddkInfo, params.DiskPath)
+ default:
+ return NewKVMGuestDisk(params.DiskPath)
+ }
+}
diff --git a/pkg/hostman/diskutils/diskutils.go b/pkg/hostman/diskutils/diskutils.go
index 951e17b195..401aa4c532 100644
--- a/pkg/hostman/diskutils/diskutils.go
+++ b/pkg/hostman/diskutils/diskutils.go
@@ -308,6 +308,10 @@ func (d *SKVMGuestDisk) DetectIsUEFISupport(rootfs fsdriver.IRootFsDriver) bool
return false
}
+func (d *SKVMGuestDisk) MountRootfs() fsdriver.IRootFsDriver {
+ return d.MountKvmRootfs()
+}
+
func (d *SKVMGuestDisk) MountKvmRootfs() fsdriver.IRootFsDriver {
return d.mountKvmRootfs(false)
}
@@ -340,6 +344,10 @@ func (d *SKVMGuestDisk) UmountKvmRootfs(fd fsdriver.IRootFsDriver) {
}
}
+func (d *SKVMGuestDisk) UmountRootfs(fd fsdriver.IRootFsDriver) {
+ d.UmountKvmRootfs(fd)
+}
+
func (d *SKVMGuestDisk) MakePartition(fs string) error {
return Mkpartition(d.nbdDev, fs)
}
diff --git a/pkg/hostman/diskutils/vddk.go b/pkg/hostman/diskutils/vddk.go
new file mode 100644
index 0000000000..a8deb1d57f
--- /dev/null
+++ b/pkg/hostman/diskutils/vddk.go
@@ -0,0 +1,381 @@
+// 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 diskutils
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "crypto/tls"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "yunion.io/x/log"
+ "yunion.io/x/pkg/errors"
+
+ "yunion.io/x/onecloud/pkg/hostman/guestfs"
+ "yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
+ "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
+)
+
+const (
+ TMPDIR = "/tmp/vmware-root"
+)
+
+var (
+ MNT_PATTERN = regexp.MustCompile(`Disk flat file mounted under ([^\s]+)`)
+)
+
+type VDDKDisk struct {
+ Host string
+ Port int
+ User string
+ Passwd string
+ VmRef string
+ DiskPath string
+
+ FUseDir string
+ PartDirs []string
+ Proc *Command
+ Pid int
+}
+
+func NewVDDKDisk(vddkInfo *apis.VDDKConInfo, diskPath string) *VDDKDisk {
+ return &VDDKDisk{
+ Host: vddkInfo.Host,
+ Port: int(vddkInfo.Port),
+ User: vddkInfo.User,
+ Passwd: vddkInfo.Passwd,
+ VmRef: vddkInfo.Vmref,
+ DiskPath: diskPath,
+ }
+}
+
+type Command struct {
+ *exec.Cmd
+ done chan error
+ stdouterr *bytes.Buffer
+ stdin io.Writer
+}
+
+func NewCommand(name string, arg ...string) *Command {
+ cmd := Command{
+ Cmd: exec.Command(name, arg...),
+ done: make(chan error, 1),
+ }
+ cmd.stdouterr = bytes.NewBuffer([]byte{})
+ cmd.Stdout = cmd.stdouterr
+ cmd.Stderr = cmd.stdouterr
+ cmd.stdin, _ = cmd.StdinPipe()
+ return &cmd
+}
+
+func (c *Command) Send(msg []byte) error {
+ _, err := c.stdin.Write(msg)
+ return err
+}
+
+func (c *Command) Start() error {
+ if err := c.Cmd.Start(); err != nil {
+ return err
+ }
+ go func() {
+ c.done <- c.Cmd.Wait()
+ }()
+ return nil
+}
+
+func (c *Command) Exited() bool {
+ return len(c.done) == 1
+}
+
+// Wait will block
+func (c *Command) Wait() error {
+ return <-c.done
+}
+
+func (c *Command) Kill() {
+ c.Process.Kill()
+}
+
+func execpath() string {
+ return "/opt/vmware-vddk/bin/vix-mntapi-sample"
+}
+
+func libdir() string {
+ return "/usr/lib/vmware"
+}
+
+func logpath(pid int) string {
+ return fmt.Sprintf("%s/vixDiskLib-%d.log", TMPDIR, pid)
+}
+
+func (vd *VDDKDisk) Connect() bool {
+ return true
+}
+
+func (vd *VDDKDisk) Disconnect() bool {
+ return true
+}
+
+func (vd *VDDKDisk) MountRootfs() fsdriver.IRootFsDriver {
+ if err := vd.Mount(); err == nil {
+ for _, mntPath := range vd.PartDirs {
+ part := newVDDKPartition(mntPath)
+ if fs := guestfs.DetectRootFs(part); fs != nil {
+ log.Infof("Use rootfs %s", fs)
+ return fs
+ }
+ }
+ }
+ return nil
+}
+
+func (vd *VDDKDisk) UmountRootfs(fd fsdriver.IRootFsDriver) {
+ err := vd.Umount()
+ if err != nil {
+ log.Errorf("VDDKDisk Umount failed: %s", err)
+ }
+}
+
+func (vd *VDDKDisk) ParsePartitions(buf string) error {
+ // Disk flat file mounted under /run/vmware/fuse/7673253059900458465
+ // Mounted Volume 1, Type 1, isMounted 1, symLink /tmp/vmware-root/7673253059900458465_1, numGuestMountPoints 0 ()
+ // print buf
+ ms := MNT_PATTERN.FindAllStringSubmatch(buf, -1)
+ if len(ms) != 0 {
+ vd.FUseDir = ms[0][1]
+ diskId := filepath.Base(vd.FUseDir)
+ files, err := ioutil.ReadDir(TMPDIR)
+ if err != nil {
+ return errors.Wrapf(err, "ioutil.ReadDir for %s", TMPDIR)
+ }
+ for _, f := range files {
+ if strings.HasPrefix(f.Name(), diskId) {
+ vd.PartDirs = append(vd.PartDirs, filepath.Join(TMPDIR, f.Name()))
+ }
+ }
+ }
+ log.Infof("Fuse path: %s partitiaons: %s", vd.FUseDir, vd.PartDirs)
+ return nil
+}
+
+func (vd *VDDKDisk) Mount() (err error) {
+ defer func() {
+ if err == nil {
+ return
+ }
+ vd.Proc = nil
+ log.Errorf("Exec vix-mntapi-sample error: %s", err)
+ }()
+
+ err = vd.ExecProg()
+ if err != nil {
+ return errors.Wrap(err, "VDDKDisk.ExecProg")
+ }
+ err = vd.WaitMounted()
+ if err != nil {
+ return errors.Wrap(err, "VDDKDisk.Mount")
+ }
+ return nil
+}
+
+func (vd *VDDKDisk) Umount() error {
+ if vd.Proc != nil {
+ err := vd.Proc.Send([]byte{'y'})
+ if err != nil {
+ errors.Wrap(err, "send 'y' to VDDKDisk.Proc")
+ }
+ err = vd.Proc.Wait()
+ if err != nil {
+ return errors.Wrap(err, "vd.Proc.Wait")
+ }
+ }
+ if len(vd.FUseDir) != 0 {
+ for _, p := range append(vd.PartDirs, vd.FUseDir) {
+ vd.fuseUmount(p)
+ }
+ }
+ if vd.Pid != 0 {
+ logpath := logpath(vd.Pid)
+ _, err := os.Stat(logpath)
+ if err == nil || os.IsExist(err) {
+ os.Remove(logpath)
+ }
+ }
+ return nil
+}
+
+func (vd *VDDKDisk) fuseUmount(path string) {
+ maxTries, tried := 4, 0
+
+ _, err := os.Stat(path)
+ if err != nil && os.IsNotExist(err) {
+ // no such path
+ return
+ }
+
+ for tried < maxTries {
+ tried += 1
+ err := exec.Command("umount", path).Run()
+ if err != nil {
+ time.Sleep(time.Duration(tried) * 15 * time.Second)
+ log.Errorf("Fail to umount %s: %s", path, err)
+ continue
+ }
+ _, err = os.Stat(path)
+ if err == nil || os.IsExist(err) {
+ err = exec.Command("rm", "-rf", path).Run()
+ if err != nil {
+ time.Sleep(time.Duration(tried) * 15 * time.Second)
+ log.Errorf("Fail to umount %s: %s", path, err)
+ continue
+ }
+ }
+ }
+}
+
+func (vd *VDDKDisk) ExecProg() error {
+ thumb, err := vd.getServerCertThumbSha1(fmt.Sprintf("%s:%d", vd.Host, vd.Port))
+ if err != nil {
+ return errors.Wrapf(err, "Fail contact server %s", vd.Host)
+ }
+ cmd := NewCommand(execpath(), "-info", "-host", vd.Host, "-port", strconv.Itoa(vd.Port), "-user", vd.User,
+ "-password", vd.Passwd, "-mode", "nbd", "-thumb", thumb, "-vm", fmt.Sprintf("moref=%s", vd.VmRef), vd.DiskPath)
+ env := os.Environ()
+ env = append(env, fmt.Sprintf("LD_LIBRARY_PATH=%s", libdir()))
+ cmd.Env = env
+ vd.Proc = cmd
+ err = vd.Proc.Start()
+ if err != nil {
+ return errors.Wrap(err, "vd.Proc.Start")
+ }
+ vd.Pid = cmd.Process.Pid
+ return nil
+}
+
+// getServerCertBin try to obtain the remote ssl certificate
+func (vd *VDDKDisk) getServerCertBin(addr string) ([]byte, error) {
+ rawConn, err := net.Dial("tcp", addr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "net.Dial for addr '%s'", addr)
+ }
+ defer rawConn.Close()
+
+ // get the wrpped conn
+ sslWrappedConn := tls.Client(rawConn, &tls.Config{InsecureSkipVerify: true})
+ err = sslWrappedConn.Handshake()
+ if err != nil {
+ return nil, errors.Wrapf(err, "fail to complete ssl handshake with addr '%s'", addr)
+ }
+ return sslWrappedConn.ConnectionState().PeerCertificates[0].Raw, nil
+}
+
+func (vd *VDDKDisk) getServerCertThumbSha1(addr string) (string, error) {
+ certBin, err := vd.getServerCertBin(addr)
+ if err != nil {
+ return "", err
+ }
+ sha := sha1.Sum(certBin)
+ shaHex := hex.EncodeToString(sha[:])
+ length := len(shaHex) / 2 * 2
+ tmp := make([][]byte, 0, length)
+ for i := 1; i < length; i += 2 {
+ tmp = append(tmp, []byte{shaHex[i-1], shaHex[i]})
+ }
+ return string(bytes.Join(tmp, []byte{':'})), nil
+}
+
+func (vd *VDDKDisk) WaitMounted() error {
+ endStr := []byte("Do you want to procede to unmount the volume")
+ timeout := 30 * time.Second
+ endClock := time.After(timeout)
+ isEnd := false
+
+Loop:
+ for !vd.Proc.Exited() {
+ select {
+ case <-endClock:
+ break Loop
+ default:
+ if bytes.Contains(vd.Proc.stdouterr.Bytes(), endStr) {
+ log.Debugf("find the mark")
+ isEnd = true
+ break Loop
+ }
+ }
+ // Reduce inspection density
+ time.Sleep(100 * time.Millisecond)
+ }
+
+ backup := vd.Proc.stdouterr.String()
+ err := vd.ParsePartitions(backup)
+ if err != nil {
+ return errors.Wrap(err, "VDDKDisk.ParsePartitions")
+ }
+ if vd.Proc.Exited() {
+ retCode := vd.Proc.ProcessState.ExitCode()
+ // ignore the error
+ vd.Proc.Kill()
+ vd.Proc = nil
+ return errors.Error(fmt.Sprintf("VDDKDisk prog exit error(%d): %s", retCode, backup))
+ } else if !isEnd {
+ // timeout
+ vd.Proc.Kill()
+ return errors.Error(fmt.Sprintf("VDDKDisk read timeout, program blocked"))
+ }
+ return nil
+}
+
+type VDDKPartition struct {
+ *guestfs.SLocalGuestFS
+}
+
+func (vp *VDDKPartition) Mount() bool {
+ log.Debugf("VDDKPartition.Mount not implement")
+ return true
+}
+
+func (vp *VDDKPartition) MountPartReadOnly() bool {
+ log.Debugf("VDDKPartition.MountPartReadOnly not implement")
+ return true
+}
+
+func (vp *VDDKPartition) Umount() bool {
+ log.Debugf("VDDKPartition.Umount not implement")
+ return true
+}
+
+func (vp *VDDKPartition) IsReadonly() bool {
+ return guestfs.IsPartitionReadonly(vp)
+}
+
+func (vp *VDDKPartition) GetPhysicalPartitionType() string {
+ log.Debugf("VDDKPartition.GetPhysicalPartitionType not implement")
+ return ""
+}
+
+func newVDDKPartition(mntPath string) *VDDKPartition {
+ return &VDDKPartition{guestfs.NewLocalGuestFS(mntPath)}
+}
diff --git a/pkg/hostman/hostdeployer/apis/deploy.pb.go b/pkg/hostman/hostdeployer/apis/deploy.pb.go
index e420bc0600..2c163dcd60 100644
--- a/pkg/hostman/hostdeployer/apis/deploy.pb.go
+++ b/pkg/hostman/hostdeployer/apis/deploy.pb.go
@@ -46,6 +46,7 @@ type GuestDesc struct {
Nics []*Nic `protobuf:"bytes,4,rep,name=nics,proto3" json:"nics,omitempty"`
NicsStandby []*Nic `protobuf:"bytes,5,rep,name=nics_standby,json=nicsStandby,proto3" json:"nics_standby,omitempty"`
Disks []*Disk `protobuf:"bytes,6,rep,name=disks,proto3" json:"disks,omitempty"`
+ Hypervisor string `protobuf:"bytes,7,opt,name=Hypervisor,proto3" json:"Hypervisor,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -118,6 +119,13 @@ func (m *GuestDesc) GetDisks() []*Disk {
return nil
}
+func (m *GuestDesc) GetHypervisor() string {
+ if m != nil {
+ return m.Hypervisor
+ }
+ return ""
+}
+
type Disk struct {
DiskId string `protobuf:"bytes,1,opt,name=disk_id,json=diskId,proto3" json:"disk_id,omitempty"`
Driver string `protobuf:"bytes,2,opt,name=driver,proto3" json:"driver,omitempty"`
@@ -524,6 +532,77 @@ func (m *Nic) GetName() string {
return ""
}
+type VDDKConInfo struct {
+ Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
+ Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
+ User string `protobuf:"bytes,3,opt,name=user,proto3" json:"user,omitempty"`
+ Passwd string `protobuf:"bytes,4,opt,name=passwd,proto3" json:"passwd,omitempty"`
+ Vmref string `protobuf:"bytes,5,opt,name=vmref,proto3" json:"vmref,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *VDDKConInfo) Reset() { *m = VDDKConInfo{} }
+func (m *VDDKConInfo) String() string { return proto.CompactTextString(m) }
+func (*VDDKConInfo) ProtoMessage() {}
+func (*VDDKConInfo) Descriptor() ([]byte, []int) {
+ return fileDescriptor_05f09e103004e384, []int{3}
+}
+
+func (m *VDDKConInfo) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_VDDKConInfo.Unmarshal(m, b)
+}
+func (m *VDDKConInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_VDDKConInfo.Marshal(b, m, deterministic)
+}
+func (m *VDDKConInfo) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_VDDKConInfo.Merge(m, src)
+}
+func (m *VDDKConInfo) XXX_Size() int {
+ return xxx_messageInfo_VDDKConInfo.Size(m)
+}
+func (m *VDDKConInfo) XXX_DiscardUnknown() {
+ xxx_messageInfo_VDDKConInfo.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_VDDKConInfo proto.InternalMessageInfo
+
+func (m *VDDKConInfo) GetHost() string {
+ if m != nil {
+ return m.Host
+ }
+ return ""
+}
+
+func (m *VDDKConInfo) GetPort() int32 {
+ if m != nil {
+ return m.Port
+ }
+ return 0
+}
+
+func (m *VDDKConInfo) GetUser() string {
+ if m != nil {
+ return m.User
+ }
+ return ""
+}
+
+func (m *VDDKConInfo) GetPasswd() string {
+ if m != nil {
+ return m.Passwd
+ }
+ return ""
+}
+
+func (m *VDDKConInfo) GetVmref() string {
+ if m != nil {
+ return m.Vmref
+ }
+ return ""
+}
+
type DeployInfo struct {
PublicKey *SSHKeys `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
Deploys []*DeployContent `protobuf:"bytes,2,rep,name=deploys,proto3" json:"deploys,omitempty"`
@@ -542,7 +621,7 @@ func (m *DeployInfo) Reset() { *m = DeployInfo{} }
func (m *DeployInfo) String() string { return proto.CompactTextString(m) }
func (*DeployInfo) ProtoMessage() {}
func (*DeployInfo) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{3}
+ return fileDescriptor_05f09e103004e384, []int{4}
}
func (m *DeployInfo) XXX_Unmarshal(b []byte) error {
@@ -633,7 +712,7 @@ func (m *SSHKeys) Reset() { *m = SSHKeys{} }
func (m *SSHKeys) String() string { return proto.CompactTextString(m) }
func (*SSHKeys) ProtoMessage() {}
func (*SSHKeys) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{4}
+ return fileDescriptor_05f09e103004e384, []int{5}
}
func (m *SSHKeys) XXX_Unmarshal(b []byte) error {
@@ -695,7 +774,7 @@ func (m *DeployContent) Reset() { *m = DeployContent{} }
func (m *DeployContent) String() string { return proto.CompactTextString(m) }
func (*DeployContent) ProtoMessage() {}
func (*DeployContent) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{5}
+ return fileDescriptor_05f09e103004e384, []int{6}
}
func (m *DeployContent) XXX_Unmarshal(b []byte) error {
@@ -747,7 +826,7 @@ func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{6}
+ return fileDescriptor_05f09e103004e384, []int{7}
}
func (m *Empty) XXX_Unmarshal(b []byte) error {
@@ -785,7 +864,7 @@ func (m *DeployGuestFsResponse) Reset() { *m = DeployGuestFsResponse{} }
func (m *DeployGuestFsResponse) String() string { return proto.CompactTextString(m) }
func (*DeployGuestFsResponse) ProtoMessage() {}
func (*DeployGuestFsResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{7}
+ return fileDescriptor_05f09e103004e384, []int{8}
}
func (m *DeployGuestFsResponse) XXX_Unmarshal(b []byte) error {
@@ -856,19 +935,20 @@ func (m *DeployGuestFsResponse) GetKey() string {
}
type DeployParams struct {
- DiskPath string `protobuf:"bytes,1,opt,name=disk_path,json=diskPath,proto3" json:"disk_path,omitempty"`
- GuestDesc *GuestDesc `protobuf:"bytes,2,opt,name=guest_desc,json=guestDesc,proto3" json:"guest_desc,omitempty"`
- DeployInfo *DeployInfo `protobuf:"bytes,3,opt,name=deploy_info,json=deployInfo,proto3" json:"deploy_info,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ DiskPath string `protobuf:"bytes,1,opt,name=disk_path,json=diskPath,proto3" json:"disk_path,omitempty"`
+ GuestDesc *GuestDesc `protobuf:"bytes,2,opt,name=guest_desc,json=guestDesc,proto3" json:"guest_desc,omitempty"`
+ DeployInfo *DeployInfo `protobuf:"bytes,3,opt,name=deploy_info,json=deployInfo,proto3" json:"deploy_info,omitempty"`
+ VddkInfo *VDDKConInfo `protobuf:"bytes,4,opt,name=vddk_info,json=vddkInfo,proto3" json:"vddk_info,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
}
func (m *DeployParams) Reset() { *m = DeployParams{} }
func (m *DeployParams) String() string { return proto.CompactTextString(m) }
func (*DeployParams) ProtoMessage() {}
func (*DeployParams) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{8}
+ return fileDescriptor_05f09e103004e384, []int{9}
}
func (m *DeployParams) XXX_Unmarshal(b []byte) error {
@@ -910,6 +990,13 @@ func (m *DeployParams) GetDeployInfo() *DeployInfo {
return nil
}
+func (m *DeployParams) GetVddkInfo() *VDDKConInfo {
+ if m != nil {
+ return m.VddkInfo
+ }
+ return nil
+}
+
type ResizeFsParams struct {
DiskPath string `protobuf:"bytes,1,opt,name=disk_path,json=diskPath,proto3" json:"disk_path,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -921,7 +1008,7 @@ func (m *ResizeFsParams) Reset() { *m = ResizeFsParams{} }
func (m *ResizeFsParams) String() string { return proto.CompactTextString(m) }
func (*ResizeFsParams) ProtoMessage() {}
func (*ResizeFsParams) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{9}
+ return fileDescriptor_05f09e103004e384, []int{10}
}
func (m *ResizeFsParams) XXX_Unmarshal(b []byte) error {
@@ -962,7 +1049,7 @@ func (m *FormatFsParams) Reset() { *m = FormatFsParams{} }
func (m *FormatFsParams) String() string { return proto.CompactTextString(m) }
func (*FormatFsParams) ProtoMessage() {}
func (*FormatFsParams) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{10}
+ return fileDescriptor_05f09e103004e384, []int{11}
}
func (m *FormatFsParams) XXX_Unmarshal(b []byte) error {
@@ -1018,7 +1105,7 @@ func (m *ReleaseInfo) Reset() { *m = ReleaseInfo{} }
func (m *ReleaseInfo) String() string { return proto.CompactTextString(m) }
func (*ReleaseInfo) ProtoMessage() {}
func (*ReleaseInfo) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{11}
+ return fileDescriptor_05f09e103004e384, []int{12}
}
func (m *ReleaseInfo) XXX_Unmarshal(b []byte) error {
@@ -1079,7 +1166,7 @@ func (m *SaveToGlanceParams) Reset() { *m = SaveToGlanceParams{} }
func (m *SaveToGlanceParams) String() string { return proto.CompactTextString(m) }
func (*SaveToGlanceParams) ProtoMessage() {}
func (*SaveToGlanceParams) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{12}
+ return fileDescriptor_05f09e103004e384, []int{13}
}
func (m *SaveToGlanceParams) XXX_Unmarshal(b []byte) error {
@@ -1126,7 +1213,7 @@ func (m *SaveToGlanceResponse) Reset() { *m = SaveToGlanceResponse{} }
func (m *SaveToGlanceResponse) String() string { return proto.CompactTextString(m) }
func (*SaveToGlanceResponse) ProtoMessage() {}
func (*SaveToGlanceResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{13}
+ return fileDescriptor_05f09e103004e384, []int{14}
}
func (m *SaveToGlanceResponse) XXX_Unmarshal(b []byte) error {
@@ -1172,7 +1259,7 @@ func (m *ProbeImageInfoPramas) Reset() { *m = ProbeImageInfoPramas{} }
func (m *ProbeImageInfoPramas) String() string { return proto.CompactTextString(m) }
func (*ProbeImageInfoPramas) ProtoMessage() {}
func (*ProbeImageInfoPramas) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{14}
+ return fileDescriptor_05f09e103004e384, []int{15}
}
func (m *ProbeImageInfoPramas) XXX_Unmarshal(b []byte) error {
@@ -1217,7 +1304,7 @@ func (m *ImageInfo) Reset() { *m = ImageInfo{} }
func (m *ImageInfo) String() string { return proto.CompactTextString(m) }
func (*ImageInfo) ProtoMessage() {}
func (*ImageInfo) Descriptor() ([]byte, []int) {
- return fileDescriptor_05f09e103004e384, []int{15}
+ return fileDescriptor_05f09e103004e384, []int{16}
}
func (m *ImageInfo) XXX_Unmarshal(b []byte) error {
@@ -1291,6 +1378,7 @@ func init() {
proto.RegisterType((*GuestDesc)(nil), "apis.GuestDesc")
proto.RegisterType((*Disk)(nil), "apis.Disk")
proto.RegisterType((*Nic)(nil), "apis.Nic")
+ proto.RegisterType((*VDDKConInfo)(nil), "apis.VDDKConInfo")
proto.RegisterType((*DeployInfo)(nil), "apis.DeployInfo")
proto.RegisterType((*SSHKeys)(nil), "apis.SSHKeys")
proto.RegisterType((*DeployContent)(nil), "apis.DeployContent")
@@ -1309,102 +1397,107 @@ func init() {
func init() { proto.RegisterFile("deploy.proto", fileDescriptor_05f09e103004e384) }
var fileDescriptor_05f09e103004e384 = []byte{
- // 1512 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xdd, 0x6e, 0x1b, 0x37,
- 0x16, 0x86, 0xfe, 0x47, 0x47, 0xb6, 0x6c, 0x33, 0xfe, 0x99, 0x28, 0x9b, 0x8d, 0x21, 0x60, 0x17,
- 0x46, 0x90, 0x18, 0x58, 0x67, 0x77, 0x6f, 0x16, 0x0b, 0x6c, 0x10, 0x6f, 0x52, 0x21, 0x4d, 0x60,
- 0x8c, 0x63, 0xf4, 0xae, 0x03, 0x6a, 0x86, 0x92, 0x59, 0xcf, 0x90, 0x03, 0x92, 0x63, 0x45, 0x7d,
- 0x88, 0x3e, 0x49, 0xaf, 0x02, 0xf4, 0xa6, 0x8f, 0xd1, 0xbb, 0xbe, 0x42, 0x9f, 0xa2, 0x38, 0x24,
- 0x67, 0x34, 0x4e, 0x02, 0x34, 0x37, 0xbd, 0x12, 0xcf, 0x77, 0x0e, 0xc9, 0xc3, 0xf3, 0xf3, 0x9d,
- 0x11, 0x6c, 0xa5, 0xac, 0xc8, 0xe4, 0xfa, 0xb4, 0x50, 0xd2, 0x48, 0xd2, 0xa5, 0x05, 0xd7, 0xd3,
- 0x9f, 0x5b, 0x30, 0x7c, 0x55, 0x32, 0x6d, 0xce, 0x99, 0x4e, 0x08, 0x81, 0xae, 0xa0, 0x39, 0x0b,
- 0x5b, 0xc7, 0xad, 0x93, 0x61, 0x64, 0xd7, 0x88, 0x95, 0x25, 0x4f, 0xc3, 0xb6, 0xc3, 0x70, 0x4d,
- 0x0e, 0xa1, 0x9f, 0xca, 0x9c, 0x72, 0x11, 0x76, 0x2c, 0xea, 0x25, 0xf2, 0x10, 0xba, 0x82, 0x27,
- 0x3a, 0xec, 0x1e, 0x77, 0x4e, 0x46, 0x67, 0xc3, 0x53, 0xbc, 0xe2, 0xf4, 0x2d, 0x4f, 0x22, 0x0b,
- 0x93, 0x27, 0xb0, 0x85, 0xbf, 0xb1, 0x36, 0x54, 0xa4, 0xf3, 0x75, 0xd8, 0xfb, 0xd8, 0x6c, 0x84,
- 0xea, 0x4b, 0xa7, 0x25, 0xc7, 0xd0, 0x4b, 0xb9, 0xbe, 0xd1, 0x61, 0xdf, 0x9a, 0x81, 0x33, 0x3b,
- 0xe7, 0xfa, 0x26, 0x72, 0x8a, 0xe9, 0xaf, 0x1d, 0xe8, 0xa2, 0x4c, 0x8e, 0x60, 0x80, 0x48, 0xcc,
- 0x53, 0xef, 0x7a, 0x1f, 0xc5, 0x99, 0x73, 0x54, 0xf1, 0x5b, 0xa6, 0xbc, 0xfb, 0x5e, 0x22, 0x0f,
- 0x01, 0x12, 0x9a, 0x5c, 0xb3, 0x38, 0x97, 0x29, 0xf3, 0x8f, 0x18, 0x5a, 0xe4, 0x8d, 0x4c, 0x19,
- 0xb9, 0x0f, 0x01, 0xe5, 0xd2, 0x29, 0xbb, 0x56, 0x39, 0xa0, 0x5c, 0x5a, 0x15, 0x81, 0xae, 0xe6,
- 0xdf, 0xb3, 0xb0, 0x77, 0xdc, 0x3a, 0xe9, 0x44, 0x76, 0x4d, 0x1e, 0xc1, 0xc8, 0xb0, 0xbc, 0xc8,
- 0xa8, 0x61, 0xe8, 0x42, 0xdf, 0xee, 0x80, 0x0a, 0x9a, 0xa5, 0x78, 0x1d, 0xcf, 0xe9, 0x92, 0xc5,
- 0x05, 0x35, 0xd7, 0xe1, 0xc0, 0x5d, 0x67, 0x91, 0x0b, 0x6a, 0xae, 0x51, 0xad, 0x8d, 0x54, 0x68,
- 0xc0, 0xd3, 0x30, 0x70, 0x6a, 0x8f, 0xcc, 0x52, 0xf2, 0x17, 0x18, 0xe6, 0x7c, 0xa9, 0xa8, 0xe1,
- 0x62, 0x19, 0x0e, 0x8f, 0x5b, 0x27, 0x41, 0xb4, 0x01, 0xc8, 0x63, 0xd8, 0x33, 0x54, 0x2d, 0x99,
- 0x89, 0x1b, 0x67, 0x80, 0x3d, 0x63, 0xc7, 0x29, 0x2e, 0xeb, 0x93, 0x08, 0x74, 0xad, 0x07, 0x23,
- 0x97, 0x4b, 0x5c, 0x63, 0x88, 0x16, 0x52, 0xe5, 0xd4, 0x84, 0x5b, 0x2e, 0x44, 0x4e, 0x22, 0xfb,
- 0xd0, 0xe3, 0x22, 0x65, 0xef, 0xc3, 0xed, 0xe3, 0xd6, 0x49, 0x2f, 0x72, 0x02, 0xf9, 0x1b, 0x8c,
- 0x73, 0xa6, 0x96, 0x2c, 0xd6, 0x82, 0x16, 0xfa, 0x5a, 0x9a, 0x70, 0x6c, 0x1d, 0xda, 0xb6, 0xe8,
- 0xa5, 0x07, 0xc9, 0x18, 0xda, 0x0b, 0x1d, 0xee, 0xd8, 0x03, 0xdb, 0x0b, 0x4d, 0xfe, 0x0a, 0x90,
- 0xcb, 0x52, 0x98, 0x42, 0x72, 0x61, 0xc2, 0x5d, 0x17, 0xa0, 0x0d, 0x42, 0x76, 0xa1, 0x93, 0xb2,
- 0xdb, 0x70, 0xcf, 0x2a, 0x70, 0x39, 0xfd, 0xad, 0x0b, 0x9d, 0xb7, 0x3c, 0x41, 0x4d, 0x4e, 0x13,
- 0x9f, 0x56, 0x5c, 0xe2, 0xd9, 0xbc, 0xf0, 0xf9, 0x6c, 0xf3, 0x02, 0x2d, 0x04, 0x33, 0x3e, 0x89,
- 0xb8, 0x24, 0x07, 0xd0, 0x17, 0xcc, 0x60, 0x1c, 0x5c, 0xf2, 0x7a, 0x82, 0x99, 0x59, 0x4a, 0x42,
- 0x18, 0xdc, 0x72, 0x65, 0x4a, 0x9a, 0xd9, 0xec, 0x05, 0x51, 0x25, 0xa2, 0x66, 0x49, 0x0d, 0x5b,
- 0xd1, 0xb5, 0x4f, 0x5e, 0x25, 0x5a, 0xc7, 0x84, 0xf6, 0x29, 0xc3, 0x65, 0xa3, 0xf6, 0x83, 0x3b,
- 0xb5, 0x7f, 0x08, 0x7d, 0x25, 0x4b, 0xc3, 0xb4, 0x4d, 0xd1, 0x30, 0xf2, 0x12, 0xe2, 0x7c, 0x61,
- 0xbb, 0xca, 0x25, 0xc5, 0x4b, 0x78, 0x67, 0x4e, 0xf5, 0x4d, 0xc6, 0x84, 0x4d, 0x47, 0x2f, 0xaa,
- 0xc4, 0x46, 0xd1, 0x6e, 0xdd, 0x29, 0xda, 0x43, 0xe8, 0xcf, 0x15, 0x4f, 0x97, 0xcc, 0xa6, 0x64,
- 0x18, 0x79, 0x09, 0xab, 0x7f, 0xc5, 0x95, 0xcd, 0xfb, 0xd8, 0x29, 0x50, 0x74, 0xe9, 0xbe, 0xcd,
- 0xa8, 0xb0, 0x79, 0xe8, 0x45, 0x76, 0x8d, 0xc5, 0xc4, 0x85, 0x61, 0x6a, 0x41, 0x13, 0xe6, 0x13,
- 0xb1, 0x01, 0x30, 0xb6, 0xf3, 0x95, 0x4d, 0x43, 0x2f, 0x6a, 0xcf, 0x57, 0x9b, 0x22, 0x20, 0xcd,
- 0x22, 0x78, 0x04, 0x23, 0x1f, 0xb9, 0x98, 0x17, 0x3a, 0xbc, 0x77, 0xdc, 0xc1, 0x74, 0x7a, 0x68,
- 0x56, 0x68, 0x34, 0x60, 0xef, 0x0d, 0x53, 0x82, 0x65, 0xe8, 0xd5, 0xbe, 0xcb, 0x77, 0x05, 0xcd,
- 0x52, 0xf2, 0x00, 0x86, 0x86, 0xd1, 0x3c, 0x5e, 0x71, 0x73, 0x1d, 0x1e, 0x58, 0x75, 0x80, 0xc0,
- 0x37, 0xdc, 0x55, 0x64, 0x4e, 0x05, 0xa6, 0xe9, 0xd0, 0xa6, 0xc9, 0x4b, 0xd8, 0x95, 0x82, 0x27,
- 0xb1, 0x59, 0x17, 0x2c, 0x3c, 0x72, 0x69, 0x12, 0x3c, 0x79, 0xb7, 0x2e, 0x6c, 0x08, 0x32, 0x2e,
- 0x6e, 0xe2, 0xb2, 0x08, 0x43, 0xb7, 0x07, 0xc5, 0x2b, 0x5b, 0x1c, 0xb9, 0x29, 0xc3, 0xfb, 0xb6,
- 0x5b, 0x71, 0x59, 0x73, 0xdc, 0x64, 0xc3, 0x71, 0xd3, 0x5f, 0xda, 0x00, 0xe7, 0x96, 0x1c, 0x67,
- 0x62, 0x21, 0xc9, 0x13, 0x80, 0xa2, 0x9c, 0x67, 0x3c, 0x89, 0x6f, 0xd8, 0xda, 0x96, 0xde, 0xe8,
- 0x6c, 0xdb, 0xd1, 0xcf, 0xe5, 0xe5, 0x57, 0xaf, 0xd9, 0x5a, 0x47, 0x43, 0x67, 0xf0, 0x9a, 0xad,
- 0xc9, 0x53, 0x18, 0x38, 0x62, 0xd5, 0x61, 0xdb, 0x32, 0xd5, 0x3d, 0xcf, 0x54, 0x16, 0x7c, 0x21,
- 0x85, 0x61, 0xc2, 0x44, 0x95, 0x0d, 0x99, 0x40, 0x50, 0x50, 0xad, 0x57, 0x52, 0xa5, 0xbe, 0x66,
- 0x6b, 0x19, 0x9f, 0xc1, 0x75, 0xcc, 0x05, 0x37, 0xb6, 0x72, 0x83, 0xa8, 0xcf, 0xf5, 0x4c, 0x70,
- 0x83, 0x0c, 0xc1, 0x04, 0x9d, 0x67, 0x2c, 0x36, 0x66, 0xed, 0xab, 0x77, 0xe8, 0x90, 0x77, 0x66,
- 0x8d, 0x1c, 0x90, 0xb2, 0x05, 0x2d, 0x33, 0x13, 0x2b, 0x29, 0x4d, 0x5c, 0x6a, 0xa6, 0x6c, 0x25,
- 0x07, 0xd1, 0x8e, 0x57, 0x44, 0x52, 0x9a, 0x2b, 0xcd, 0x14, 0xf9, 0x0f, 0x4c, 0x56, 0x5c, 0xa4,
- 0x72, 0xa5, 0xe3, 0x6a, 0x0f, 0x4d, 0x73, 0x2e, 0xdc, 0xa6, 0x81, 0xdd, 0x74, 0xe4, 0x2d, 0xce,
- 0x9d, 0xc1, 0x73, 0xd4, 0xdb, 0xcd, 0x8f, 0x61, 0xcf, 0xfb, 0x91, 0x64, 0xb2, 0x4c, 0x9d, 0xab,
- 0x81, 0xbb, 0xc8, 0x29, 0x5e, 0x20, 0x8e, 0x3e, 0x4f, 0x7f, 0x6c, 0xc1, 0xc0, 0x87, 0x0b, 0xfd,
- 0xff, 0x28, 0xa2, 0xc3, 0x66, 0x08, 0xad, 0xff, 0x19, 0x33, 0x2c, 0x6e, 0x58, 0xb9, 0x0e, 0xdf,
- 0x71, 0x8a, 0x8b, 0xda, 0xf6, 0x04, 0x76, 0x9d, 0xbf, 0x0d, 0x53, 0x17, 0xc7, 0xb1, 0xc5, 0x37,
- 0x96, 0x4f, 0x80, 0x14, 0x4a, 0x7e, 0xc7, 0x12, 0xd3, 0xb4, 0x75, 0x94, 0xb0, 0xeb, 0x35, 0xb5,
- 0xf5, 0xf4, 0x0a, 0xb6, 0xef, 0x64, 0xac, 0x26, 0xcb, 0x56, 0x83, 0x2c, 0x43, 0x18, 0x24, 0x4e,
- 0xed, 0xdd, 0xab, 0x44, 0x2c, 0x5a, 0x9a, 0x18, 0x2e, 0xeb, 0x91, 0xe8, 0xa4, 0xe9, 0x00, 0x7a,
- 0xff, 0xcf, 0x0b, 0xb3, 0x9e, 0xfe, 0xd4, 0x82, 0x03, 0x77, 0x81, 0x9d, 0xb7, 0x2f, 0x75, 0xc4,
- 0x74, 0x21, 0x85, 0x66, 0xb6, 0xdf, 0xb9, 0x36, 0x4a, 0x36, 0x86, 0x97, 0x51, 0xd2, 0xf2, 0x15,
- 0x53, 0x1a, 0xcf, 0xf4, 0x97, 0x79, 0x11, 0x5d, 0xa3, 0x2a, 0xb9, 0xf6, 0x57, 0xd9, 0x35, 0xd6,
- 0x55, 0x46, 0xc5, 0xb2, 0xa4, 0xcb, 0x6a, 0x66, 0xd5, 0x32, 0xb6, 0xb5, 0xd4, 0xb6, 0x6c, 0x86,
- 0x51, 0x5b, 0x6a, 0x3c, 0x99, 0x26, 0x09, 0xd2, 0x6f, 0xc5, 0x77, 0x5e, 0xc4, 0x7e, 0xc1, 0x20,
- 0x79, 0xbe, 0xbb, 0x61, 0xeb, 0xe9, 0x0f, 0x2d, 0xd8, 0x72, 0x7e, 0x5f, 0x50, 0x45, 0x73, 0x8d,
- 0xbd, 0x6b, 0x87, 0x6d, 0x23, 0x38, 0x01, 0x02, 0x76, 0x94, 0x9d, 0x02, 0x2c, 0xf1, 0x79, 0x71,
- 0xca, 0x74, 0x62, 0xdd, 0x1e, 0x9d, 0xed, 0xb8, 0x7e, 0xa8, 0x3f, 0x33, 0xa2, 0xe1, 0xb2, 0xfe,
- 0xe2, 0xf8, 0x07, 0x8c, 0x5c, 0x63, 0xc4, 0x5c, 0x2c, 0xa4, 0x7d, 0xd0, 0xe8, 0x6c, 0xb7, 0xd9,
- 0x40, 0xd8, 0x91, 0x11, 0xa4, 0xf5, 0x7a, 0xfa, 0x14, 0xc6, 0x11, 0xc3, 0xb9, 0xfb, 0x52, 0x7f,
- 0x81, 0x47, 0xd3, 0x6f, 0x61, 0xfc, 0xd2, 0x4e, 0xb4, 0x2f, 0x32, 0x47, 0xe5, 0x42, 0xc7, 0x7e,
- 0x22, 0xba, 0xb0, 0x07, 0x0b, 0xed, 0x4e, 0xa8, 0xbf, 0x85, 0x3a, 0x9b, 0x6f, 0xa1, 0xa9, 0x84,
- 0x51, 0xc4, 0x32, 0x46, 0x35, 0xb3, 0xdc, 0xf1, 0xa7, 0x27, 0x73, 0xfa, 0x06, 0xc8, 0x25, 0xbd,
- 0x65, 0xef, 0xe4, 0xab, 0x8c, 0x8a, 0x84, 0x7d, 0xc9, 0xa3, 0x26, 0x10, 0x24, 0x32, 0x2f, 0x14,
- 0xd3, 0xda, 0xde, 0x1e, 0x44, 0xb5, 0x3c, 0x65, 0xb0, 0xdf, 0x3c, 0xae, 0xae, 0xca, 0x23, 0x18,
- 0x48, 0xed, 0xb2, 0xe2, 0x5f, 0x22, 0xb5, 0x7d, 0xe1, 0x3f, 0x61, 0x4b, 0xb9, 0x07, 0x3b, 0xad,
- 0x4b, 0xf2, 0x9e, 0xcb, 0x59, 0x23, 0x14, 0xd1, 0x48, 0x6d, 0x84, 0xe9, 0x33, 0xd8, 0xbf, 0x50,
- 0x72, 0xce, 0x66, 0xf8, 0xd5, 0x83, 0xc8, 0x85, 0xa2, 0x39, 0xfd, 0x83, 0xdc, 0x7d, 0x68, 0xc3,
- 0xb0, 0xde, 0x40, 0x1e, 0xdf, 0xf5, 0xe8, 0xb3, 0x77, 0x56, 0x4e, 0x3a, 0xef, 0xed, 0xa8, 0x68,
- 0x57, 0xde, 0xdb, 0x49, 0xf1, 0x77, 0xd8, 0xe1, 0x3a, 0x2e, 0xd9, 0x82, 0xc7, 0xba, 0x2c, 0x0a,
- 0xa9, 0xdc, 0x97, 0x43, 0x10, 0x6d, 0x73, 0x7d, 0xc5, 0x16, 0xfc, 0xd2, 0x81, 0x48, 0x33, 0x5c,
- 0xc7, 0xd9, 0x6d, 0x1e, 0x17, 0x54, 0x19, 0x6e, 0x3b, 0xdb, 0x71, 0xf2, 0x98, 0xeb, 0xaf, 0x6f,
- 0xf3, 0x8b, 0x0a, 0xc5, 0x61, 0xc7, 0x75, 0xac, 0x18, 0x4d, 0xa5, 0xc8, 0x2a, 0x72, 0x06, 0xae,
- 0x23, 0x8f, 0x90, 0x7f, 0xc3, 0x51, 0x71, 0xbd, 0xd6, 0x3c, 0xa1, 0xd9, 0xe6, 0x30, 0xe7, 0x9b,
- 0xeb, 0xbe, 0x83, 0x4a, 0x5d, 0x1f, 0x6a, 0x5d, 0xfd, 0x17, 0x1c, 0xd9, 0x69, 0xa0, 0x0d, 0xcd,
- 0x32, 0x96, 0x36, 0x29, 0xd7, 0xd1, 0xf4, 0x3e, 0x4e, 0x07, 0xaf, 0xad, 0x79, 0xf7, 0xec, 0x43,
- 0x1b, 0x46, 0xae, 0x75, 0x9e, 0x2f, 0x91, 0x99, 0xfe, 0x57, 0x11, 0x9b, 0xe7, 0x1d, 0x42, 0x9a,
- 0xed, 0xe5, 0xca, 0x67, 0xf2, 0xa0, 0x89, 0x7d, 0x4c, 0x50, 0x4f, 0x21, 0xa8, 0x3a, 0x8e, 0xec,
- 0x57, 0x31, 0x6f, 0x76, 0xe0, 0x64, 0xe4, 0x50, 0xcb, 0x74, 0x68, 0x5e, 0x75, 0x5c, 0x65, 0x7e,
- 0xb7, 0x03, 0xef, 0x9a, 0x9f, 0xc3, 0x56, 0xb3, 0x00, 0x49, 0xe8, 0x27, 0xed, 0x27, 0x35, 0x3e,
- 0x99, 0x7c, 0xaa, 0xa9, 0x7d, 0xfc, 0x2f, 0x8c, 0xef, 0xd6, 0x17, 0xf1, 0xd6, 0x9f, 0xab, 0xba,
- 0x89, 0xa7, 0xa4, 0x1a, 0x9e, 0xf7, 0xed, 0x9f, 0xa2, 0x67, 0xbf, 0x07, 0x00, 0x00, 0xff, 0xff,
- 0xdf, 0xa7, 0xb1, 0x31, 0x24, 0x0d, 0x00, 0x00,
+ // 1591 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcd, 0x6e, 0x23, 0xb9,
+ 0x11, 0x86, 0xfe, 0xd5, 0x25, 0x8f, 0xec, 0xe1, 0x7a, 0xc6, 0xbd, 0xda, 0x6c, 0xd6, 0x10, 0x90,
+ 0xc0, 0x18, 0xcc, 0x18, 0x88, 0x37, 0xc9, 0x25, 0x08, 0x90, 0xc5, 0x38, 0xb3, 0x2b, 0x4c, 0x76,
+ 0x61, 0xb4, 0xc7, 0xc9, 0x2d, 0x0d, 0xba, 0x9b, 0x92, 0x18, 0x77, 0x93, 0x0d, 0x92, 0x92, 0x56,
+ 0x79, 0xa6, 0x9c, 0x16, 0xc8, 0x35, 0x0f, 0x91, 0x5b, 0xf2, 0x08, 0x79, 0x8a, 0xa0, 0x8a, 0xec,
+ 0x56, 0x7b, 0x76, 0x80, 0xcc, 0x25, 0x27, 0x55, 0x7d, 0x55, 0x24, 0x8b, 0xf5, 0xf3, 0xb1, 0x05,
+ 0x47, 0xb9, 0xa8, 0x0a, 0xbd, 0xbf, 0xac, 0x8c, 0x76, 0x9a, 0xf5, 0x79, 0x25, 0xed, 0xfc, 0xdf,
+ 0x1d, 0x88, 0xbe, 0xde, 0x08, 0xeb, 0xae, 0x85, 0xcd, 0x18, 0x83, 0xbe, 0xe2, 0xa5, 0x88, 0x3b,
+ 0xe7, 0x9d, 0x8b, 0x28, 0x21, 0x19, 0xb1, 0xcd, 0x46, 0xe6, 0x71, 0xd7, 0x63, 0x28, 0xb3, 0xe7,
+ 0x30, 0xcc, 0x75, 0xc9, 0xa5, 0x8a, 0x7b, 0x84, 0x06, 0x8d, 0x7d, 0x0e, 0x7d, 0x25, 0x33, 0x1b,
+ 0xf7, 0xcf, 0x7b, 0x17, 0x93, 0xab, 0xe8, 0x12, 0x8f, 0xb8, 0xfc, 0x4e, 0x66, 0x09, 0xc1, 0xec,
+ 0x25, 0x1c, 0xe1, 0x6f, 0x6a, 0x1d, 0x57, 0xf9, 0xfd, 0x3e, 0x1e, 0xbc, 0xef, 0x36, 0x41, 0xf3,
+ 0xad, 0xb7, 0xb2, 0x73, 0x18, 0xe4, 0xd2, 0x3e, 0xd8, 0x78, 0x48, 0x6e, 0xe0, 0xdd, 0xae, 0xa5,
+ 0x7d, 0x48, 0xbc, 0x81, 0xfd, 0x14, 0xe0, 0x9b, 0x7d, 0x25, 0xcc, 0x56, 0x5a, 0x6d, 0xe2, 0x11,
+ 0x85, 0xd2, 0x42, 0xe6, 0xff, 0xea, 0x41, 0x1f, 0xfd, 0xd9, 0x19, 0x8c, 0x70, 0x45, 0x2a, 0xf3,
+ 0x70, 0xb5, 0x21, 0xaa, 0x0b, 0x7f, 0x11, 0x23, 0xb7, 0xc2, 0x84, 0xeb, 0x05, 0x8d, 0x7d, 0x0e,
+ 0x90, 0xf1, 0x6c, 0x2d, 0xd2, 0x52, 0xe7, 0x22, 0x5c, 0x32, 0x22, 0xe4, 0x5b, 0x9d, 0x0b, 0xf6,
+ 0x29, 0x8c, 0xb9, 0xd4, 0xde, 0xd8, 0x27, 0xe3, 0x88, 0x4b, 0x4d, 0x26, 0x06, 0x7d, 0x2b, 0xff,
+ 0x2a, 0xe2, 0xc1, 0x79, 0xe7, 0xa2, 0x97, 0x90, 0xcc, 0xbe, 0x80, 0x89, 0x13, 0x65, 0x55, 0x70,
+ 0x27, 0x30, 0x84, 0xa1, 0x0f, 0xb4, 0x86, 0x16, 0x39, 0x1e, 0x27, 0x4b, 0xbe, 0x12, 0x69, 0xc5,
+ 0xdd, 0x3a, 0x5c, 0x24, 0x22, 0xe4, 0x86, 0xbb, 0x35, 0x9a, 0xad, 0xd3, 0x06, 0x1d, 0x64, 0x1e,
+ 0x8f, 0xbd, 0x39, 0x20, 0x8b, 0x9c, 0xfd, 0x04, 0xa2, 0x52, 0xae, 0x0c, 0x77, 0x52, 0xad, 0xe2,
+ 0xe8, 0xbc, 0x73, 0x31, 0x4e, 0x0e, 0x00, 0x7b, 0x01, 0x4f, 0x1d, 0x37, 0x2b, 0xe1, 0xd2, 0xd6,
+ 0x1e, 0x40, 0x7b, 0x1c, 0x7b, 0xc3, 0x6d, 0xb3, 0x13, 0x83, 0x3e, 0x45, 0x30, 0xf1, 0xb5, 0x46,
+ 0x19, 0x53, 0xb4, 0xd4, 0xa6, 0xe4, 0x2e, 0x3e, 0xf2, 0x29, 0xf2, 0x1a, 0x3b, 0x85, 0x81, 0x54,
+ 0xb9, 0xf8, 0x3e, 0x7e, 0x72, 0xde, 0xb9, 0x18, 0x24, 0x5e, 0x61, 0x3f, 0x83, 0x69, 0x29, 0xcc,
+ 0x4a, 0xa4, 0x56, 0xf1, 0xca, 0xae, 0xb5, 0x8b, 0xa7, 0x14, 0xd0, 0x13, 0x42, 0x6f, 0x03, 0xc8,
+ 0xa6, 0xd0, 0x5d, 0xda, 0xf8, 0x98, 0x36, 0xec, 0x2e, 0xa9, 0x92, 0xa5, 0xde, 0x28, 0x57, 0x69,
+ 0xa9, 0x5c, 0x7c, 0xe2, 0x13, 0x74, 0x40, 0xd8, 0x09, 0xf4, 0x72, 0xb1, 0x8d, 0x9f, 0x92, 0x01,
+ 0xc5, 0xf9, 0x7f, 0xfa, 0xd0, 0xfb, 0x4e, 0x66, 0x68, 0x29, 0x79, 0x16, 0xca, 0x8a, 0x22, 0xee,
+ 0x2d, 0xab, 0x50, 0xcf, 0xae, 0xac, 0xd0, 0x43, 0x09, 0x17, 0x8a, 0x88, 0x22, 0x7b, 0x06, 0x43,
+ 0x25, 0x1c, 0xe6, 0xc1, 0x17, 0x6f, 0xa0, 0x84, 0x5b, 0xe4, 0x2c, 0x86, 0xd1, 0x56, 0x1a, 0xb7,
+ 0xe1, 0x05, 0x55, 0x6f, 0x9c, 0xd4, 0x2a, 0x5a, 0x56, 0xdc, 0x89, 0x1d, 0xdf, 0x87, 0xe2, 0xd5,
+ 0x2a, 0x05, 0xa6, 0x6c, 0x28, 0x19, 0x8a, 0xad, 0xd9, 0x18, 0x3f, 0x9a, 0x8d, 0xe7, 0x30, 0x34,
+ 0x7a, 0xe3, 0x84, 0xa5, 0x12, 0x45, 0x49, 0xd0, 0x10, 0x97, 0x4b, 0x9a, 0x3a, 0x5f, 0x94, 0xa0,
+ 0xe1, 0x99, 0x25, 0xb7, 0x0f, 0x85, 0x50, 0x54, 0x8e, 0x41, 0x52, 0xab, 0xad, 0xa6, 0x3d, 0x7a,
+ 0xd4, 0xb4, 0xcf, 0x61, 0x78, 0x6f, 0x64, 0xbe, 0x12, 0x54, 0x92, 0x28, 0x09, 0x1a, 0x76, 0xff,
+ 0x4e, 0x1a, 0xaa, 0xfb, 0xd4, 0x1b, 0x50, 0xf5, 0xe5, 0xde, 0x16, 0x5c, 0x51, 0x1d, 0x06, 0x09,
+ 0xc9, 0xd8, 0x4c, 0x52, 0x39, 0x61, 0x96, 0x3c, 0x13, 0xa1, 0x10, 0x07, 0x00, 0x73, 0x7b, 0xbf,
+ 0xa3, 0x32, 0x0c, 0x92, 0xee, 0xfd, 0xee, 0xd0, 0x04, 0xac, 0xdd, 0x04, 0x5f, 0xc0, 0x24, 0x64,
+ 0x2e, 0x95, 0x95, 0x8d, 0x3f, 0x39, 0xef, 0x61, 0x39, 0x03, 0xb4, 0xa8, 0x2c, 0x3a, 0x88, 0xef,
+ 0x9d, 0x30, 0x4a, 0x14, 0x18, 0xd5, 0xa9, 0xaf, 0x77, 0x0d, 0x2d, 0x72, 0xf6, 0x19, 0x44, 0x4e,
+ 0xf0, 0x32, 0xdd, 0x49, 0xb7, 0x8e, 0x9f, 0x91, 0x79, 0x8c, 0xc0, 0x9f, 0xa4, 0xef, 0xc8, 0x92,
+ 0x2b, 0x2c, 0xd3, 0x73, 0x2a, 0x53, 0xd0, 0x70, 0x2a, 0x95, 0xcc, 0x52, 0xb7, 0xaf, 0x44, 0x7c,
+ 0xe6, 0xcb, 0xa4, 0x64, 0xf6, 0x6e, 0x5f, 0x51, 0x0a, 0x0a, 0xa9, 0x1e, 0xd2, 0x4d, 0x15, 0xc7,
+ 0x7e, 0x0d, 0xaa, 0x77, 0xd4, 0x1c, 0xa5, 0xdb, 0xc4, 0x9f, 0xd2, 0xb4, 0xa2, 0xd8, 0x70, 0xe0,
+ 0xec, 0xc0, 0x81, 0xf3, 0x1d, 0x4c, 0xfe, 0x78, 0x7d, 0xfd, 0xf6, 0xb5, 0x56, 0x0b, 0xb5, 0xd4,
+ 0xe8, 0xb2, 0xd6, 0xd6, 0xd5, 0x34, 0x89, 0x32, 0x8d, 0x8e, 0x36, 0x8e, 0xfa, 0x6e, 0x90, 0x90,
+ 0x4c, 0xd4, 0x69, 0x85, 0x09, 0xad, 0x47, 0x32, 0x06, 0x5f, 0x71, 0x6b, 0x77, 0x75, 0xef, 0x05,
+ 0x0d, 0x33, 0xb9, 0x2d, 0x8d, 0x58, 0x52, 0xeb, 0x45, 0x89, 0x57, 0xe6, 0xff, 0xec, 0x02, 0x5c,
+ 0x13, 0x6b, 0xd3, 0xc1, 0x2f, 0x01, 0xaa, 0xcd, 0x7d, 0x21, 0xb3, 0xf4, 0x41, 0xec, 0xe9, 0xf8,
+ 0xc9, 0xd5, 0x13, 0xcf, 0x8b, 0xb7, 0xb7, 0xdf, 0xbc, 0x15, 0x7b, 0x9b, 0x44, 0xde, 0xe1, 0xad,
+ 0xd8, 0xb3, 0x57, 0x30, 0xf2, 0x8c, 0x6f, 0xe3, 0x2e, 0x51, 0xe8, 0x27, 0x81, 0x42, 0x09, 0x7c,
+ 0xad, 0x95, 0x13, 0xca, 0x25, 0xb5, 0x0f, 0x9b, 0xc1, 0x98, 0x62, 0xd1, 0x26, 0x0f, 0x11, 0x37,
+ 0x3a, 0xe6, 0x4f, 0xda, 0x54, 0x2a, 0xe9, 0x28, 0xec, 0x71, 0x32, 0x94, 0x76, 0xa1, 0xa4, 0x43,
+ 0x6a, 0x12, 0x8a, 0xdf, 0x17, 0x22, 0x75, 0x6e, 0x1f, 0xc6, 0x26, 0xf2, 0xc8, 0x3b, 0xb7, 0x47,
+ 0xf2, 0xc9, 0xc5, 0x92, 0x6f, 0x0a, 0x97, 0x1a, 0xad, 0x5d, 0x4a, 0xe9, 0x18, 0x92, 0xd7, 0x71,
+ 0x30, 0x24, 0x5a, 0xbb, 0x3b, 0xcc, 0xcc, 0x6f, 0x60, 0xb6, 0x93, 0x2a, 0xd7, 0x3b, 0x9b, 0xd6,
+ 0x6b, 0x78, 0x5e, 0x4a, 0xe5, 0x17, 0x8d, 0x68, 0xd1, 0x59, 0xf0, 0xb8, 0xf6, 0x0e, 0x5f, 0xa1,
+ 0x9d, 0x16, 0xbf, 0x80, 0xa7, 0x21, 0x8e, 0xac, 0xd0, 0x9b, 0xdc, 0x87, 0x3a, 0xf6, 0x07, 0x79,
+ 0xc3, 0x6b, 0xc4, 0x31, 0xe6, 0xf9, 0xdf, 0x3a, 0x30, 0x0a, 0xe9, 0xc2, 0xf8, 0xdf, 0xcb, 0x68,
+ 0xd4, 0x4e, 0x21, 0xc5, 0x5f, 0x08, 0x27, 0xd2, 0x96, 0x97, 0xa7, 0x96, 0x63, 0x6f, 0xb8, 0x69,
+ 0x7c, 0x2f, 0xe0, 0xc4, 0xc7, 0xdb, 0x72, 0xf5, 0x79, 0x9c, 0x12, 0x7e, 0xf0, 0x7c, 0x09, 0xac,
+ 0x32, 0xfa, 0x2f, 0x22, 0x73, 0x6d, 0x5f, 0xdf, 0x0f, 0x27, 0xc1, 0xd2, 0x78, 0xcf, 0xef, 0xe0,
+ 0xc9, 0xa3, 0x8a, 0x35, 0x2c, 0xdd, 0x69, 0xb1, 0x74, 0x0c, 0xa3, 0xcc, 0x9b, 0x43, 0x78, 0xb5,
+ 0x8a, 0x0d, 0xc7, 0x33, 0x27, 0x75, 0xf3, 0x56, 0x7b, 0x6d, 0x3e, 0x82, 0xc1, 0xef, 0xcb, 0xca,
+ 0xed, 0xe7, 0x7f, 0xef, 0xc0, 0x33, 0x7f, 0x00, 0x7d, 0x08, 0xbc, 0xb1, 0x89, 0xb0, 0x95, 0x56,
+ 0x56, 0x10, 0xd1, 0x48, 0xeb, 0x8c, 0x6e, 0xbd, 0x9a, 0xce, 0x68, 0x22, 0x4a, 0x61, 0x2c, 0xee,
+ 0x19, 0x0e, 0x0b, 0x2a, 0x86, 0xc6, 0x4d, 0xb6, 0xae, 0x3b, 0x1e, 0x65, 0xec, 0xab, 0x82, 0xab,
+ 0xd5, 0x86, 0xaf, 0xea, 0xc7, 0xb2, 0xd1, 0x91, 0x4f, 0xb4, 0x0d, 0x2d, 0xdf, 0xd5, 0x16, 0x77,
+ 0xe6, 0x59, 0x86, 0xbc, 0x5f, 0x13, 0x6d, 0x50, 0x71, 0x50, 0x31, 0x49, 0x81, 0x68, 0x1f, 0xc4,
+ 0x7e, 0xfe, 0x8f, 0x0e, 0x1c, 0xf9, 0xb8, 0x6f, 0xb8, 0xe1, 0xa5, 0x45, 0xd2, 0xa0, 0x57, 0xbe,
+ 0x95, 0x9c, 0x31, 0x02, 0xf4, 0x86, 0x5e, 0x02, 0xac, 0xf0, 0x7a, 0x69, 0x2e, 0x6c, 0x46, 0x61,
+ 0x4f, 0xae, 0x8e, 0xfd, 0x3c, 0x34, 0xdf, 0x3f, 0x49, 0xb4, 0x6a, 0x3e, 0x85, 0x7e, 0x01, 0x13,
+ 0x3f, 0x18, 0xa9, 0x54, 0x4b, 0x4d, 0x17, 0x9a, 0x5c, 0x9d, 0xb4, 0x07, 0x08, 0x27, 0x32, 0x81,
+ 0xfc, 0x30, 0x9d, 0x97, 0x10, 0x6d, 0xf3, 0xfc, 0xc1, 0x2f, 0xe8, 0xd3, 0x82, 0xa7, 0x7e, 0x41,
+ 0x8b, 0x3c, 0x92, 0x31, 0xfa, 0xa0, 0x34, 0x7f, 0x05, 0xd3, 0x44, 0xe0, 0x07, 0xc2, 0x1b, 0xfb,
+ 0x11, 0x37, 0x98, 0xff, 0x19, 0xa6, 0x6f, 0xe8, 0xe9, 0xfd, 0x28, 0x77, 0x34, 0x2e, 0x6d, 0x1a,
+ 0x9e, 0x6e, 0x5f, 0xa6, 0xf1, 0xd2, 0xfa, 0x1d, 0x9a, 0x8f, 0xba, 0xde, 0xe1, 0xa3, 0x6e, 0xae,
+ 0x61, 0x92, 0x88, 0x42, 0x70, 0x2b, 0xe8, 0x36, 0xff, 0xf7, 0xe2, 0xcf, 0xbf, 0x05, 0x76, 0xcb,
+ 0xb7, 0xe2, 0x9d, 0xfe, 0xba, 0xe0, 0x2a, 0x13, 0x1f, 0x73, 0xa9, 0x19, 0x8c, 0x33, 0x5d, 0x56,
+ 0x46, 0x58, 0x4b, 0xa7, 0x8f, 0x93, 0x46, 0x9f, 0x0b, 0x38, 0x6d, 0x6f, 0xd7, 0x74, 0xf1, 0x19,
+ 0x8c, 0xb4, 0xf5, 0x45, 0x09, 0x37, 0xd1, 0x96, 0x6e, 0xf8, 0x4b, 0x38, 0x32, 0xfe, 0xc2, 0xde,
+ 0xda, 0x6d, 0x97, 0xac, 0x95, 0x8a, 0x64, 0x62, 0x0e, 0xca, 0xfc, 0x4b, 0x38, 0xbd, 0x31, 0xfa,
+ 0x5e, 0x2c, 0xf0, 0xf3, 0x0c, 0x91, 0x1b, 0xc3, 0x4b, 0xfe, 0x3f, 0x6a, 0xf7, 0x43, 0x17, 0xa2,
+ 0x66, 0x01, 0x7b, 0xf1, 0x38, 0xa2, 0x0f, 0x9e, 0x59, 0x07, 0xe9, 0xa3, 0xa7, 0x37, 0xad, 0x5b,
+ 0x47, 0x4f, 0x4f, 0xda, 0xcf, 0xe1, 0x58, 0xda, 0x74, 0x23, 0x96, 0x32, 0xb5, 0x9b, 0x8a, 0xde,
+ 0x9e, 0x9e, 0xff, 0xd4, 0x92, 0xf6, 0x4e, 0x2c, 0xe5, 0xad, 0x07, 0x91, 0x96, 0xa4, 0x4d, 0x8b,
+ 0x6d, 0x99, 0x56, 0xdc, 0x38, 0x49, 0x4c, 0xe0, 0x39, 0x7c, 0x2a, 0xed, 0x1f, 0xb6, 0xe5, 0x4d,
+ 0x8d, 0xe2, 0xab, 0x2c, 0x6d, 0x6a, 0x04, 0xcf, 0xb5, 0x2a, 0x6a, 0x32, 0x07, 0x69, 0x93, 0x80,
+ 0xb0, 0x5f, 0xc3, 0x59, 0xb5, 0xde, 0x5b, 0x99, 0xf1, 0xe2, 0xb0, 0x99, 0x8f, 0xcd, 0x4f, 0xeb,
+ 0xb3, 0xda, 0xdc, 0x6c, 0x4a, 0xa1, 0xfe, 0x0a, 0xce, 0xe8, 0xf5, 0xb0, 0x8e, 0x17, 0x85, 0xc8,
+ 0xdb, 0x14, 0xed, 0x69, 0xfd, 0x14, 0x5f, 0x93, 0x60, 0x6d, 0x78, 0xfa, 0xea, 0x87, 0x2e, 0x4c,
+ 0xfc, 0xa8, 0x7d, 0xb5, 0x42, 0x26, 0xfb, 0x5d, 0x4d, 0x84, 0x81, 0xa7, 0x18, 0x6b, 0x8f, 0xa3,
+ 0x6f, 0x9f, 0xd9, 0x67, 0x6d, 0xec, 0x7d, 0x42, 0x7b, 0x05, 0xe3, 0x7a, 0xe2, 0xd8, 0x69, 0x9d,
+ 0xf3, 0xf6, 0x04, 0xce, 0x26, 0x1e, 0x25, 0x66, 0x44, 0xf7, 0x7a, 0xe2, 0x6a, 0xf7, 0xc7, 0x13,
+ 0xf8, 0xd8, 0xfd, 0x1a, 0x8e, 0xda, 0x0d, 0xc8, 0xe2, 0xf0, 0x32, 0xff, 0xa8, 0xc7, 0x67, 0xb3,
+ 0x1f, 0x5b, 0x9a, 0x18, 0x7f, 0x0b, 0xd3, 0xc7, 0xfd, 0xc5, 0x82, 0xf7, 0x87, 0xba, 0x6e, 0x16,
+ 0x28, 0xac, 0x81, 0xef, 0x87, 0xf4, 0xef, 0xee, 0xcb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xb9,
+ 0xf7, 0xec, 0x82, 0xed, 0x0d, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
diff --git a/pkg/hostman/hostdeployer/apis/deploy.proto b/pkg/hostman/hostdeployer/apis/deploy.proto
index d8b2e671a9..6fa1720fa4 100644
--- a/pkg/hostman/hostdeployer/apis/deploy.proto
+++ b/pkg/hostman/hostdeployer/apis/deploy.proto
@@ -10,6 +10,8 @@ message GuestDesc {
repeated Nic nics = 4;
repeated Nic nics_standby = 5;
repeated Disk disks = 6;
+
+ string Hypervisor = 7;
}
message Disk {
@@ -32,7 +34,6 @@ message Disk {
string dev = 17;
}
-
message Nic {
string mac = 1;
string ip = 2;
@@ -62,6 +63,14 @@ message Nic {
string name = 26;
}
+message VDDKConInfo {
+ string host = 1;
+ int32 port = 2;
+ string user = 3;
+ string passwd = 4;
+ string vmref = 5;
+}
+
message DeployInfo {
SSHKeys public_key = 1;
repeated DeployContent deploys = 2;
@@ -103,6 +112,7 @@ message DeployParams {
string disk_path = 1;
GuestDesc guest_desc = 2;
DeployInfo deploy_info = 3;
+ VDDKConInfo vddk_info = 4;
}
message ResizeFsParams {
diff --git a/pkg/hostman/hostdeployer/deployserver/deployserver.go b/pkg/hostman/hostdeployer/deployserver/deployserver.go
index 443ad9bfdf..1f297fc660 100644
--- a/pkg/hostman/hostdeployer/deployserver/deployserver.go
+++ b/pkg/hostman/hostdeployer/deployserver/deployserver.go
@@ -16,17 +16,19 @@ package deployserver
import (
"context"
- "errors"
"fmt"
"net"
"os"
+ "runtime/debug"
"strings"
"google.golang.org/grpc"
execlient "yunion.io/x/executor/client"
"yunion.io/x/log"
+ "yunion.io/x/pkg/errors"
+ comapi "yunion.io/x/onecloud/pkg/apis/compute"
common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
"yunion.io/x/onecloud/pkg/cloudcommon/service"
"yunion.io/x/onecloud/pkg/hostman/diskutils"
@@ -43,21 +45,35 @@ import (
type DeployerServer struct{}
func (*DeployerServer) DeployGuestFs(ctx context.Context, req *deployapi.DeployParams,
-) (*deployapi.DeployGuestFsResponse, error) {
+) (res *deployapi.DeployGuestFsResponse, err error) {
+ // There will be some occasional unknown panic, so temporarily capture panic here.
+ defer func() {
+ if r := recover(); r != nil {
+ log.Errorf("DeployGuestFs: %s", r)
+ debug.PrintStack()
+ msg := "panic: "
+ if str, ok := r.(fmt.Stringer); ok {
+ msg += str.String()
+ }
+ res, err = nil, errors.Error(msg)
+ }
+ }()
log.Infof("Deploy guest fs on %s", req.DiskPath)
- var kvmDisk = diskutils.NewKVMGuestDisk(req.DiskPath)
- defer kvmDisk.Disconnect()
- if !kvmDisk.Connect() {
- log.Infof("Failed to connect kvm disk")
- return new(deployapi.DeployGuestFsResponse), nil
+ var disk = diskutils.GetIDisk(req)
+ if len(req.GuestDesc.Hypervisor) == 0 {
+ req.GuestDesc.Hypervisor = comapi.HYPERVISOR_KVM
}
-
- root := kvmDisk.MountKvmRootfs()
+ defer disk.Disconnect()
+ if !disk.Connect() {
+ log.Infof("Failed to connect %s disk", req.GuestDesc.Hypervisor)
+ return new(deployapi.DeployGuestFsResponse), errors.Error("disk connect failed")
+ }
+ root := disk.MountRootfs()
if root == nil {
- log.Infof("Failed mounting rootfs for kvm disk")
- return new(deployapi.DeployGuestFsResponse), nil
+ log.Infof("Failed mounting rootfs for %s disk", req.GuestDesc.Hypervisor)
+ return new(deployapi.DeployGuestFsResponse), errors.Error("rootfs mount failed")
}
- defer kvmDisk.UmountKvmRootfs(root)
+ defer disk.UmountRootfs(root)
ret, err := guestfs.DoDeployGuestFs(root, req.GuestDesc, req.DeployInfo)
if err != nil {
@@ -75,7 +91,7 @@ func (*DeployerServer) ResizeFs(ctx context.Context, req *deployapi.ResizeFsPara
disk := diskutils.NewKVMGuestDisk(req.DiskPath)
defer disk.Disconnect()
if !disk.Connect() {
- return new(deployapi.Empty), errors.New("resize fs disk connect failed")
+ return new(deployapi.Empty), errors.Error("resize fs disk connect failed")
}
root := disk.MountKvmRootfs()
@@ -166,7 +182,7 @@ func (*DeployerServer) ProbeImageInfo(ctx context.Context, req *deployapi.ProbeI
defer kvmDisk.Disconnect()
if !kvmDisk.Connect() {
log.Infof("Failed to connect kvm disk")
- return new(deployapi.ImageInfo), errors.New("Disk connector failed to connect image")
+ return new(deployapi.ImageInfo), errors.Error("Disk connector failed to connect image")
}
// Fsck is executed during mount
diff --git a/pkg/hostman/storageman/disk_agent.go b/pkg/hostman/storageman/disk_agent.go
new file mode 100644
index 0000000000..fcb189fcdc
--- /dev/null
+++ b/pkg/hostman/storageman/disk_agent.go
@@ -0,0 +1,117 @@
+// 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 storageman
+
+import (
+ "context"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/pkg/errors"
+
+ "yunion.io/x/onecloud/pkg/cloudprovider"
+ "yunion.io/x/onecloud/pkg/compute/models"
+ "yunion.io/x/onecloud/pkg/hostman/hostutils"
+ "yunion.io/x/onecloud/pkg/httperrors"
+ "yunion.io/x/onecloud/pkg/multicloud/esxi"
+)
+
+type SAgentDisk struct {
+ SLocalDisk
+}
+
+func NewAgentDisk(storage IStorage, id string) *SAgentDisk {
+ return &SAgentDisk{*NewLocalDisk(storage, id)}
+}
+
+type PrepareSaveToGlanceParams struct {
+ TaskId string
+ DiskInfo jsonutils.JSONObject
+}
+
+func (sd *SAgentDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
+ p, ok := params.(PrepareSaveToGlanceParams)
+ if !ok {
+ return nil, errors.Wrap(hostutils.ParamsError, "Resize params format error")
+ }
+ storage := sd.Storage.(*SAgentStorage)
+ return storage.PrepareSaveToGlance(ctx, p.TaskId, p.DiskInfo)
+}
+
+func (sd *SAgentDisk) ReSize(ctx context.Context, diskInfo interface{}) (jsonutils.JSONObject, error) {
+ body, ok := diskInfo.(*jsonutils.JSONDict)
+ if !ok {
+ return nil, errors.Wrap(hostutils.ParamsError, "PrepareSaveToGlance params format error")
+ }
+ // check parameters
+ params := []string{"size", "host_info", "vm_private_id", "disk_private_id"}
+ for _, param := range params {
+ if !body.Contains(param) {
+ return nil, httperrors.NewMissingParameterError(param)
+ }
+ }
+
+ type sResize struct {
+ SizeMb int64 `json:"size"`
+ HostInfo models.SVCenterAccessInfo
+ VMId string `json:"vm_private_id"`
+ DiskId string `json:"disk_private_id"`
+ }
+ resize := sResize{}
+ err := body.Unmarshal(&resize)
+ if err != nil {
+ return nil, errors.Wrapf(err, "%s: unmarshal to sResize", hostutils.ParamsError)
+ }
+
+ esxiClient, err := esxi.NewESXiClientFromAccessInfo(ctx, &resize.HostInfo)
+ if err != nil {
+ return nil, httperrors.NewInputParameterError("info of host_info error")
+ }
+ host, err := esxiClient.FindHostByIp(resize.HostInfo.PrivateId)
+ if err != nil {
+ return nil, errors.Wrapf(err, "fail to find host by ip %s", resize.HostInfo.PrivateId)
+ }
+ ivm, err := host.GetIVMById(resize.VMId)
+ if err != nil {
+ return nil, errors.Wrapf(err, "fail to find vm by ID %s", resize.VMId)
+ }
+ idisks, err := ivm.GetIDisks()
+ if err != nil {
+ return nil, errors.Wrapf(err, "fail to get idisks of vm %s", resize.VMId)
+ }
+ var (
+ idisk cloudprovider.ICloudDisk
+ hasDisk bool
+ )
+ for i := range idisks {
+ if idisks[i].GetId() == resize.DiskId {
+ idisk = idisks[i]
+ hasDisk = true
+ }
+ }
+ if !hasDisk {
+ return nil, errors.Wrapf(err, "no such disk %s", resize.DiskId)
+ }
+ url := idisk.GetAccessPath()
+ vm, disk := ivm.(*esxi.SVirtualMachine), idisk.(*esxi.SVirtualDisk)
+ online := jsonutils.QueryBoolean(body, "online", false)
+ if online {
+ esxiClient.DoExtendDiskOnline(vm, disk, resize.SizeMb)
+ } else {
+ esxiClient.ExtendDisk(url, resize.SizeMb)
+ }
+ desc := jsonutils.NewDict()
+ desc.Add(jsonutils.NewInt(resize.SizeMb), "disk_size")
+ return desc, nil
+}
diff --git a/pkg/hostman/storageman/disk_rbd.go b/pkg/hostman/storageman/disk_rbd.go
index 88b55fe4d4..68584dd99c 100644
--- a/pkg/hostman/storageman/disk_rbd.go
+++ b/pkg/hostman/storageman/disk_rbd.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// +build linux
+// +build linux,cgo
package storageman
diff --git a/pkg/hostman/storageman/imagecache_rbd.go b/pkg/hostman/storageman/imagecache_rbd.go
index b5bcc336ce..1dbc9b5e6e 100644
--- a/pkg/hostman/storageman/imagecache_rbd.go
+++ b/pkg/hostman/storageman/imagecache_rbd.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// +build linux
+// +build linux,cgo
package storageman
diff --git a/pkg/hostman/storageman/imagecachemanager_agent.go b/pkg/hostman/storageman/imagecachemanager_agent.go
index 6ed518f60f..91c679d00d 100644
--- a/pkg/hostman/storageman/imagecachemanager_agent.go
+++ b/pkg/hostman/storageman/imagecachemanager_agent.go
@@ -14,11 +14,217 @@
package storageman
-// not need to impl, using esxi agent
+import (
+ "context"
+ "fmt"
+ "path/filepath"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/log"
+ "yunion.io/x/pkg/errors"
+
+ "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
+ "yunion.io/x/onecloud/pkg/compute/models"
+ "yunion.io/x/onecloud/pkg/hostman/hostutils"
+ "yunion.io/x/onecloud/pkg/multicloud/esxi"
+)
+
type SAgentImageCacheManager struct {
- storagemanager *SStorageManager
+ imageCacheManger IImageCacheManger
}
-func NewAgentImageCacheManager(storagemanager *SStorageManager) *SAgentImageCacheManager {
- return &SAgentImageCacheManager{storagemanager}
+func NewAgentImageCacheManager(manger IImageCacheManger) *SAgentImageCacheManager {
+ return &SAgentImageCacheManager{manger}
+}
+
+type sImageCacheData struct {
+ ImageId string
+ HostId string
+ HostIp string
+ SrcHostIp string
+ SrcPath string
+ SrcDatastore models.SVCenterAccessInfo
+ Datastore models.SVCenterAccessInfo
+ Format string
+ IsForce bool
+ StoragecacheId string
+}
+
+func (c *SAgentImageCacheManager) PrefetchImageCache(ctx context.Context, data interface{}) (jsonutils.JSONObject, error) {
+ dataDict, ok := data.(*jsonutils.JSONDict)
+ if !ok {
+ return nil, errors.Wrap(hostutils.ParamsError, "PrefetchImageCache data format error")
+ }
+ idata := new(sImageCacheData)
+ err := dataDict.Unmarshal(idata)
+ if err != nil {
+ return nil, errors.Wrap(err, "%s: unmarshal to sImageCacheData error")
+ }
+ lockman.LockRawObject(ctx, idata.HostId, idata.ImageId)
+ defer lockman.ReleaseRawObject(ctx, idata.HostId, idata.ImageId)
+ if len(idata.SrcHostIp) != 0 {
+ return c.prefetchImageCacheByCopy(ctx, idata)
+ }
+ return c.prefetchImageCacheByUpload(ctx, idata, dataDict)
+}
+
+func (c *SAgentImageCacheManager) prefetchImageCacheByCopy(ctx context.Context, data *sImageCacheData) (jsonutils.JSONObject, error) {
+ client, err := esxi.NewESXiClientFromAccessInfo(ctx, &data.Datastore)
+ if err != nil {
+ return nil, err
+ }
+ dstHost, err := client.FindHostByIp(data.HostIp)
+ if err != nil {
+ return nil, err
+ }
+ dstDs, err := dstHost.FindDataStoreById(data.Datastore.PrivateId)
+ if err != nil {
+ return nil, err
+ }
+ srcHost, err := client.FindHostByIp(data.SrcHostIp)
+ if err != nil {
+ return nil, err
+ }
+ srcDs, err := srcHost.FindDataStoreById(data.SrcDatastore.PrivateId)
+ if err != nil {
+ return nil, err
+ }
+ srcPath := data.SrcPath[len(srcDs.GetUrl()):]
+ dstPath := fmt.Sprintf("image_cache/%s.vmdk", data.ImageId)
+
+ // check if dst vmdk has been existed
+ exists := false
+ log.Infof("check file: src=%s, dst=%s", srcPath, dstPath)
+ dstVmdkInfo, err := dstDs.GetVmdkInfo(ctx, dstPath)
+ if err != nil {
+ return nil, err
+ }
+ srcVmdkInfo, err := srcDs.GetVmdkInfo(ctx, srcPath)
+ if err != nil {
+ return nil, err
+ }
+ if dstVmdkInfo == srcVmdkInfo {
+ exists = true
+ }
+
+ dstUrl := dstDs.GetPathUrl(dstPath)
+ if !exists || data.IsForce {
+ _, err = dstDs.MakeDir(ctx, dstPath)
+ if err != nil {
+ return nil, errors.Wrap(err, "dstDs.MakeDir")
+ }
+ srcUrl := srcDs.GetPathUrl(srcPath)
+ log.Infof("Copy %s => %s", srcUrl, dstUrl)
+ err = client.CopyDisk(ctx, srcUrl, dstUrl, data.IsForce)
+ if err != nil {
+ return nil, errors.Wrap(err, "client.CopyDisk")
+ }
+ dstVmdkInfo, err = dstDs.GetVmdkInfo(ctx, dstPath)
+ if err != nil {
+ return nil, errors.Wrap(err, "dstDs.GetVmdkInfo")
+ }
+ }
+ ret := jsonutils.NewDict()
+ ret.Add(jsonutils.NewInt(dstVmdkInfo.Size()), "size")
+ ret.Add(jsonutils.NewString(dstUrl), "path")
+ ret.Add(jsonutils.NewString(data.ImageId), "image_id")
+ _, err = hostutils.RemoteStoragecacheCacheImage(ctx, data.StoragecacheId, data.ImageId, "ready", dstUrl)
+ if err != nil {
+ return nil, err
+ }
+
+ return ret, nil
+}
+
+func (c *SAgentImageCacheManager) prefetchImageCacheByUpload(ctx context.Context, data *sImageCacheData,
+ origin *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
+
+ format := "vmdk"
+ localImage, err := c.imageCacheManger.PrefetchImageCache(ctx, origin)
+ if err != nil {
+ return nil, err
+ }
+ localImgPath, _ := localImage.GetString("path")
+ localImgSize, _ := localImage.Int("size")
+
+ client, err := esxi.NewESXiClientFromAccessInfo(ctx, &data.Datastore)
+ if err != nil {
+ return nil, errors.Wrap(err, "esxi.NewESXiClientFromJson")
+ }
+ host, err := client.FindHostByIp(data.HostIp)
+ if err != nil {
+ return nil, err
+ }
+ ds, err := host.FindDataStoreById(data.Datastore.PrivateId)
+ if err != nil {
+ return nil, errors.Wrap(err, "SHost.FindDataStoreById")
+ }
+ remotePath := fmt.Sprintf("image_cache/%s.%s", data.ImageId, format)
+
+ // check if dst vmdk is exist
+ exists := false
+ if format == "vmdk" {
+ err = ds.CheckVmdk(ctx, remotePath)
+ if err != nil {
+ log.Debugf("ds.CheckVmdk failed: %s", err)
+ } else {
+ exists = true
+ }
+ } else {
+ ret, err := ds.CheckFile(ctx, remotePath)
+ if err != nil {
+ log.Debugf("ds.CheckFile failed: %s", err)
+ } else {
+ if int64(ret.Size) == localImgSize {
+ // exist and same size
+ exists = true
+ }
+ }
+ }
+ log.Debugf("exist: %t, remotePath: %s", exists, remotePath)
+ if !exists || data.IsForce {
+ err := ds.ImportVMDK(ctx, localImgPath, remotePath, host)
+ //err := ds.ImportVMDK(ctx, localImgPath, host)
+ if err != nil {
+ return nil, errors.Wrap(err, "SDatastore.ImportTemplate")
+ }
+ }
+ remotePath = filepath.Join(ds.GetUrl(), remotePath)
+ remoteImg := localImage.(*jsonutils.JSONDict)
+ remoteImg.Add(jsonutils.NewString(remotePath), "path")
+
+ _, err = hostutils.RemoteStoragecacheCacheImage(ctx, data.StoragecacheId, data.ImageId, "ready", remotePath)
+ if err != nil {
+ return nil, err
+ }
+ log.Debugf("prefetchImageCacheByUpload over")
+ return remoteImg, nil
+}
+
+func (c *SAgentImageCacheManager) DeleteImageCache(ctx context.Context, data interface{}) (jsonutils.JSONObject, error) {
+ dataDict, ok := data.(*jsonutils.JSONDict)
+ if !ok {
+ return nil, errors.Wrap(hostutils.ParamsError, "DeleteImageCache data format error")
+ }
+ var (
+ imageID, _ = dataDict.GetString("image_id")
+ hostIP, _ = dataDict.GetString("host_ip")
+ dsInfo, _ = dataDict.Get("ds_info")
+ )
+
+ client, _, err := esxi.NewESXiClientFromJson(ctx, dsInfo)
+ if err != nil {
+ return nil, err
+ }
+ host, err := client.FindHostByIp(hostIP)
+ if err != nil {
+ return nil, err
+ }
+ dsID, _ := dsInfo.GetString("private_id")
+ ds, err := host.FindDataStoreById(dsID)
+ if err != nil {
+ return nil, err
+ }
+ remotePath := fmt.Sprintf("image_cache/%s.vmdk", imageID)
+ return nil, ds.Delete(ctx, remotePath)
}
diff --git a/pkg/hostman/storageman/imagecachemanager_local.go b/pkg/hostman/storageman/imagecachemanager_local.go
index 4e310e4ae6..9a044d8e25 100644
--- a/pkg/hostman/storageman/imagecachemanager_local.go
+++ b/pkg/hostman/storageman/imagecachemanager_local.go
@@ -162,5 +162,3 @@ func (c *SLocalImageCacheManager) PrefetchImageCache(ctx context.Context, data i
return nil, fmt.Errorf("Failed to fetch image %s", imageId)
}
}
-
-// TODO: AgentImageCacheManager
diff --git a/pkg/hostman/storageman/imagecachemanager_rbd.go b/pkg/hostman/storageman/imagecachemanager_rbd.go
index 288f2c368c..918673f77f 100644
--- a/pkg/hostman/storageman/imagecachemanager_rbd.go
+++ b/pkg/hostman/storageman/imagecachemanager_rbd.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// +build linux
+// +build linux,cgo
package storageman
diff --git a/pkg/hostman/storageman/storage_agent.go b/pkg/hostman/storageman/storage_agent.go
new file mode 100644
index 0000000000..612d910b78
--- /dev/null
+++ b/pkg/hostman/storageman/storage_agent.go
@@ -0,0 +1,564 @@
+// 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 storageman
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path"
+ "path/filepath"
+ "time"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/log"
+ "yunion.io/x/pkg/errors"
+ "yunion.io/x/pkg/util/seclib"
+ "yunion.io/x/pkg/util/timeutils"
+
+ api "yunion.io/x/onecloud/pkg/apis/compute"
+ "yunion.io/x/onecloud/pkg/cloudcommon/agent/iagent"
+ "yunion.io/x/onecloud/pkg/cloudprovider"
+ "yunion.io/x/onecloud/pkg/compute/models"
+ deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
+ "yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient"
+ "yunion.io/x/onecloud/pkg/hostman/hostutils"
+ "yunion.io/x/onecloud/pkg/hostman/options"
+ "yunion.io/x/onecloud/pkg/mcclient/modules"
+ "yunion.io/x/onecloud/pkg/multicloud/esxi"
+ "yunion.io/x/onecloud/pkg/util/procutils"
+ "yunion.io/x/onecloud/pkg/util/qemuimg"
+)
+
+type SAgentStorage struct {
+ SLocalStorage
+ agent iagent.IAgent
+}
+
+func NewAgentStorage(manager *SStorageManager, agent iagent.IAgent, path string) *SAgentStorage {
+ s := &SAgentStorage{SLocalStorage: *NewLocalStorage(manager, path, 0)}
+ s.agent = agent
+ s.checkDirC(path)
+ return s
+}
+
+func (as *SAgentStorage) GetDiskById(diskId string) IDisk {
+ return NewAgentDisk(as, diskId)
+}
+
+func (as *SAgentStorage) CreateDiskByDiskInfo(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
+ createParams, ok := params.(*SDiskCreateByDiskinfo)
+ if !ok {
+ return nil, errors.Wrap(hostutils.ParamsError, "CreateDiskByDiskInfo params format error")
+ }
+
+ hd := SHostDatastore{}
+ err := createParams.DiskInfo.Unmarshal(&hd)
+ if err != nil {
+ return nil, errors.Wrap(hostutils.ParamsError, err.Error())
+ }
+
+ diskMeta, err := as.SLocalStorage.CreateDiskByDiskinfo(ctx, params)
+ if err != nil {
+ return nil, errors.Wrap(err, "as.SLocalStorage.CreateDiskByDiskinfo")
+ }
+ disk := as.GetDiskById(createParams.DiskId)
+
+ _, ds, err := as.getHostAndDatastore(ctx, hd)
+ if err != nil {
+ return nil, errors.Wrap(err, "as.getHostAndDatastore")
+ }
+ remotePath := "disks/" + createParams.DiskId
+ file, err := os.Open(disk.GetPath())
+ if err != nil {
+ return nil, errors.Wrap(err, "fail to open disk path")
+ }
+ defer file.Close()
+ err = ds.Upload(ctx, remotePath, file)
+ if err != nil {
+ return nil, errors.Wrap(err, "dataStore.Upload")
+ }
+ as.RemoveDisk(disk)
+ return diskMeta, nil
+}
+
+func (as *SAgentStorage) agentRebuildRoot(ctx context.Context, data jsonutils.JSONObject) error {
+ type sRebuildParam struct {
+ GuestExtId string
+ SHostDatastore
+ Desc struct {
+ Disks []struct {
+ DiskId string
+ ImagePath string
+ }
+ }
+ }
+ rp := sRebuildParam{}
+ err := data.Unmarshal(&rp)
+ if err != nil {
+ return errors.Wrap(err, hostutils.ParamsError.Error())
+ }
+ host, _, err := as.getHostAndDatastore(ctx, rp.SHostDatastore)
+ if err != nil {
+ return errors.Wrap(err, "as.getHostAndDatastore")
+ }
+ ivm, err := host.GetIVMById(rp.GuestExtId)
+ if err != nil {
+ return errors.Wrapf(err, "host.GetIVMById of id '%s'", rp.GuestExtId)
+ }
+ if len(rp.Desc.Disks) == 0 {
+ return errors.Wrap(hostutils.ParamsError, "agentRebuildRoot data.desc.disks is empty")
+ }
+ imagePath := rp.Desc.Disks[0].ImagePath
+ diskId := rp.Desc.Disks[0].DiskId
+ newPath, err := host.FileUrlPathToDsPath(imagePath)
+ if err != nil {
+ return err
+ }
+ vm := ivm.(*esxi.SVirtualMachine)
+ return vm.DoRebuildRoot(ctx, newPath, diskId)
+}
+
+func (as *SAgentStorage) agentCreateGuest(ctx context.Context, data *jsonutils.JSONDict) error {
+ hd := SHostDatastore{}
+ err := data.Unmarshal(&hd)
+ if err != nil {
+ return errors.Wrap(err, hostutils.ParamsError.Error())
+ }
+ host, ds, err := as.getHostAndDatastore(ctx, hd)
+ if err != nil {
+ return err
+ }
+ desc, _ := data.Get("desc")
+ descDict, ok := desc.(*jsonutils.JSONDict)
+ if !ok {
+ return errors.Wrap(hostutils.ParamsError, "agentCreateGuest data format error")
+ }
+ vm, err := host.DoCreateVM(ctx, ds, descDict)
+ if err != nil {
+ return errors.Wrap(err, "SHost.DoCreateVM")
+ }
+ /*
+ id, _ := data.GetString("guest_ext_id")
+ ivm, err := host.GetIVMById(id)
+ if err != nil {
+ return errors.Wrap(err, "SHost.GetIVMById")
+ }
+ */
+ name, _ := descDict.GetString("name")
+ err = as.tryRenameVm(ctx, vm, name)
+ if err != nil {
+ return errors.Wrapf(err, "RenameVm name '%s'", name)
+ }
+ return nil
+}
+
+func (as *SAgentStorage) tryRenameVm(ctx context.Context, vm *esxi.SVirtualMachine, name string) error {
+ var (
+ tried = 0
+ err error
+ )
+ alterName := fmt.Sprintf("%s-%s", name, timeutils.ShortDate(time.Now()))
+ cands := []string{name, alterName}
+
+ for tried < 10 {
+ var n string
+ if tried < len(cands) {
+ n = cands[tried]
+ } else {
+ n = fmt.Sprintf("%s-%d", alterName, tried-len(cands)+1)
+ }
+ tried += 1
+ err = vm.DoRename(ctx, n)
+ if err == nil {
+ return nil
+ }
+ }
+ return err
+}
+
+func (as *SAgentStorage) AgentDeployGuest(ctx context.Context, data interface{}) (jsonutils.JSONObject, error) {
+ init := false
+ dataDict := data.(*jsonutils.JSONDict)
+ action, _ := dataDict.GetString("action")
+ if action == "create" {
+ err := as.agentCreateGuest(ctx, dataDict)
+ if err != nil {
+ return nil, errors.Wrap(err, "agentCreateGuest")
+ }
+ init = true
+ } else if action == "rebuild" {
+ err := as.agentRebuildRoot(ctx, dataDict)
+ if err != nil {
+ return nil, errors.Wrap(err, "agentRebuildRoot")
+ }
+ }
+
+ var (
+ hostIp, _ = dataDict.GetString("host_ip")
+ dsInfo, _ = dataDict.Get("datastore")
+ )
+ dc, info, err := esxi.NewESXiClientFromJson(ctx, dsInfo)
+ if err != nil {
+ return nil, errors.Wrap(err, "esxi.NewESXiClientFromJson")
+ }
+ host, err := dc.FindHostByIp(hostIp)
+ if err != nil {
+ return nil, errors.Wrap(err, "SDatacenter.FindHostByIp")
+ }
+ vmId, _ := dataDict.GetString("guest_ext_id")
+ realHost := host
+ ivm, err := host.GetIVMById(vmId)
+ if err == cloudprovider.ErrNotFound {
+ // reschedule by DRS, migrate to other host
+ siblingHosts, err := host.GetSiblingHosts()
+ if err != nil {
+ return nil, errors.Wrap(err, "SHost.GetSiblingHosts")
+ }
+ for _, sh := range siblingHosts {
+ ivm, err = host.GetIVMById(vmId)
+ if err == nil {
+ realHost = sh
+ break
+ }
+ }
+ }
+ if err != nil {
+ return nil, errors.Wrap(err, "SHost.GetIVMById")
+ }
+ if ivm == nil {
+ return nil, errors.Error(fmt.Sprintf("no such vm '%s'", ivm.GetId()))
+ }
+ vm := ivm.(*esxi.SVirtualMachine)
+ disks, err := vm.GetIDisks()
+ if err != nil {
+ return nil, errors.Wrap(err, "VM.GetIDisks")
+ }
+ if len(disks) == 0 {
+ return nil, errors.Error(fmt.Sprintf("no such disks for vm %s", vm.GetId()))
+ }
+ vmref := vm.GetMoid()
+ rootPath := disks[0].(*esxi.SVirtualDisk).GetFilename()
+
+ key := deployapi.SSHKeys{}
+ err = dataDict.Unmarshal(&key, "desc")
+ if err != nil {
+ return nil, errors.Wrapf(err, "%s: unmarshal to deployapi.SSHKeys", hostutils.ParamsError.Error())
+ }
+
+ deployArray := make([]*deployapi.DeployContent, 0)
+ if dataDict.Contains("deploys") {
+ err = dataDict.Unmarshal(&deployArray, "deploys")
+ if err != nil {
+ return nil, errors.Wrapf(err, "%s: unmarshal to array of deployapi.DeployContent", hostutils.ParamsError.Error())
+ }
+ }
+
+ resetPassword := jsonutils.QueryBoolean(dataDict, "reset_password", false)
+ passwd, _ := dataDict.GetString("password")
+ if resetPassword && len(passwd) == 0 {
+ passwd = seclib.RandomPassword(12)
+ }
+
+ log.Debugf("host: %s, port: %d, user: %s, passwd: %s", info.Host, info.Port, info.Account, info.Password)
+ vddkInfo := deployapi.VDDKConInfo{
+ Host: info.Host,
+ Port: int32(info.Port),
+ User: info.Account,
+ Passwd: info.Password,
+ Vmref: vmref,
+ }
+ guestDesc := deployapi.GuestDesc{}
+ err = dataDict.Unmarshal(&guestDesc, "desc")
+ if err != nil {
+ return nil, errors.Wrapf(err, "%s: unmarshal to guestDesc", hostutils.ParamsError.Error())
+ }
+
+ desc, _ := dataDict.Get("desc")
+ guestDesc.Hypervisor = api.HYPERVISOR_ESXI
+ deploy, err := deployclient.GetDeployClient().DeployGuestFs(ctx, &deployapi.DeployParams{
+ DiskPath: rootPath,
+ GuestDesc: &guestDesc,
+ DeployInfo: &deployapi.DeployInfo{
+ PublicKey: &key,
+ Deploys: deployArray,
+ Password: passwd,
+ IsInit: init,
+ },
+ VddkInfo: &vddkInfo,
+ })
+ if err != nil {
+ log.Errorf("DeployClient.DeployGuestFs: %s", err)
+ // if deploy fail, try customization
+ as.waitVmToolsVersion(ctx, vm)
+ err = vm.DoCustomize(ctx, desc)
+ if err != nil {
+ return nil, errors.Wrap(err, "VM.DoCustomize")
+ }
+ }
+
+ array := jsonutils.NewArray()
+ diskArray, _ := desc.GetArray("disks")
+ for idx, d := range disks {
+ disk := d.(*esxi.SVirtualDisk)
+ diskId, _ := diskArray[idx].GetString("disk_id")
+ diskDict := jsonutils.NewDict()
+ diskDict.Add(jsonutils.NewString(diskId), "disk_id")
+ diskDict.Add(jsonutils.NewString(disk.GetGlobalId()), "uuid")
+ diskDict.Add(jsonutils.NewInt(int64(disk.GetDiskSizeMB())), "size")
+ diskDict.Add(jsonutils.NewString(disk.GetFilename()), "path")
+ diskDict.Add(jsonutils.NewString(disk.GetCacheMode()), "cache_mode")
+ diskDict.Add(jsonutils.NewString(disk.GetDiskType()), "disk_type")
+ diskDict.Add(jsonutils.NewString(disk.GetDriver()), "driver")
+ array.Add(diskDict)
+ }
+ updated := jsonutils.NewDict()
+ updated.Add(array, "disks")
+ updated.Add(jsonutils.NewString(vm.GetGlobalId()), "uuid")
+ updated.Add(jsonutils.NewString(realHost.GetAccessIp()), "host_ip")
+ ret := jsonutils.Marshal(deploy)
+ ret.(*jsonutils.JSONDict).Update(updated)
+ return ret, nil
+}
+
+func (as *SAgentStorage) waitVmToolsVersion(ctx context.Context, vm *esxi.SVirtualMachine) {
+ timeout := 90 * time.Second
+
+ timeUpper := time.Now().Add(timeout)
+ for len(vm.GetToolsVersion()) == 0 && time.Now().Before(timeUpper) {
+ if vm.GetStatus() != api.VM_RUNNING {
+ vm.StartVM(ctx)
+ time.Sleep(5 * time.Second)
+ }
+ }
+ timeUpper = time.Now().Add(timeout)
+ for vm.GetStatus() == api.VM_RUNNING && time.Now().Before(timeUpper) {
+ vm.StopVM(ctx, true)
+ time.Sleep(5 * time.Second)
+ }
+ return
+}
+
+type SHostDatastore struct {
+ HostIp string
+ Datastore models.SVCenterAccessInfo
+}
+
+func (as *SAgentStorage) getHostAndDatastore(ctx context.Context, data SHostDatastore) (*esxi.SHost, *esxi.SDatastore, error) {
+ client, err := esxi.NewESXiClientFromAccessInfo(ctx, &data.Datastore)
+ if err != nil {
+ return nil, nil, errors.Wrap(err, "fail to generate client")
+ }
+ host, err := client.FindHostByIp(data.HostIp)
+ if err != nil {
+ return nil, nil, errors.Wrap(err, "fail to find host")
+ }
+ ds, err := host.FindDataStoreById(data.Datastore.PrivateId)
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "fail to find datastore")
+ }
+ return host, ds, nil
+}
+
+func (as *SAgentStorage) isExist(dir string) bool {
+ _, err := os.Stat(dir)
+ if err != nil {
+ if os.IsExist(err) {
+ return true
+ }
+ return false
+ }
+ return true
+}
+
+func (as *SAgentStorage) checkDirC(dir string) error {
+ if as.isExist(dir) {
+ return nil
+ }
+ return os.MkdirAll(dir, 0770)
+}
+
+func (as *SAgentStorage) checkFileR(file string) error {
+ if !as.isExist(file) {
+ return nil
+ }
+ return os.Remove(file)
+}
+
+func (as *SAgentStorage) PrepareSaveToGlance(ctx context.Context, taskId string, diskInfo jsonutils.JSONObject) (
+ ret jsonutils.JSONObject, err error) {
+
+ type specStruct struct {
+ Vm models.SVCenterAccessInfo
+ Disk models.SVCenterAccessInfo
+ HostIp string
+ ImageId string
+ }
+
+ spec := specStruct{}
+ err = diskInfo.Unmarshal(&spec)
+ if err != nil {
+ return nil, errors.Wrap(hostutils.ParamsError, err.Error())
+ }
+
+ destDir := as.GetImgsaveBackupPath()
+ as.checkDirC(destDir)
+ backupPath := filepath.Join(destDir, fmt.Sprintf("%s.%s", spec.Vm.PrivateId, taskId))
+ defer as.checkFileR(backupPath)
+
+ client, err := esxi.NewESXiClientFromAccessInfo(ctx, &spec.Vm)
+ if err != nil {
+ return nil, errors.Wrap(err, "esxi.NewESXiClientFromJson")
+ }
+ host, err := client.FindHostByIp(spec.HostIp)
+ if err != nil {
+ return nil, errors.Wrapf(err, "ESXiClient.FindHostByIp of ip '%s'", spec.HostIp)
+ }
+ ivm, err := host.GetIVMById(spec.Vm.PrivateId)
+ if err != nil {
+ return nil, errors.Wrapf(err, "esxi.SHost.GetIVMById for '%s'", spec.Vm.PrivateId)
+ }
+ vm := ivm.(*esxi.SVirtualMachine)
+ idisk, err := vm.GetIDiskById(spec.Disk.PrivateId)
+ if err != nil {
+ return nil, errors.Wrapf(err, "esxi.SVirtualMachine for '%s'", spec.Disk.PrivateId)
+ }
+ disk := idisk.(*esxi.SVirtualDisk)
+ log.Infof("Export VM image to %s", backupPath)
+ err = vm.ExportTemplate(ctx, disk.GetIndex(), backupPath)
+ if err != nil {
+ return nil, errors.Wrap(err, "VM.ExportTemplate")
+ }
+ dict := jsonutils.NewDict()
+ dict.Add(jsonutils.NewString(backupPath), "backup")
+ return dict, nil
+}
+
+func (as *SAgentStorage) SaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
+ data, ok := params.(*jsonutils.JSONDict)
+ if !ok {
+ return nil, errors.Wrap(hostutils.ParamsError, "SaveToGlance params format error")
+ }
+
+ var (
+ imageId, _ = data.GetString("image_id")
+ imagePath, _ = data.GetString("image_path")
+ compress = jsonutils.QueryBoolean(data, "compress", true)
+ format, _ = data.GetString("format")
+ )
+ log.Debugf("image path: %s", imagePath)
+
+ if err := as.saveToGlance(ctx, imageId, imagePath, compress, format); err != nil {
+ log.Errorf("Save to glance failed: %s", err)
+ as.onSaveToGlanceFailed(ctx, imageId)
+ return nil, err
+ }
+
+ imagecacheManager := as.Manager.LocalStorageImagecacheManager
+ if len(imagecacheManager.GetId()) > 0 {
+ err := procutils.NewCommand("rm", "-f", imagePath).Run()
+ return nil, err
+ } else {
+ dstPath := path.Join(imagecacheManager.GetPath(), imageId)
+ if err := procutils.NewCommand("mv", imagePath, dstPath).Run(); err != nil {
+ log.Errorf("Fail to move saved image to cache: %s", err)
+ }
+ imagecacheManager.LoadImageCache(imageId)
+ _, err := hostutils.RemoteStoragecacheCacheImage(ctx,
+ imagecacheManager.GetId(), imageId, "ready", dstPath)
+ if err != nil {
+ log.Errorf("Fail to remote cache image: %s", err)
+ }
+ }
+ return nil, nil
+}
+
+func (as *SAgentStorage) saveToGlance(ctx context.Context, imageId, imagePath string,
+ compress bool, format string) error {
+ ret, err := deployclient.GetDeployClient().SaveToGlance(context.Background(),
+ &deployapi.SaveToGlanceParams{DiskPath: imagePath, Compress: compress})
+ if err != nil {
+ return errors.Wrap(err, "DeployClient.SaveToGlance")
+ }
+
+ if compress {
+ origin, err := qemuimg.NewQemuImage(imagePath)
+ if err != nil {
+ log.Errorln(err)
+ return errors.Wrap(err, "qemuimg.NewQemuImage")
+ }
+ if len(format) == 0 {
+ format = options.HostOptions.DefaultImageSaveFormat
+ }
+ if format == "qcow2" {
+ if err := origin.Convert2Qcow2(true); err != nil {
+ log.Errorln(err)
+ return err
+ }
+ } else {
+ if err := origin.Convert2Vmdk(true); err != nil {
+ log.Errorln(err)
+ return err
+ }
+ }
+ }
+
+ f, err := os.Open(imagePath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ finfo, err := f.Stat()
+ if err != nil {
+ return err
+ }
+ size := finfo.Size()
+
+ var params = jsonutils.NewDict()
+ if len(ret.OsInfo) > 0 {
+ params.Set("os_type", jsonutils.NewString(ret.OsInfo))
+ }
+ relInfo := ret.ReleaseInfo
+ if relInfo != nil {
+ params.Set("os_distribution", jsonutils.NewString(relInfo.Distro))
+ if len(relInfo.Version) > 0 {
+ params.Set("os_version", jsonutils.NewString(relInfo.Version))
+ }
+ if len(relInfo.Arch) > 0 {
+ params.Set("os_arch", jsonutils.NewString(relInfo.Arch))
+ }
+ if len(relInfo.Version) > 0 {
+ params.Set("os_language", jsonutils.NewString(relInfo.Language))
+ }
+ }
+ params.Set("image_id", jsonutils.NewString(imageId))
+
+ _, err = modules.Images.Upload(hostutils.GetImageSession(ctx, as.agent.GetZoneName()),
+ params, f, size)
+ if err != nil {
+ return errors.Wrap(err, "Images.Upload")
+ }
+ return nil
+}
+
+func (as *SAgentStorage) onSaveToGlanceFailed(ctx context.Context, imageId string) {
+ params := jsonutils.NewDict()
+ params.Set("status", jsonutils.NewString("killed"))
+ _, err := modules.Images.Update(hostutils.GetImageSession(ctx, as.agent.GetZoneName()),
+ imageId, params)
+ if err != nil {
+ log.Errorln(err)
+ }
+}
diff --git a/pkg/hostman/storageman/storage_rbd.go b/pkg/hostman/storageman/storage_rbd.go
index f29d934ce1..1e9f9e596d 100644
--- a/pkg/hostman/storageman/storage_rbd.go
+++ b/pkg/hostman/storageman/storage_rbd.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// +build linux
+// +build linux,cgo
package storageman
diff --git a/pkg/multicloud/esxi/datacenter.go b/pkg/multicloud/esxi/datacenter.go
index 9b908f0e3f..b26c24b19d 100644
--- a/pkg/multicloud/esxi/datacenter.go
+++ b/pkg/multicloud/esxi/datacenter.go
@@ -175,7 +175,8 @@ func (dc *SDatacenter) fetchVms(vmRefs []types.ManagedObjectReference, all bool)
}
}
- retVms := make([]cloudprovider.ICloudVM, 0)
+ // avoid applying new memory and copying
+ retVms := make([]cloudprovider.ICloudVM, 0, len(vms))
for i := 0; i < len(vms); i += 1 {
if all || !strings.HasPrefix(vms[i].Entity().Name, api.ESXI_IMAGE_CACHE_TMP_PREFIX) {
vmObj := NewVirtualMachine(dc.manager, &vms[i], dc)
@@ -187,6 +188,22 @@ func (dc *SDatacenter) fetchVms(vmRefs []types.ManagedObjectReference, all bool)
return retVms, nil
}
+func (dc *SDatacenter) fetchDatastores(datastoreRefs []types.ManagedObjectReference) ([]cloudprovider.ICloudStorage, error) {
+ var dss []mo.Datastore
+ if datastoreRefs != nil {
+ err := dc.manager.references2Objects(datastoreRefs, DATASTORE_PROPS, &dss)
+ if err != nil {
+ return nil, errors.Wrap(err, "dc.manager.references2Objects")
+ }
+ }
+
+ retDatastores := make([]cloudprovider.ICloudStorage, 0, len(dss))
+ for i := range dss {
+ retDatastores = append(retDatastores, NewDatastore(dc.manager, &dss[i], dc))
+ }
+ return retDatastores, nil
+}
+
func (dc *SDatacenter) scanNetworks() error {
if dc.inetworks == nil {
if dc.isDefaultDc() {
diff --git a/pkg/multicloud/esxi/device.go b/pkg/multicloud/esxi/device.go
index b02f8582ee..46d179a99d 100644
--- a/pkg/multicloud/esxi/device.go
+++ b/pkg/multicloud/esxi/device.go
@@ -43,7 +43,7 @@ func (dev *SVirtualDevice) getControllerKey() int32 {
return dev.dev.GetVirtualDevice().ControllerKey
}
-func (dev *SVirtualDevice) getIndex() int {
+func (dev *SVirtualDevice) GetIndex() int {
return dev.index
}
diff --git a/pkg/multicloud/esxi/devtools.go b/pkg/multicloud/esxi/devtools.go
index ec05faabad..bb296af1ea 100644
--- a/pkg/multicloud/esxi/devtools.go
+++ b/pkg/multicloud/esxi/devtools.go
@@ -15,7 +15,12 @@
package esxi
import (
+ "fmt"
+ "reflect"
+
"github.com/vmware/govmomi/vim25/types"
+
+ "yunion.io/x/pkg/errors"
)
func NewDiskDev(sizeMb int64, templatePath string, uuid string, index int32, key int32, controlKey int32) *types.VirtualDisk {
@@ -55,3 +60,142 @@ func addDevSpec(device types.BaseVirtualDevice) *types.VirtualDeviceConfigSpec {
spec.Device = device
return &spec
}
+
+func NewSCSIDev(key, ctlKey int32, driver string) types.BaseVirtualDevice {
+ desc := types.Description{Label: "SCSI controller 0", Summary: "VMware virtual SCSI"}
+
+ if driver == "pvscsi" {
+ device := types.ParaVirtualSCSIController{}
+ device.DeviceInfo = &desc
+ device.Key = key
+ device.ControllerKey = ctlKey
+ device.SharedBus = "noSharing"
+ return &device
+ }
+ device := types.VirtualLsiLogicController{}
+ device.DeviceInfo = &desc
+ device.Key = key
+ device.ControllerKey = ctlKey
+ device.SharedBus = "noSharing"
+ return &device
+}
+
+func NewAHCIDev(key, ctlKey int32) types.BaseVirtualDevice {
+ device := types.VirtualAHCIController{}
+ device.DeviceInfo = &types.Description{Label: "SATA controller 0", Summary: "AHCI"}
+ device.ControllerKey = ctlKey
+ device.Key = key
+ return &device
+}
+
+func NewSVGADev(key, ctlKey int32) types.BaseVirtualDevice {
+ device := types.VirtualMachineVideoCard{}
+ device.DeviceInfo = &types.Description{Label: "Video card", Summary: "Video card"}
+ device.ControllerKey = ctlKey
+ device.Key = key
+ device.VideoRamSizeInKB = 16 * 1024
+ return &device
+}
+
+func NewIDEDev(key, index int32) types.BaseVirtualDevice {
+ device := types.VirtualIDEController{}
+ s := fmt.Sprintf("IDE %d", index)
+ device.DeviceInfo = &types.Description{Label: s, Summary: s}
+ device.Key = key + index
+ device.BusNumber = index
+ return &device
+}
+
+func NewCDROMDev(path string, key, ctlKey int32) types.BaseVirtualDevice {
+ device := types.VirtualCdrom{}
+ device.DeviceInfo = &types.Description{Label: "CD/DVD drive 1", Summary: "Local ISO Emulated CD-ROM"}
+ device.ControllerKey = ctlKey
+ device.Key = key
+
+ connectable := types.VirtualDeviceConnectInfo{AllowGuestControl: true, Status: "untried"}
+ if len(path) != 0 {
+ device.Backing = &types.VirtualCdromIsoBackingInfo{types.VirtualDeviceFileBackingInfo{FileName: path}}
+ connectable.StartConnected = true
+ } else {
+ device.Backing = &types.VirtualCdromRemoteAtapiBackingInfo{}
+ connectable.StartConnected = false
+ }
+
+ device.Connectable = &connectable
+ return &device
+}
+
+func NewVNICDev(host *SHost, mac, driver string, vlanId int32, key, ctlKey, index int32) (types.BaseVirtualDevice, error) {
+ desc := types.Description{Label: fmt.Sprintf("Network adapter %d", index+1), Summary: "VM Network"}
+
+ inet, err := host.FindNetworkByVlanID(vlanId)
+ if err != nil {
+ return nil, errors.Wrap(err, "SHost.FindNetworkByVlanID")
+ }
+ if inet == nil {
+ return nil, errors.Error(fmt.Sprintf("VLAN %d not found", vlanId))
+ }
+
+ var (
+ False = false
+ True = true
+ )
+ var backing types.BaseVirtualDeviceBackingInfo
+ switch inet.(type) {
+ case *SDistributedVirtualPortgroup:
+ net := inet.(*SDistributedVirtualPortgroup)
+ port, err := net.FindPort()
+ if err != nil {
+ return nil, errors.Wrap(err, "net.FindPort")
+ }
+ if port == nil {
+ return nil, errors.Error("no valid port on DVS, exhausted")
+ }
+ portCon := types.DistributedVirtualSwitchPortConnection{
+ PortgroupKey: port.PortgroupKey,
+ SwitchUuid: port.DvsUuid,
+ PortKey: port.Key,
+ }
+ backing = &types.VirtualEthernetCardDistributedVirtualPortBackingInfo{Port: portCon}
+ case *SNetwork:
+ monet := inet.(*SNetwork).getMONetwork()
+ backing = &types.VirtualEthernetCardNetworkBackingInfo{
+ VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
+ DeviceName: monet.Name,
+ UseAutoDetect: &False,
+ },
+ Network: &monet.Self,
+ }
+ default:
+ return nil, errors.Error(fmt.Sprintf("Unsuppport network type %s", reflect.TypeOf(inet).Name()))
+ }
+
+ connectable := types.VirtualDeviceConnectInfo{
+ StartConnected: true,
+ AllowGuestControl: true,
+ Connected: false,
+ Status: "untried",
+ }
+
+ nic := types.VirtualEthernetCard{
+ VirtualDevice: types.VirtualDevice{
+ DeviceInfo: &desc,
+ Backing: backing,
+ },
+ WakeOnLanEnabled: &True,
+ }
+ nic.Connectable = &connectable
+ nic.ControllerKey = ctlKey
+ nic.Key = key + index
+ if len(mac) != 0 {
+ nic.AddressType = "Manual"
+ nic.MacAddress = mac
+ } else {
+ nic.AddressType = "Generated"
+ }
+ if driver == "e1000" {
+ return &types.VirtualE1000{nic}, nil
+ }
+
+ return &types.VirtualVmxnet3{types.VirtualVmxnet{nic}}, nil
+}
diff --git a/pkg/multicloud/esxi/host.go b/pkg/multicloud/esxi/host.go
index 2c383a1a74..06676c4aec 100644
--- a/pkg/multicloud/esxi/host.go
+++ b/pkg/multicloud/esxi/host.go
@@ -21,11 +21,13 @@ import (
"strings"
"time"
+ "github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
+ "yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/regutils"
@@ -34,7 +36,7 @@ import (
"yunion.io/x/onecloud/pkg/multicloud"
)
-var HOST_SYSTEM_PROPS = []string{"name", "parent", "summary", "config", "hardware", "vm", "datastore"}
+var HOST_SYSTEM_PROPS = []string{"name", "parent", "summary", "config", "hardware", "vm", "datastore", "network"}
type SHostStorageAdapterInfo struct {
Device string
@@ -89,6 +91,10 @@ type SHost struct {
storageCache *SDatastoreImageCache
vms []cloudprovider.ICloudVM
+
+ parent *mo.ComputeResource
+
+ networks []SNetwork
}
func NewHost(manager *SESXiClient, host *mo.HostSystem, dc *SDatacenter) *SHost {
@@ -587,6 +593,205 @@ func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudpr
return nil, cloudprovider.ErrNotImplemented
}
+func (self *SHost) DoCreateVM(ctx context.Context, ds *SDatastore, data *jsonutils.JSONDict) (*SVirtualMachine, error) {
+ var (
+ deviceChange = make([]types.BaseVirtualDeviceConfigSpec, 0, 5)
+ name string
+ osName = "Linux"
+ memoryMB int64 = 2048
+ numCPUs int32 = 2
+ bios string
+ )
+
+ if data.Contains("uuid") {
+ name, _ = data.GetString("uuid")
+ } else {
+ name, _ = data.GetString("name")
+ }
+ if data.Contains("os_name") {
+ osName, _ = data.GetString("os_name")
+ }
+ datastorePath := fmt.Sprintf("[%s] %s", ds.GetRelName(), name)
+ if data.Contains("mem") {
+ memoryMB, _ = data.Int("mem")
+ }
+ if data.Contains("cpu") {
+ cpunum, _ := data.Int("cpu")
+ numCPUs = int32(cpunum)
+ }
+
+ firmware := ""
+ if data.Contains("bios") {
+ bios, _ = data.GetString("bios")
+ }
+ if len(bios) != 0 {
+ if bios == "BIOS" {
+ firmware = "bios"
+ } else if bios == "UEFI" {
+ firmware = "efi"
+ }
+ }
+
+ guestId := "rhel6_64Guest"
+ if osName == "Windows" {
+ guestId = "windows7Server64Guest"
+ }
+
+ version := "vmx-10"
+ if self.isVersion50() {
+ version = "vmx-08"
+ }
+
+ uuid, _ := data.GetString("uuid")
+
+ spec := types.VirtualMachineConfigSpec{
+ Name: name,
+ Version: version,
+ Uuid: uuid,
+ GuestId: guestId,
+ NumCPUs: numCPUs,
+ MemoryMB: memoryMB,
+ Firmware: firmware,
+ }
+ spec.Files = &types.VirtualMachineFileInfo{
+ VmPathName: datastorePath,
+ }
+
+ deviceChange = append(deviceChange, addDevSpec(NewIDEDev(200, 0)))
+ deviceChange = append(deviceChange, addDevSpec(NewIDEDev(200, 1)))
+ deviceChange = append(deviceChange, addDevSpec(NewSVGADev(500, 100)))
+ disks, _ := data.GetArray("disks")
+ driver := "scsi"
+ if len(disks) > 0 {
+ driver, _ = disks[0].GetString("driver")
+ }
+ if driver == "scsi" || driver == "pvscsi" {
+ if self.isVersion50() {
+ driver = "scsi"
+ }
+ deviceChange = append(deviceChange, addDevSpec(NewSCSIDev(1000, 100, driver)))
+ }
+ cdromPath := ""
+ if data.Contains("cdrom") {
+ tmp, _ := data.Get("cdrom")
+ cdromPath, _ = tmp.GetString("path")
+ }
+ var err error
+ if len(cdromPath) != 0 && !strings.HasPrefix(cdromPath, "[") {
+ cdromPath, err = self.FileUrlPathToDsPath(cdromPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "SHost.FileUrlPathToDsPath for cdrom path '%s'", cdromPath)
+ }
+ }
+ deviceChange = append(deviceChange, addDevSpec(NewCDROMDev(cdromPath, 16000, 201)))
+
+ var (
+ scsiIdx = 0
+ ideIdx = 0
+ index = 0
+ ctrlKey = 0
+ )
+ for _, disk := range disks {
+ imagePath, _ := disk.GetString("image_path")
+ var size int64 = 0
+ if len(imagePath) == 0 {
+ size, _ = disk.Int("size")
+ if size == 0 {
+ size = 30 * 1024
+ }
+ } else {
+ imagePath, err = self.FileUrlPathToDsPath(imagePath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "SHost.FileUrlPathToDsPath for image path '%s'", imagePath)
+ }
+ }
+ uuid, _ := disk.GetString("disk_id")
+ driver := "scsi"
+ if disk.Contains("driver") {
+ driver, _ = disk.GetString("driver")
+ }
+ if driver == "scsi" || driver == "pvscsi" {
+ if self.isVersion50() {
+ driver = "scsi"
+ }
+ ctrlKey = 1000
+ index = scsiIdx
+ scsiIdx += 1
+ } else {
+ ctrlKey = 200 + ideIdx/2
+ index = ideIdx % 2
+ ideIdx += 1
+ }
+ log.Debugf("size: %d, image path: %s, uuid: %s, index: %d, ctrlKey: %d", size, imagePath, uuid, index, ctrlKey)
+ spec := addDevSpec(NewDiskDev(size, imagePath, uuid, int32(index), 2000, int32(ctrlKey)))
+ spec.FileOperation = "create"
+ deviceChange = append(deviceChange, spec)
+ }
+
+ nics, _ := data.GetArray("nics")
+ for _, nic := range nics {
+ index, _ := nic.Int("index")
+ mac, _ := nic.GetString("mac")
+ driver := "e1000"
+ if nic.Contains("driver") {
+ driver, _ = nic.GetString("driver")
+ }
+ if self.isVersion50() {
+ driver = "e1000"
+ }
+ var vlanId int64 = 1
+ if nic.Contains("vlan") {
+ vlanId, _ = nic.Int("vlan")
+ }
+ dev, err := NewVNICDev(self, mac, driver, int32(vlanId), 4000, 100, int32(index))
+ if err != nil {
+ return nil, errors.Wrap(err, "NewVNICDev")
+ }
+ deviceChange = append(deviceChange, addDevSpec(dev))
+ }
+
+ spec.DeviceChange = deviceChange
+ dc, err := self.GetDatacenter()
+ if err != nil {
+ return nil, errors.Wrapf(err, "SHost.GetDatacenter for host '%s'", self.GetId())
+ }
+ // get vmFloder
+ folders, err := dc.getObjectDatacenter().Folders(ctx)
+ if err != nil {
+ return nil, errors.Wrap(err, "object.DataCenter.Folders")
+ }
+ vmFolder := folders.VmFolder
+ resourcePool, err := self.GetResourcePool()
+ if err != nil {
+ return nil, errors.Wrap(err, "SHost.GetResourcePool")
+ }
+ task, err := vmFolder.CreateVM(ctx, spec, resourcePool, self.GetoHostSystem())
+ if err != nil {
+ return nil, errors.Wrap(err, "VmFolder.Create")
+ }
+
+ info, err := task.WaitForResult(ctx, nil)
+ if err != nil {
+ return nil, errors.Wrap(err, "Task.WaitForResult")
+ }
+
+ var moVM mo.VirtualMachine
+ err = self.manager.reference2Object(info.Result.(types.ManagedObjectReference), VIRTUAL_MACHINE_PROPS, &moVM)
+ if err != nil {
+ return nil, errors.Wrap(err, "fail to fetch virtual machine just created")
+ }
+
+ return NewVirtualMachine(self.manager, &moVM, self.datacenter), nil
+}
+
+func (host *SHost) isVersion50() bool {
+ version := host.GetVersion()
+ if strings.HasPrefix(version, "5.") {
+ return true
+ }
+ return false
+}
+
func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
nics := host.getNicInfo()
inics := make([]cloudprovider.ICloudHostNetInterface, len(nics))
@@ -662,3 +867,212 @@ func (host *SHost) GetManagementServerIp() string {
func (host *SHost) IsManagedByVCenter() bool {
return len(host.getHostSystem().Summary.ManagementServerIp) > 0
}
+
+func (host *SHost) FindDataStoreById(id string) (*SDatastore, error) {
+ datastores, err := host.GetDataStores()
+ if err != nil {
+ return nil, err
+ }
+ for i := range datastores {
+ if datastores[i].GetGlobalId() == id {
+ return datastores[i].(*SDatastore), nil
+ }
+ }
+ return nil, fmt.Errorf("no such datastore %s", id)
+}
+
+func (host *SHost) GetDataStores() ([]cloudprovider.ICloudStorage, error) {
+ err := host.fetchDatastores()
+ if err != nil {
+ return nil, err
+ }
+ return host.datastores, nil
+}
+
+func (host *SHost) fetchDatastores() error {
+ if host.datastores != nil {
+ return nil
+ }
+
+ dc, err := host.GetDatacenter()
+ if err != nil {
+ return err
+ }
+
+ MAX_TRIES := 3
+ for tried := 0; tried < MAX_TRIES; tried += 1 {
+ hostDss := host.getHostSystem().Datastore
+ if len(hostDss) == 0 {
+ // log.Errorf("host VMs are nil!!!!!")
+ return nil
+ }
+
+ dss, err := dc.fetchDatastores(hostDss)
+ if err != nil {
+ log.Errorf("dc.fetchVms fail %s", err)
+ time.Sleep(time.Second)
+ host.Refresh()
+ continue
+ }
+ host.datastores = dss
+ break
+ }
+ return nil
+}
+
+func (host *SHost) FileUrlPathToDsPath(path string) (string, error) {
+ var newPath string
+ dss, err := host.GetDataStores()
+ if err != nil {
+ return newPath, err
+ }
+ for _, ds := range dss {
+ rds := ds.(*SDatastore)
+ if strings.HasPrefix(path, rds.GetUrl()) {
+ newPath = fmt.Sprintf("[%s] %s", rds.GetRelName(), path[len(rds.GetUrl()):])
+ }
+ break
+ }
+ return newPath, nil
+}
+
+func (host *SHost) FindNetworkByVlanID(vlanID int32) (IVMNetwork, error) {
+ if vlanID > 1 && vlanID < 4095 {
+ return host.findVlanDVPG(int32(vlanID))
+ }
+ n, err := host.findNovlanDVPG()
+ if err != nil {
+ return nil, err
+ }
+ if n != nil {
+ return n, err
+ }
+ return host.findBasicNetwork()
+}
+
+func (host *SHost) findBasicNetwork() (*SNetwork, error) {
+ nets, err := host.GetNetwork()
+ if err != nil {
+ return nil, err
+ }
+ if len(nets) == 0 {
+ return nil, nil
+ }
+ return &nets[0], nil
+}
+
+func (host *SHost) GetNetwork() ([]SNetwork, error) {
+ if host.networks != nil {
+ return host.networks, nil
+ }
+ netMobs := host.getHostSystem().Network
+ moNets := make([]mo.Network, 0)
+ err := host.manager.references2Objects(netMobs, NETWORK_PROPS, &moNets)
+ if err != nil {
+ return nil, errors.Wrap(err, "references2Objects")
+ }
+ nets := make([]SNetwork, len(moNets))
+ for i := range moNets {
+ nets[i] = *NewNetwork(host.manager, &moNets[i], host.datacenter)
+ }
+ host.networks = nets
+ return host.networks, nil
+}
+
+func (host *SHost) findNovlanDVPG() (*SDistributedVirtualPortgroup, error) {
+ nets, err := host.datacenter.GetNetworks()
+ if err != nil {
+ return nil, errors.Wrap(err, "SHost.datacenter.GetNetworks")
+ }
+ for _, net := range nets {
+ dvpg, ok := net.(*SDistributedVirtualPortgroup)
+ if !ok || !dvpg.ContainHost(host) || len(dvpg.GetActivePorts()) == 0 {
+ continue
+ }
+ nvlan := dvpg.GetVlanId()
+ if nvlan <= 1 || nvlan >= 4095 {
+ return dvpg, nil
+ }
+ }
+ return nil, nil
+}
+
+func (host *SHost) findVlanDVPG(vlanId int32) (*SDistributedVirtualPortgroup, error) {
+ nets, err := host.datacenter.GetNetworks()
+ if err != nil {
+ return nil, errors.Wrap(err, "SHost.datacenter.GetNetworks")
+ }
+ for _, net := range nets {
+ dvpg, ok := net.(*SDistributedVirtualPortgroup)
+ if !ok || !dvpg.ContainHost(host) || len(dvpg.GetActivePorts()) == 0 {
+ continue
+ }
+ nvlan := dvpg.GetVlanId()
+ if nvlan == vlanId {
+ return dvpg, nil
+ }
+ }
+ return nil, nil
+}
+
+func (host *SHost) GetoHostSystem() *object.HostSystem {
+ return object.NewHostSystem(host.manager.client.Client, host.getHostSystem().Reference())
+}
+
+func (host *SHost) GetResourcePool() (*object.ResourcePool, error) {
+ var err error
+ if host.parent == nil {
+ host.parent, err = host.getResourcePool()
+ if err != nil {
+ return nil, err
+ }
+ }
+ return object.NewResourcePool(host.manager.client.Client, *host.parent.ResourcePool), nil
+}
+
+func (host *SHost) getResourcePool() (*mo.ComputeResource, error) {
+ var mcr *mo.ComputeResource
+ var parent interface{}
+
+ moHost := host.getHostSystem()
+
+ switch moHost.Parent.Type {
+ case "ComputeResource":
+ mcr = new(mo.ComputeResource)
+ parent = mcr
+ case "ClusterComputeResource":
+ mcc := new(mo.ClusterComputeResource)
+ mcr = &mcc.ComputeResource
+ parent = mcc
+ default:
+ return nil, errors.Error(fmt.Sprintf("unknown host parent type: %s", moHost.Parent.Type))
+ }
+
+ err := host.manager.reference2Object(*moHost.Parent, []string{"resourcePool"}, parent)
+ if err != nil {
+ return nil, errors.Wrap(err, "SESXiClient.reference2Object")
+ }
+ return mcr, nil
+}
+
+func (host *SHost) GetCluster() (*mo.ComputeResource, error) {
+ return host.getResourcePool()
+}
+
+func (host *SHost) GetSiblingHosts() ([]*SHost, error) {
+ rp, err := host.GetCluster()
+ if err != nil {
+ return nil, err
+ }
+ moHosts := make([]mo.HostSystem, 0, len(rp.Host))
+ err = host.manager.references2Objects(rp.Host, HOST_SYSTEM_PROPS, &moHosts)
+ if err != nil {
+ return nil, errors.Wrap(err, "SESXiClient.references2Objects")
+ }
+
+ ret := make([]*SHost, len(moHosts))
+ for i := range moHosts {
+ ret[i] = NewHost(host.manager, &moHosts[i], host.datacenter)
+ }
+ return ret, nil
+}
diff --git a/pkg/multicloud/esxi/logger.go b/pkg/multicloud/esxi/logger.go
new file mode 100644
index 0000000000..5b0e49d27a
--- /dev/null
+++ b/pkg/multicloud/esxi/logger.go
@@ -0,0 +1,74 @@
+// 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 esxi
+
+import (
+ "github.com/vmware/govmomi/vim25/progress"
+
+ "yunion.io/x/log"
+)
+
+type logger struct {
+ name string
+ chid chan progress.Report
+ over chan struct{}
+}
+
+func (l *logger) Sink() chan<- progress.Report {
+ return l.chid
+}
+
+func (l *logger) End() {
+ close(l.over)
+}
+
+func newLeaseLogger(name string, cap int) *logger {
+ return &logger{
+ name: name,
+ chid: make(chan progress.Report, cap),
+ over: make(chan struct{}),
+ }
+}
+
+func (l *logger) Log() {
+ go func() {
+ var pre float32 = 0
+
+ log.Debugf("logger.Log...")
+ Loop:
+ for {
+ select {
+ case r, ok := <-l.chid:
+ if !ok {
+ break Loop
+ }
+ if r.Error() != nil {
+ log.Errorf("%s report, error: %s", l.name, r.Error())
+ break Loop
+ }
+ if r.Percentage() >= pre {
+ log.Debugf("%s report: speed: %s, percentage: %f%%", l.name, r.Detail(), pre)
+ pre += 10
+ }
+ if r.Percentage() >= 110 {
+ break Loop
+ }
+ case <-l.over:
+ break Loop
+ }
+
+ }
+ }()
+}
diff --git a/pkg/multicloud/esxi/manager.go b/pkg/multicloud/esxi/manager.go
index fc5137c22b..b0c27e7163 100644
--- a/pkg/multicloud/esxi/manager.go
+++ b/pkg/multicloud/esxi/manager.go
@@ -26,15 +26,19 @@ import (
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/session"
"github.com/vmware/govmomi/view"
+ "github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
+ "yunion.io/x/pkg/utils"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
+ "yunion.io/x/onecloud/pkg/compute/models"
+ "yunion.io/x/onecloud/pkg/hostman/hostutils"
"yunion.io/x/onecloud/pkg/multicloud"
)
@@ -98,6 +102,34 @@ func NewESXiClient2(providerId string, providerName string, host string, port in
return cli, nil
}
+func NewESXiClientFromJson(ctx context.Context, input jsonutils.JSONObject) (*SESXiClient, *models.SVCenterAccessInfo, error) {
+ accessInfo := new(models.SVCenterAccessInfo)
+ err := input.Unmarshal(accessInfo)
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "%s: unmarshal to SVCenterAccessInfo", hostutils.ParamsError)
+ }
+ c, err := NewESXiClientFromAccessInfo(ctx, accessInfo)
+ return c, accessInfo, err
+}
+
+func NewESXiClientFromAccessInfo(ctx context.Context, accessInfo *models.SVCenterAccessInfo) (*SESXiClient, error) {
+ if len(accessInfo.VcenterId) > 0 {
+ tmp, err := utils.DescryptAESBase64(accessInfo.VcenterId, accessInfo.Password)
+ if err == nil {
+ accessInfo.Password = tmp
+ }
+ }
+ client, err := NewESXiClient("", "", accessInfo.Host, accessInfo.Port, accessInfo.Account, accessInfo.Password)
+ if err != nil {
+ return nil, err
+ }
+ if ctx == nil {
+ ctx = context.Background()
+ }
+ client.context = ctx
+ return client, nil
+}
+
func (cli *SESXiClient) getUrl() string {
if cli.port == 443 || cli.port == 0 {
return fmt.Sprintf("https://%s", cli.host)
@@ -419,3 +451,67 @@ func (cli *SESXiClient) GetCapabilities() []string {
}
return caps
}
+
+func (cli *SESXiClient) FindVMByPrivateID(idstr string) (*SVirtualMachine, error) {
+ searchIndex := object.NewSearchIndex(cli.client.Client)
+ instanceUuid := true
+ vmRef, err := searchIndex.FindByUuid(cli.context, nil, idstr, true, &instanceUuid)
+ if err != nil {
+ return nil, errors.Wrap(err, "searchIndex.FindByUuid fail")
+ }
+ if vmRef == nil {
+ return nil, fmt.Errorf("cannot find %s", idstr)
+ }
+ var vm mo.VirtualMachine
+ err = cli.reference2Object(vmRef.Reference(), VIRTUAL_MACHINE_PROPS, &vm)
+ if err != nil {
+ return nil, errors.Wrap(err, "reference2Object fail")
+ }
+
+ return NewVirtualMachine(cli, &vm, nil), nil
+}
+
+func (cli *SESXiClient) DoExtendDiskOnline(_vm *SVirtualMachine, _disk *SVirtualDisk, newSizeMb int64) error {
+ disk := _disk.getVirtualDisk()
+ disk.CapacityInKB = newSizeMb * 1024
+ devSepc := types.VirtualDeviceConfigSpec{Operation: types.VirtualDeviceConfigSpecOperationEdit, Device: disk}
+ spec := types.VirtualMachineConfigSpec{DeviceChange: []types.BaseVirtualDeviceConfigSpec{&devSepc}}
+ vm := object.NewVirtualMachine(cli.client.Client, _vm.getVirtualMachine().Reference())
+ task, err := vm.Reconfigure(cli.context, spec)
+ if err != nil {
+ return errors.Wrapf(err, "vm reconfigure failed")
+ }
+ return task.Wait(cli.context)
+}
+
+func (cli *SESXiClient) ExtendDisk(url string, newSizeMb int64) error {
+ param := types.ExtendVirtualDisk_Task{
+ This: *cli.client.Client.ServiceContent.VirtualDiskManager,
+ Name: url,
+ NewCapacityKb: newSizeMb * 1024,
+ }
+ response, err := methods.ExtendVirtualDisk_Task(cli.context, cli.client, ¶m)
+ if err != nil {
+ return errors.Wrapf(err, "extend virtualdisk task failed")
+ }
+ log.Debugf("extend virtual disk task response: %s", response.Returnval.String())
+ return nil
+}
+
+func (cli *SESXiClient) CopyDisk(ctx context.Context, src, dst string, isForce bool) error {
+ dm := object.NewVirtualDiskManager(cli.client.Client)
+ task, err := dm.CopyVirtualDisk(ctx, src, nil, dst, nil, nil, isForce)
+ if err != nil {
+ return err
+ }
+ return task.Wait(ctx)
+}
+
+func (cli *SESXiClient) MoveDisk(ctx context.Context, src, dst string, isForce bool) error {
+ dm := object.NewVirtualDiskManager(cli.client.Client)
+ task, err := dm.MoveVirtualDisk(ctx, src, nil, dst, nil, isForce)
+ if err != nil {
+ return err
+ }
+ return task.Wait(ctx)
+}
diff --git a/pkg/multicloud/esxi/network.go b/pkg/multicloud/esxi/network.go
index 9b05cf0119..5a6d04c739 100644
--- a/pkg/multicloud/esxi/network.go
+++ b/pkg/multicloud/esxi/network.go
@@ -15,8 +15,13 @@
package esxi
import (
+ "strings"
+
+ "github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
+
+ "yunion.io/x/pkg/errors"
)
type IVMNetwork interface {
@@ -26,6 +31,7 @@ type IVMNetwork interface {
GetNumPorts() int32
GetActivePorts() []string
GetType() string
+ ContainHost(host *SHost) bool
}
const (
@@ -85,6 +91,17 @@ func (net *SNetwork) GetActivePorts() []string {
return nil
}
+func (net *SNetwork) ContainHost(host *SHost) bool {
+ objs := net.getMONetwork().Host
+ moHost := host.getHostSystem()
+ for _, obj := range objs {
+ if obj.Value == moHost.Reference().Value {
+ return true
+ }
+ }
+ return false
+}
+
func (net *SDistributedVirtualPortgroup) getMODVPortgroup() *mo.DistributedVirtualPortgroup {
return net.object.(*mo.DistributedVirtualPortgroup)
}
@@ -142,3 +159,116 @@ func (net *SDistributedVirtualPortgroup) GetActivePorts() []string {
}
return nil
}
+
+func (net *SDistributedVirtualPortgroup) ContainHost(host *SHost) bool {
+ objs := net.getMODVPortgroup().Host
+ moHost := host.getHostSystem()
+ for _, obj := range objs {
+ if obj.Value == moHost.Reference().Value {
+ return true
+ }
+ }
+ return false
+}
+
+func (net *SDistributedVirtualPortgroup) Uplink() bool {
+ dvpg := net.getMODVPortgroup()
+ return *dvpg.Config.Uplink
+}
+
+func (net *SDistributedVirtualPortgroup) FindPort() (*types.DistributedVirtualPort, error) {
+ dvgp := net.getMODVPortgroup()
+ odvs := object.NewDistributedVirtualSwitch(net.manager.client.Client, *dvgp.Config.DistributedVirtualSwitch)
+ var (
+ False = false
+ True = true
+ )
+ criteria := types.DistributedVirtualSwitchPortCriteria{
+ Connected: &False,
+ Inside: &True,
+ PortgroupKey: []string{dvgp.Key},
+ }
+ ports, err := odvs.FetchDVPorts(net.manager.context, &criteria)
+ if err != nil {
+ return nil, errors.Wrap(err, "object.DVS.FetchDVPorts")
+ }
+ if len(ports) > 0 {
+ // release extra space timely
+ return &ports[:1][0], nil
+ }
+ return nil, nil
+}
+
+func (net *SDistributedVirtualPortgroup) AddHostToDVS(host *SHost) (err error) {
+ // get dvs
+ dvgp := net.getMODVPortgroup()
+ var s mo.DistributedVirtualSwitch
+ err = net.manager.reference2Object(*dvgp.Config.DistributedVirtualSwitch, []string{"config"}, &s)
+ if err != nil {
+ return errors.Wrapf(err, "fail to convert reference to object")
+ }
+ moHost := host.getHostSystem()
+
+ // check firstly
+ for _, host := range s.Config.GetDVSConfigInfo().Host {
+ if host.Config.Host.Value == moHost.Reference().Value {
+ // host is already a member of dvs
+ return nil
+ }
+ }
+ config := &types.DVSConfigSpec{ConfigVersion: s.Config.GetDVSConfigInfo().ConfigVersion}
+ backing := new(types.DistributedVirtualSwitchHostMemberPnicBacking)
+ pnics := moHost.Config.Network.Pnic
+
+ if len(pnics) == 0 {
+ return errors.Error("no pnic in this host")
+ }
+
+ // try one by one
+ // have some bug
+ for i := 0; i < len(pnics); i++ {
+ backing.PnicSpec = []types.DistributedVirtualSwitchHostMemberPnicSpec{
+ {
+ PnicDevice: pnics[i].Device,
+ },
+ }
+ config.Host = []types.DistributedVirtualSwitchHostMemberConfigSpec{
+ {
+ Operation: "add",
+ Host: moHost.Reference(),
+ Backing: backing,
+ },
+ }
+ var task *object.Task
+ dvs := object.NewDistributedVirtualSwitch(net.manager.client.Client, s.Reference())
+ task, err = dvs.Reconfigure(net.manager.context, config)
+ if err != nil {
+ return errors.Wrapf(err, "dvs.Reconfigure")
+ }
+ err = task.Wait(net.manager.context)
+ if err == nil {
+ return nil
+ }
+ if strings.Contains(err.Error(), "concurrent modification") {
+ i -= 1
+ }
+ }
+ return err
+}
+
+func FindVlanDistVswitch(nets []IVMNetwork, vlanID int32) IVMNetwork {
+ for _, net := range nets {
+ _, ok := net.(*SDistributedVirtualPortgroup)
+ if !ok {
+ continue
+ }
+ if len(net.GetActivePorts()) == 0 {
+ continue
+ }
+ nvlan := net.GetVlanId()
+ if nvlan == vlanID {
+ return net
+ }
+ }
+ return nil
+}
diff --git a/pkg/multicloud/esxi/storage.go b/pkg/multicloud/esxi/storage.go
index 732902cf95..bdc691a331 100644
--- a/pkg/multicloud/esxi/storage.go
+++ b/pkg/multicloud/esxi/storage.go
@@ -15,20 +15,30 @@
package esxi
import (
+ "bytes"
"context"
+ "encoding/binary"
"fmt"
"io"
"io/ioutil"
+ "math/rand"
"net/http"
"net/url"
+ "os"
"path"
+ "path/filepath"
"regexp"
"strconv"
"strings"
+ "text/template"
"time"
+ "unicode"
"github.com/vmware/govmomi/object"
+ "github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/vim25/mo"
+ "github.com/vmware/govmomi/vim25/progress"
+ "github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
"yunion.io/x/jsonutils"
@@ -80,6 +90,10 @@ func (self *SDatastore) GetName() string {
return fmt.Sprintf("%s-%s", self.getVolumeType(), volName)
}
+func (self *SDatastore) GetRelName() string {
+ return self.getDatastore().Info.GetDatastoreInfo().Name
+}
+
func (self *SDatastore) GetCapacityMB() int64 {
moStore := self.getDatastore()
return moStore.Summary.Capacity / 1024 / 1024
@@ -701,6 +715,21 @@ func (self *SDatastore) RemoveDir(ctx context.Context, remotePath string) error
return dnm.DeleteDirectory(ctx, dcObj, remotePath)
}
+// CheckDirC will check that Dir 'remotePath' is exist, if not, create one.
+func (self *SDatastore) CheckDirC(remotePath string) error {
+ _, err := self.CheckFile(context.Background(), remotePath)
+ if err == nil {
+ return nil
+ }
+ if errors.Cause(err) != cloudprovider.ErrNotFound {
+ return err
+ }
+ m := object.NewFileManager(self.manager.client.Client)
+ path := fmt.Sprintf("[%s] %s", self.GetRelName(), remotePath)
+ return m.MakeDirectory(self.manager.context, path, self.datacenter.getObjectDatacenter(),
+ true)
+}
+
func (self *SDatastore) IsSysDiskStore() bool {
return true
}
@@ -717,3 +746,242 @@ func (self *SDatastore) MoveVmdk(ctx context.Context, srcPath string, dstPath st
}
return task.Wait(ctx)
}
+
+// domainName will abandon the rune which should't disapear
+func (self *SDatastore) domainName(name string) string {
+ var b bytes.Buffer
+ for _, r := range name {
+ if b.Len() == 0 {
+ if unicode.IsLetter(r) {
+ b.WriteRune(r)
+ }
+ } else {
+ if unicode.IsDigit(r) || unicode.IsLetter(r) || r == '-' {
+ b.WriteRune(r)
+ }
+ }
+ }
+ return strings.TrimRight(b.String(), "-")
+}
+
+// ImportVMDK will upload local vmdk 'diskFile' to the 'remotePath' of remote datastore
+func (self *SDatastore) ImportVMDK(ctx context.Context, diskFile, remotePath string, host *SHost) error {
+ name := fmt.Sprintf("yunioncloud.%s%d", self.domainName(remotePath), rand.Int())
+ vm, err := self.ImportVM(ctx, diskFile, name, host)
+ if err != nil {
+ return errors.Wrap(err, "SDatastore.ImportVM")
+ }
+
+ defer func() {
+ task, err := vm.Destroy(ctx)
+ if err != nil {
+ log.Errorf("vm.Destory: %s", err)
+ return
+ }
+
+ if err = task.Wait(ctx); err != nil {
+ log.Errorf("task.Wait: %s", err)
+ }
+ }()
+
+ // check if 'image_cache' is esixt
+ err = self.CheckDirC("image_cache")
+ if err != nil {
+ return errors.Wrap(err, "SDatastore.CheckDirC")
+ }
+
+ fm := self.getDatastoreObj().NewFileManager(self.datacenter.getObjectDatacenter(), true)
+
+ // if image_cache not exist
+ return fm.Move(ctx, fmt.Sprintf("[%s] %s/%s.vmdk", self.GetRelName(), name, name), fmt.Sprintf("[%s] %s",
+ self.GetRelName(), remotePath))
+}
+
+var (
+ ErrInvalidFormat = errors.Error("vmdk: invalid format (must be streamOptimized)")
+)
+
+// info is used to inspect a vmdk and generate an ovf template
+type info struct {
+ Header struct {
+ MagicNumber uint32
+ Version uint32
+ Flags uint32
+ Capacity uint64
+ }
+
+ Capacity uint64
+ Size int64
+ Name string
+ ImportName string
+}
+
+// stat looks at the vmdk header to make sure the format is streamOptimized and
+// extracts the disk capacity required to properly generate the ovf descriptor.
+func stat(name string) (*info, error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+
+ var (
+ di info
+ buf bytes.Buffer
+ )
+
+ _, err = io.CopyN(&buf, f, int64(binary.Size(di.Header)))
+ fi, _ := f.Stat()
+ _ = f.Close()
+ if err != nil {
+ return nil, err
+ }
+
+ err = binary.Read(&buf, binary.LittleEndian, &di.Header)
+ if err != nil {
+ return nil, err
+ }
+
+ if di.Header.MagicNumber != 0x564d444b { // SPARSE_MAGICNUMBER
+ return nil, ErrInvalidFormat
+ }
+
+ if di.Header.Flags&(1<<16) == 0 { // SPARSEFLAG_COMPRESSED
+ // Needs to be converted, for example:
+ // vmware-vdiskmanager -r src.vmdk -t 5 dst.vmdk
+ // qemu-img convert -O vmdk -o subformat=streamOptimized src.vmdk dst.vmdk
+ return nil, ErrInvalidFormat
+ }
+
+ di.Capacity = di.Header.Capacity * 512 // VMDK_SECTOR_SIZE
+ di.Size = fi.Size()
+ di.Name = filepath.Base(name)
+ di.ImportName = strings.TrimSuffix(di.Name, ".vmdk")
+
+ return &di, nil
+}
+
+// ovf returns an expanded descriptor template
+func (di *info) ovf() (string, error) {
+ var buf bytes.Buffer
+
+ tmpl, err := template.ParseFiles("/opt/yunion/share/vmware/ovf.xml")
+ if err != nil {
+ return "", err
+ }
+
+ err = tmpl.Execute(&buf, di)
+ if err != nil {
+ return "", err
+ }
+
+ return buf.String(), nil
+}
+
+// ImportParams contains the set of optional params to the Import function.
+// Note that "optional" may depend on environment, such as ESX or vCenter.
+type ImportParams struct {
+ Path string
+ Logger progress.Sinker
+ Type types.VirtualDiskType
+ Force bool
+ Datacenter *object.Datacenter
+ Pool *object.ResourcePool
+ Folder *object.Folder
+ Host *object.HostSystem
+}
+
+// ImportVM will import a vm by uploading a local vmdk
+func (self *SDatastore) ImportVM(ctx context.Context, diskFile, name string, host *SHost) (*object.VirtualMachine, error) {
+
+ var (
+ c = self.manager.client.Client
+ datastore = self.getDatastoreObj()
+ )
+
+ m := ovf.NewManager(c)
+
+ disk, err := stat(diskFile)
+ if err != nil {
+ return nil, errors.Wrap(err, "stat")
+ }
+
+ disk.ImportName = name
+
+ // Expand the ovf template
+ descriptor, err := disk.ovf()
+ if err != nil {
+ return nil, errors.Wrap(err, "disk.ovf")
+ }
+
+ folders, err := self.datacenter.getObjectDatacenter().Folders(ctx)
+ if err != nil {
+ return nil, errors.Wrap(err, "Folders")
+ }
+ pool, err := host.GetResourcePool()
+ if err != nil {
+ return nil, errors.Wrap(err, "getResourcePool")
+ }
+
+ kind := types.VirtualDiskTypeThin
+
+ params := types.OvfCreateImportSpecParams{
+ DiskProvisioning: string(kind),
+ EntityName: disk.ImportName,
+ }
+
+ spec, err := m.CreateImportSpec(ctx, descriptor, pool, datastore, params)
+ if err != nil {
+ return nil, err
+ }
+ if spec.Error != nil {
+ return nil, errors.Error(spec.Error[0].LocalizedMessage)
+ }
+
+ lease, err := pool.ImportVApp(ctx, spec.ImportSpec, folders.VmFolder, host.GetoHostSystem())
+ if err != nil {
+ return nil, err
+ }
+
+ info, err := lease.Wait(ctx, spec.FileItem)
+ if err != nil {
+ return nil, err
+ }
+
+ f, err := os.Open(diskFile)
+ if err != nil {
+ return nil, err
+ }
+
+ lr := newLeaseLogger("upload vmdk", 5)
+
+ lr.Log()
+ defer lr.End()
+
+ opts := soap.Upload{
+ ContentLength: disk.Size,
+ Progress: lr,
+ }
+
+ u := lease.StartUpdater(ctx, info)
+ defer u.Done()
+
+ item := info.Items[0] // we only have 1 disk to upload
+
+ err = lease.Upload(ctx, item, f, opts)
+
+ _ = f.Close()
+
+ if err != nil {
+ return nil, errors.Wrap(err, "lease.Upload")
+ }
+
+ if err = lease.Complete(ctx); err != nil {
+ log.Debugf("lease complete error: %s, sleep 1s and try again", err)
+ time.Sleep(time.Second)
+ if err = lease.Complete(ctx); err != nil {
+ return nil, errors.Wrap(err, "lease.Complete")
+ }
+ }
+
+ return object.NewVirtualMachine(c, info.Entity), nil
+}
diff --git a/pkg/multicloud/esxi/vdisk.go b/pkg/multicloud/esxi/vdisk.go
index f5c171ae5b..d16a8775a8 100644
--- a/pkg/multicloud/esxi/vdisk.go
+++ b/pkg/multicloud/esxi/vdisk.go
@@ -352,9 +352,13 @@ func (disk *SVirtualDisk) GetExpiredAt() time.Time {
}
func (disk *SVirtualDisk) Rebuild(ctx context.Context) error {
- return disk.vm.rebuildDisk(ctx, disk)
+ return disk.vm.rebuildDisk(ctx, disk, "")
}
func (disk *SVirtualDisk) GetProjectId() string {
return ""
}
+
+func (disk *SVirtualDisk) GetFilename() string {
+ return disk.getBackingInfo().GetFileName()
+}
diff --git a/pkg/multicloud/esxi/virtualmachine.go b/pkg/multicloud/esxi/virtualmachine.go
index aa016c3b3a..029343d995 100644
--- a/pkg/multicloud/esxi/virtualmachine.go
+++ b/pkg/multicloud/esxi/virtualmachine.go
@@ -24,6 +24,7 @@ import (
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
+ "github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
"yunion.io/x/jsonutils"
@@ -36,8 +37,10 @@ import (
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
+ cloudtypes "yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/util/billing"
+ "yunion.io/x/onecloud/pkg/util/netutils2"
)
var VIRTUAL_MACHINE_PROPS = []string{"name", "parent", "runtime", "summary", "config", "guest", "resourcePool", "layoutEx"}
@@ -160,7 +163,14 @@ func (self *SVirtualMachine) RebuildRoot(ctx context.Context, imageId string, pa
return "", cloudprovider.ErrNotImplemented
}
-func (self *SVirtualMachine) rebuildDisk(ctx context.Context, disk *SVirtualDisk) error {
+func (self *SVirtualMachine) DoRebuildRoot(ctx context.Context, imagePath string, uuid string) error {
+ if len(self.vdisks) == 0 {
+ return errors.ErrNotFound
+ }
+ return self.rebuildDisk(ctx, &self.vdisks[0], imagePath)
+}
+
+func (self *SVirtualMachine) rebuildDisk(ctx context.Context, disk *SVirtualDisk, imagePath string) error {
uuid := disk.GetId()
sizeMb := disk.GetDiskSizeMB()
index := disk.index
@@ -171,7 +181,7 @@ func (self *SVirtualMachine) rebuildDisk(ctx context.Context, disk *SVirtualDisk
if err != nil {
return err
}
- return self.createDiskInternal(ctx, sizeMb, uuid, int32(index), diskKey, ctlKey)
+ return self.createDiskInternal(ctx, sizeMb, uuid, int32(index), diskKey, ctlKey, imagePath, false)
}
func (self *SVirtualMachine) UpdateVM(ctx context.Context, name string) error {
@@ -826,11 +836,12 @@ func (self *SVirtualMachine) CreateDisk(ctx context.Context, sizeMb int, uuid st
ctlKey += int32(index / 2)
}
- return self.createDiskInternal(ctx, sizeMb, uuid, int32(index), diskKey, ctlKey)
+ return self.createDiskInternal(ctx, sizeMb, uuid, int32(index), diskKey, ctlKey, "", true)
}
-func (self *SVirtualMachine) createDiskInternal(ctx context.Context, sizeMb int, uuid string, index int32, diskKey int32, ctlKey int32) error {
- devSpec := NewDiskDev(int64(sizeMb), "", uuid, index, diskKey, ctlKey)
+func (self *SVirtualMachine) createDiskInternal(ctx context.Context, sizeMb int, uuid string, index int32,
+ diskKey int32, ctlKey int32, imagePath string, check bool) error {
+ devSpec := NewDiskDev(int64(sizeMb), imagePath, uuid, index, diskKey, ctlKey)
spec := addDevSpec(devSpec)
spec.FileOperation = types.VirtualDeviceConfigSpecFileOperationCreate
configSpec := types.VirtualMachineConfigSpec{}
@@ -846,6 +857,9 @@ func (self *SVirtualMachine) createDiskInternal(ctx context.Context, sizeMb int,
if err != nil {
return err
}
+ if !check {
+ return nil
+ }
oldDiskCnt := len(self.vdisks)
maxTries := 60
for tried := 0; tried < maxTries; tried += 1 {
@@ -906,3 +920,161 @@ func (self *SVirtualMachine) CheckFileInfo(ctx context.Context) error {
}
return nil
}
+
+func (self *SVirtualMachine) DoRename(ctx context.Context, name string) error {
+ task, err := self.getVmObj().Rename(ctx, name)
+ if err != nil {
+ return errors.Wrap(err, "object.VirtualMachine.Rename")
+ }
+ return task.Wait(ctx)
+}
+
+func (self *SVirtualMachine) GetMoid() string {
+ return self.getVirtualMachine().Self.Value
+}
+
+func (self *SVirtualMachine) GetToolsVersion() string {
+ return self.getVirtualMachine().Guest.ToolsVersion
+}
+
+func (self *SVirtualMachine) DoCustomize(ctx context.Context, params jsonutils.JSONObject) error {
+ spec := new(types.CustomizationSpec)
+
+ ipSettings := new(types.CustomizationGlobalIPSettings)
+ domain := "local"
+ if params.Contains("domain") {
+ domain, _ = params.GetString("domain")
+ }
+ ipSettings.DnsSuffixList = []string{domain}
+
+ // deal nics
+ nics, _ := params.GetArray("nics")
+ serverNics := make([]cloudtypes.SServerNic, len(nics))
+ for i := range nics {
+ var nicType cloudtypes.SServerNic
+ nics[i].Unmarshal(&nicType)
+ serverNics[i] = nicType
+ }
+
+ // find dnsServerList
+ for i := range serverNics {
+ dnsList := netutils2.GetNicDns(&serverNics[i])
+ if len(dnsList) != 0 {
+ ipSettings.DnsServerList = dnsList
+ }
+ }
+ spec.GlobalIPSettings = *ipSettings
+
+ maps := make([]types.CustomizationAdapterMapping, 0, len(nics))
+ for _, nic := range serverNics {
+ conf := types.CustomizationAdapterMapping{}
+ conf.MacAddress = nic.Mac
+ if len(conf.MacAddress) == 0 {
+ conf.MacAddress = "9e:46:27:21:a2:b2"
+ }
+
+ conf.Adapter = types.CustomizationIPSettings{}
+ fixedIp := new(types.CustomizationFixedIp)
+ fixedIp.IpAddress = nic.Ip
+ if len(fixedIp.IpAddress) == 0 {
+ fixedIp.IpAddress = "10.168.26.23"
+ }
+ conf.Adapter.Ip = fixedIp
+ maskLen := nic.Masklen
+ if maskLen == 0 {
+ maskLen = 24
+ }
+ mask := netutils2.Netlen2Mask(maskLen)
+ conf.Adapter.SubnetMask = mask
+
+ if len(nic.Gateway) != 0 {
+ conf.Adapter.Gateway = []string{nic.Gateway}
+ }
+ dnsList := netutils2.GetNicDns(&nic)
+ if len(dnsList) != 0 {
+ conf.Adapter.DnsServerList = dnsList
+ dns := nic.Domain
+ if len(dns) == 0 {
+ dns = "local"
+ }
+ conf.Adapter.DnsDomain = dns
+ }
+ maps = append(maps, conf)
+ }
+ spec.NicSettingMap = maps
+
+ var (
+ osName = "Linux"
+ name = "yunionhost"
+ )
+ if params.Contains("os_name") {
+ osName, _ = params.GetString("os_name")
+ }
+ if params.Contains("name") {
+ name, _ = params.GetString("name")
+ }
+ if osName == "Linux" {
+ linuxPrep := types.CustomizationLinuxPrep{
+ HostName: &types.CustomizationFixedName{Name: name},
+ Domain: domain,
+ TimeZone: "Asia/Shanghai",
+ }
+ spec.Identity = &linuxPrep
+ } else {
+ sysPrep := types.CustomizationSysprep{
+ GuiUnattended: types.CustomizationGuiUnattended{
+ TimeZone: 210,
+ AutoLogon: false,
+ },
+ UserData: types.CustomizationUserData{
+ FullName: "Administrator",
+ OrgName: "Yunion",
+ ProductId: "",
+ ComputerName: &types.CustomizationFixedName{
+ Name: name,
+ },
+ },
+ Identification: types.CustomizationIdentification{},
+ }
+ spec.Identity = &sysPrep
+ }
+ task, err := self.getVmObj().Customize(ctx, *spec)
+ if err != nil {
+ return errors.Wrap(err, "object.VirtualMachine.Customize")
+ }
+ return task.Wait(ctx)
+}
+
+func (self *SVirtualMachine) ExportTemplate(ctx context.Context, idx int, diskPath string) error {
+ lease, err := self.getVmObj().Export(ctx)
+ if err != nil {
+ return errors.Wrap(err, "esxi.SVirtualMachine.DoExportTemplate")
+ }
+ info, err := lease.Wait(ctx, nil)
+ if err != nil {
+ return errors.Wrap(err, "lease.Wait")
+ }
+
+ u := lease.StartUpdater(ctx, info)
+ defer u.Done()
+
+ if idx >= len(info.Items) {
+ return errors.Error(fmt.Sprintf("No such Device whose index is %d", idx))
+ }
+
+ lr := newLeaseLogger("download vmdk", 5)
+ lr.Log()
+ defer lr.End()
+ log.Debugf("download to %s start...", diskPath)
+ err = lease.DownloadFile(ctx, diskPath, info.Items[idx], soap.Download{Progress: lr})
+ if err != nil {
+ return errors.Wrap(err, "lease.DownloadFile")
+ }
+
+ err = lease.Complete(ctx)
+ if err != nil {
+ return errors.Wrap(err, "lease.Complete")
+ }
+ log.Debugf("download to %s finish", diskPath)
+ return nil
+}
diff --git a/pkg/util/netutils2/netutils.go b/pkg/util/netutils2/netutils.go
index 2f7bfe13ce..f93ab8153b 100644
--- a/pkg/util/netutils2/netutils.go
+++ b/pkg/util/netutils2/netutils.go
@@ -266,7 +266,7 @@ type SNetInterface struct {
Mask net.IPMask
Mac string
- // Mtu int
+ Mtu int
}
var SECRET_PREFIX = "169.254"
@@ -319,6 +319,8 @@ func (n *SNetInterface) fetchConfig(expectIp string) {
return
}
+ n.Mtu = inter.MTU
+
n.Mac = inter.HardwareAddr.String()
addrs, err := inter.Addrs()
if err == nil {
@@ -337,15 +339,6 @@ func (n *SNetInterface) fetchConfig(expectIp string) {
}
}
- // mtuStr, err := fileutils2.FileGetContents(fmt.Sprintf("/sys/class/net/%s/mtu", n.name))
- // if err != nil {
- // log.Errorln("Fail to read MTU for %s: %s", n.name, err)
- // }
-
- // n.Mtu, err = strconv.Atoi(mtuStr)
- // if err != nil {
- // log.Errorln("Fail to read MTU for %s: %s", n.name, err)
- // }
}
func (n *SNetInterface) DisableGso() {
diff --git a/vendor/github.com/vmware/govmomi/ovf/cim.go b/vendor/github.com/vmware/govmomi/ovf/cim.go
new file mode 100644
index 0000000000..ce20bde19f
--- /dev/null
+++ b/vendor/github.com/vmware/govmomi/ovf/cim.go
@@ -0,0 +1,78 @@
+/*
+Copyright (c) 2015 VMware, Inc. All Rights Reserved.
+
+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 ovf
+
+/*
+Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_VirtualSystemSettingData.xsd
+*/
+
+type CIMVirtualSystemSettingData struct {
+ ElementName string `xml:"ElementName"`
+ InstanceID string `xml:"InstanceID"`
+
+ AutomaticRecoveryAction *uint8 `xml:"AutomaticRecoveryAction"`
+ AutomaticShutdownAction *uint8 `xml:"AutomaticShutdownAction"`
+ AutomaticStartupAction *uint8 `xml:"AutomaticStartupAction"`
+ AutomaticStartupActionDelay *string `xml:"AutomaticStartupActionDelay>Interval"`
+ AutomaticStartupActionSequenceNumber *uint16 `xml:"AutomaticStartupActionSequenceNumber"`
+ Caption *string `xml:"Caption"`
+ ConfigurationDataRoot *string `xml:"ConfigurationDataRoot"`
+ ConfigurationFile *string `xml:"ConfigurationFile"`
+ ConfigurationID *string `xml:"ConfigurationID"`
+ CreationTime *string `xml:"CreationTime"`
+ Description *string `xml:"Description"`
+ LogDataRoot *string `xml:"LogDataRoot"`
+ Notes []string `xml:"Notes"`
+ RecoveryFile *string `xml:"RecoveryFile"`
+ SnapshotDataRoot *string `xml:"SnapshotDataRoot"`
+ SuspendDataRoot *string `xml:"SuspendDataRoot"`
+ SwapFileDataRoot *string `xml:"SwapFileDataRoot"`
+ VirtualSystemIdentifier *string `xml:"VirtualSystemIdentifier"`
+ VirtualSystemType *string `xml:"VirtualSystemType"`
+}
+
+/*
+Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_ResourceAllocationSettingData.xsd
+*/
+
+type CIMResourceAllocationSettingData struct {
+ ElementName string `xml:"ElementName"`
+ InstanceID string `xml:"InstanceID"`
+
+ ResourceType *uint16 `xml:"ResourceType"`
+ OtherResourceType *string `xml:"OtherResourceType"`
+ ResourceSubType *string `xml:"ResourceSubType"`
+
+ AddressOnParent *string `xml:"AddressOnParent"`
+ Address *string `xml:"Address"`
+ AllocationUnits *string `xml:"AllocationUnits"`
+ AutomaticAllocation *bool `xml:"AutomaticAllocation"`
+ AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
+ Caption *string `xml:"Caption"`
+ Connection []string `xml:"Connection"`
+ ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
+ Description *string `xml:"Description"`
+ HostResource []string `xml:"HostResource"`
+ Limit *uint64 `xml:"Limit"`
+ MappingBehavior *uint `xml:"MappingBehavior"`
+ Parent *string `xml:"Parent"`
+ PoolID *string `xml:"PoolID"`
+ Reservation *uint64 `xml:"Reservation"`
+ VirtualQuantity *uint `xml:"VirtualQuantity"`
+ VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
+ Weight *uint `xml:"Weight"`
+}
diff --git a/vendor/github.com/vmware/govmomi/ovf/doc.go b/vendor/github.com/vmware/govmomi/ovf/doc.go
new file mode 100644
index 0000000000..6284b1ac58
--- /dev/null
+++ b/vendor/github.com/vmware/govmomi/ovf/doc.go
@@ -0,0 +1,25 @@
+/*
+Copyright (c) 2015 VMware, Inc. All Rights Reserved.
+
+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 ovf provides functionality to unmarshal and inspect the structure
+of an OVF file. It is not a complete implementation of the specification and
+is intended to be used to import virtual infrastructure into vSphere.
+
+For a complete specification of the OVF standard, refer to:
+https://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.0.pdf
+*/
+package ovf
diff --git a/vendor/github.com/vmware/govmomi/ovf/env.go b/vendor/github.com/vmware/govmomi/ovf/env.go
new file mode 100644
index 0000000000..584564ab77
--- /dev/null
+++ b/vendor/github.com/vmware/govmomi/ovf/env.go
@@ -0,0 +1,99 @@
+/*
+Copyright (c) 2015 VMware, Inc. All Rights Reserved.
+
+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 ovf
+
+import (
+ "bytes"
+ "fmt"
+
+ "github.com/vmware/govmomi/vim25/xml"
+)
+
+const (
+ ovfEnvHeader = ``
+ ovfEnvPlatformSection = `
+ %s
+ %s
+ %s
+ %s
+ `
+ ovfEnvPropertyHeader = ``
+ ovfEnvPropertyEntry = ``
+ ovfEnvPropertyFooter = ``
+ ovfEnvFooter = ``
+)
+
+type Env struct {
+ XMLName xml.Name `xml:"http://schemas.dmtf.org/ovf/environment/1 Environment"`
+ ID string `xml:"id,attr"`
+ EsxID string `xml:"http://www.vmware.com/schema/ovfenv esxId,attr"`
+
+ Platform *PlatformSection `xml:"PlatformSection"`
+ Property *PropertySection `xml:"PropertySection"`
+}
+
+type PlatformSection struct {
+ Kind string `xml:"Kind"`
+ Version string `xml:"Version"`
+ Vendor string `xml:"Vendor"`
+ Locale string `xml:"Locale"`
+}
+
+type PropertySection struct {
+ Properties []EnvProperty `xml:"Property"`
+}
+
+type EnvProperty struct {
+ Key string `xml:"key,attr"`
+ Value string `xml:"value,attr"`
+}
+
+// Marshal marshals Env to xml by using xml.Marshal.
+func (e Env) Marshal() (string, error) {
+ x, err := xml.Marshal(e)
+ if err != nil {
+ return "", err
+ }
+
+ return fmt.Sprintf("%s%s", xml.Header, x), nil
+}
+
+// MarshalManual manually marshals Env to xml suitable for a vApp guest.
+// It exists to overcome the lack of expressiveness in Go's XML namespaces.
+func (e Env) MarshalManual() string {
+ var buffer bytes.Buffer
+
+ buffer.WriteString(xml.Header)
+ buffer.WriteString(fmt.Sprintf(ovfEnvHeader, e.EsxID))
+ buffer.WriteString(fmt.Sprintf(ovfEnvPlatformSection, e.Platform.Kind, e.Platform.Version, e.Platform.Vendor, e.Platform.Locale))
+
+ buffer.WriteString(fmt.Sprintf(ovfEnvPropertyHeader))
+ for _, p := range e.Property.Properties {
+ buffer.WriteString(fmt.Sprintf(ovfEnvPropertyEntry, p.Key, p.Value))
+ }
+ buffer.WriteString(fmt.Sprintf(ovfEnvPropertyFooter))
+
+ buffer.WriteString(fmt.Sprintf(ovfEnvFooter))
+
+ return buffer.String()
+}
diff --git a/vendor/github.com/vmware/govmomi/ovf/envelope.go b/vendor/github.com/vmware/govmomi/ovf/envelope.go
new file mode 100644
index 0000000000..d8b6fd8953
--- /dev/null
+++ b/vendor/github.com/vmware/govmomi/ovf/envelope.go
@@ -0,0 +1,191 @@
+/*
+Copyright (c) 2015 VMware, Inc. All Rights Reserved.
+
+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 ovf
+
+type Envelope struct {
+ References []File `xml:"References>File"`
+
+ // Package level meta-data
+ Annotation *AnnotationSection `xml:"AnnotationSection"`
+ Product *ProductSection `xml:"ProductSection"`
+ Network *NetworkSection `xml:"NetworkSection"`
+ Disk *DiskSection `xml:"DiskSection"`
+ OperatingSystem *OperatingSystemSection `xml:"OperatingSystemSection"`
+ Eula *EulaSection `xml:"EulaSection"`
+ VirtualHardware *VirtualHardwareSection `xml:"VirtualHardwareSection"`
+ ResourceAllocation *ResourceAllocationSection `xml:"ResourceAllocationSection"`
+ DeploymentOption *DeploymentOptionSection `xml:"DeploymentOptionSection"`
+
+ // Content: A VirtualSystem or a VirtualSystemCollection
+ VirtualSystem *VirtualSystem `xml:"VirtualSystem"`
+}
+
+type VirtualSystem struct {
+ Content
+
+ Annotation []AnnotationSection `xml:"AnnotationSection"`
+ Product []ProductSection `xml:"ProductSection"`
+ OperatingSystem []OperatingSystemSection `xml:"OperatingSystemSection"`
+ Eula []EulaSection `xml:"EulaSection"`
+ VirtualHardware []VirtualHardwareSection `xml:"VirtualHardwareSection"`
+}
+
+type File struct {
+ ID string `xml:"id,attr"`
+ Href string `xml:"href,attr"`
+ Size uint `xml:"size,attr"`
+ Compression *string `xml:"compression,attr"`
+ ChunkSize *int `xml:"chunkSize,attr"`
+}
+
+type Content struct {
+ ID string `xml:"id,attr"`
+ Info string `xml:"Info"`
+ Name *string `xml:"Name"`
+}
+
+type Section struct {
+ Required *bool `xml:"required,attr"`
+ Info string `xml:"Info"`
+}
+
+type AnnotationSection struct {
+ Section
+
+ Annotation string `xml:"Annotation"`
+}
+
+type ProductSection struct {
+ Section
+
+ Class *string `xml:"class,attr"`
+ Instance *string `xml:"instance,attr"`
+
+ Product string `xml:"Product"`
+ Vendor string `xml:"Vendor"`
+ Version string `xml:"Version"`
+ FullVersion string `xml:"FullVersion"`
+ ProductURL string `xml:"ProductUrl"`
+ VendorURL string `xml:"VendorUrl"`
+ AppURL string `xml:"AppUrl"`
+ Property []Property `xml:"Property"`
+}
+
+type Property struct {
+ Key string `xml:"key,attr"`
+ Type string `xml:"type,attr"`
+ Qualifiers *string `xml:"qualifiers,attr"`
+ UserConfigurable *bool `xml:"userConfigurable,attr"`
+ Default *string `xml:"value,attr"`
+ Password *bool `xml:"password,attr"`
+
+ Label *string `xml:"Label"`
+ Description *string `xml:"Description"`
+
+ Values []PropertyConfigurationValue `xml:"Value"`
+}
+
+type PropertyConfigurationValue struct {
+ Value string `xml:"value,attr"`
+ Configuration *string `xml:"configuration,attr"`
+}
+
+type NetworkSection struct {
+ Section
+
+ Networks []Network `xml:"Network"`
+}
+
+type Network struct {
+ Name string `xml:"name,attr"`
+
+ Description string `xml:"Description"`
+}
+
+type DiskSection struct {
+ Section
+
+ Disks []VirtualDiskDesc `xml:"Disk"`
+}
+
+type VirtualDiskDesc struct {
+ DiskID string `xml:"diskId,attr"`
+ FileRef *string `xml:"fileRef,attr"`
+ Capacity string `xml:"capacity,attr"`
+ CapacityAllocationUnits *string `xml:"capacityAllocationUnits,attr"`
+ Format *string `xml:"format,attr"`
+ PopulatedSize *int `xml:"populatedSize,attr"`
+ ParentRef *string `xml:"parentRef,attr"`
+}
+
+type OperatingSystemSection struct {
+ Section
+
+ ID int16 `xml:"id,attr"`
+ Version *string `xml:"version,attr"`
+ OSType *string `xml:"osType,attr"`
+
+ Description *string `xml:"Description"`
+}
+
+type EulaSection struct {
+ Section
+
+ License string `xml:"License"`
+}
+
+type VirtualHardwareSection struct {
+ Section
+
+ ID *string `xml:"id,attr"`
+ Transport *string `xml:"transport,attr"`
+
+ System *VirtualSystemSettingData `xml:"System"`
+ Item []ResourceAllocationSettingData `xml:"Item"`
+}
+
+type VirtualSystemSettingData struct {
+ CIMVirtualSystemSettingData
+}
+
+type ResourceAllocationSettingData struct {
+ CIMResourceAllocationSettingData
+
+ Required *bool `xml:"required,attr"`
+ Configuration *string `xml:"configuration,attr"`
+ Bound *string `xml:"bound,attr"`
+}
+
+type ResourceAllocationSection struct {
+ Section
+
+ Item []ResourceAllocationSettingData `xml:"Item"`
+}
+
+type DeploymentOptionSection struct {
+ Section
+
+ Configuration []DeploymentOptionConfiguration `xml:"Configuration"`
+}
+
+type DeploymentOptionConfiguration struct {
+ ID string `xml:"id,attr"`
+ Default *bool `xml:"default,attr"`
+
+ Label string `xml:"Label"`
+ Description string `xml:"Description"`
+}
diff --git a/vendor/github.com/vmware/govmomi/ovf/manager.go b/vendor/github.com/vmware/govmomi/ovf/manager.go
new file mode 100644
index 0000000000..3ee2afdd46
--- /dev/null
+++ b/vendor/github.com/vmware/govmomi/ovf/manager.go
@@ -0,0 +1,103 @@
+/*
+Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
+
+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 ovf
+
+import (
+ "context"
+
+ "github.com/vmware/govmomi/vim25"
+ "github.com/vmware/govmomi/vim25/methods"
+ "github.com/vmware/govmomi/vim25/mo"
+ "github.com/vmware/govmomi/vim25/types"
+)
+
+type Manager struct {
+ types.ManagedObjectReference
+
+ c *vim25.Client
+}
+
+func NewManager(c *vim25.Client) *Manager {
+ return &Manager{*c.ServiceContent.OvfManager, c}
+}
+
+// CreateDescriptor wraps methods.CreateDescriptor
+func (m *Manager) CreateDescriptor(ctx context.Context, obj mo.Reference, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
+ req := types.CreateDescriptor{
+ This: m.Reference(),
+ Obj: obj.Reference(),
+ Cdp: cdp,
+ }
+
+ res, err := methods.CreateDescriptor(ctx, m.c, &req)
+ if err != nil {
+ return nil, err
+ }
+
+ return &res.Returnval, nil
+}
+
+// CreateImportSpec wraps methods.CreateImportSpec
+func (m *Manager) CreateImportSpec(ctx context.Context, ovfDescriptor string, resourcePool mo.Reference, datastore mo.Reference, cisp types.OvfCreateImportSpecParams) (*types.OvfCreateImportSpecResult, error) {
+ req := types.CreateImportSpec{
+ This: m.Reference(),
+ OvfDescriptor: ovfDescriptor,
+ ResourcePool: resourcePool.Reference(),
+ Datastore: datastore.Reference(),
+ Cisp: cisp,
+ }
+
+ res, err := methods.CreateImportSpec(ctx, m.c, &req)
+ if err != nil {
+ return nil, err
+ }
+
+ return &res.Returnval, nil
+}
+
+// ParseDescriptor wraps methods.ParseDescriptor
+func (m *Manager) ParseDescriptor(ctx context.Context, ovfDescriptor string, pdp types.OvfParseDescriptorParams) (*types.OvfParseDescriptorResult, error) {
+ req := types.ParseDescriptor{
+ This: m.Reference(),
+ OvfDescriptor: ovfDescriptor,
+ Pdp: pdp,
+ }
+
+ res, err := methods.ParseDescriptor(ctx, m.c, &req)
+ if err != nil {
+ return nil, err
+ }
+
+ return &res.Returnval, nil
+}
+
+// ValidateHost wraps methods.ValidateHost
+func (m *Manager) ValidateHost(ctx context.Context, ovfDescriptor string, host mo.Reference, vhp types.OvfValidateHostParams) (*types.OvfValidateHostResult, error) {
+ req := types.ValidateHost{
+ This: m.Reference(),
+ OvfDescriptor: ovfDescriptor,
+ Host: host.Reference(),
+ Vhp: vhp,
+ }
+
+ res, err := methods.ValidateHost(ctx, m.c, &req)
+ if err != nil {
+ return nil, err
+ }
+
+ return &res.Returnval, nil
+}
diff --git a/vendor/github.com/vmware/govmomi/ovf/ovf.go b/vendor/github.com/vmware/govmomi/ovf/ovf.go
new file mode 100644
index 0000000000..bd279e757d
--- /dev/null
+++ b/vendor/github.com/vmware/govmomi/ovf/ovf.go
@@ -0,0 +1,35 @@
+/*
+Copyright (c) 2015 VMware, Inc. All Rights Reserved.
+
+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 ovf
+
+import (
+ "io"
+
+ "github.com/vmware/govmomi/vim25/xml"
+)
+
+func Unmarshal(r io.Reader) (*Envelope, error) {
+ var e Envelope
+
+ dec := xml.NewDecoder(r)
+ err := dec.Decode(&e)
+ if err != nil {
+ return nil, err
+ }
+
+ return &e, nil
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index ff50bff2ec..eb4ae34ee5 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -507,6 +507,7 @@ github.com/vmihailenco/msgpack/codes
github.com/vmware/govmomi
github.com/vmware/govmomi/nfc
github.com/vmware/govmomi/object
+github.com/vmware/govmomi/ovf
github.com/vmware/govmomi/property
github.com/vmware/govmomi/session
github.com/vmware/govmomi/task