mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 10:46:58 +08:00
Merge pull request #4419 from rainzm/feature/esxi_agent_bb
Feature(esxi-agent): Esxi Agent Go Version
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Envelope xmlns="http://schemas.dmtf.org/ovf/envelope/1"
|
||||
xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
|
||||
xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common"
|
||||
xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
|
||||
xmlns:vmw="http://www.vmware.com/schema/ovf"
|
||||
xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<References>
|
||||
<File ovf:href="{{ .Name }}" ovf:id="file1" ovf:size="{{ .Size }}"/>
|
||||
</References>
|
||||
<DiskSection>
|
||||
<Info>Virtual disk information</Info>
|
||||
<Disk ovf:capacity="{{ .Capacity }}" ovf:capacityAllocationUnits="byte" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="0"/>
|
||||
</DiskSection>
|
||||
<VirtualSystem ovf:id="{{ .ImportName }}">
|
||||
<Info>A virtual machine</Info>
|
||||
<Name>{{ .ImportName }}</Name>
|
||||
<OperatingSystemSection ovf:id="100" vmw:osType="other26xLinux64Guest">
|
||||
<Info>The kind of installed guest operating system</Info>
|
||||
</OperatingSystemSection>
|
||||
<VirtualHardwareSection>
|
||||
<Info>Virtual hardware requirements</Info>
|
||||
<System>
|
||||
<vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
|
||||
<vssd:InstanceID>0</vssd:InstanceID>
|
||||
<vssd:VirtualSystemIdentifier>{{ .ImportName }}</vssd:VirtualSystemIdentifier>
|
||||
<vssd:VirtualSystemType>vmx-07</vssd:VirtualSystemType>
|
||||
</System>
|
||||
<Item>
|
||||
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
|
||||
<rasd:Description>Number of Virtual CPUs</rasd:Description>
|
||||
<rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
|
||||
<rasd:InstanceID>1</rasd:InstanceID>
|
||||
<rasd:ResourceType>3</rasd:ResourceType>
|
||||
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
|
||||
<rasd:Description>Memory Size</rasd:Description>
|
||||
<rasd:ElementName>1024MB of memory</rasd:ElementName>
|
||||
<rasd:InstanceID>2</rasd:InstanceID>
|
||||
<rasd:ResourceType>4</rasd:ResourceType>
|
||||
<rasd:VirtualQuantity>1024</rasd:VirtualQuantity>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:Address>0</rasd:Address>
|
||||
<rasd:Description>SCSI Controller</rasd:Description>
|
||||
<rasd:ElementName>SCSI Controller 0</rasd:ElementName>
|
||||
<rasd:InstanceID>3</rasd:InstanceID>
|
||||
<rasd:ResourceSubType>VirtualSCSI</rasd:ResourceSubType>
|
||||
<rasd:ResourceType>6</rasd:ResourceType>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:AddressOnParent>0</rasd:AddressOnParent>
|
||||
<rasd:ElementName>Hard Disk 1</rasd:ElementName>
|
||||
<rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
|
||||
<rasd:InstanceID>9</rasd:InstanceID>
|
||||
<rasd:Parent>3</rasd:Parent>
|
||||
<rasd:ResourceType>17</rasd:ResourceType>
|
||||
<vmw:Config ovf:required="false" vmw:key="backing.writeThrough" vmw:value="false"/>
|
||||
</Item>
|
||||
</VirtualHardwareSection>
|
||||
</VirtualSystem>
|
||||
</Envelope>
|
||||
@@ -0,0 +1,2 @@
|
||||
DESCRIPTION="Esxi Agent"
|
||||
# SERVICE="yes"
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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/<disk_id>", 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_id>"]
|
||||
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_id>"]
|
||||
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_id>"]
|
||||
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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
)
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 (<null>)
|
||||
// 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)}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+415
-1
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ type SNetInterface struct {
|
||||
Mask net.IPMask
|
||||
Mac string
|
||||
|
||||
// Mtu int
|
||||
Mtu int
|
||||
}
|
||||
|
||||
var SECRET_PREFIX = "169.254"
|
||||
@@ -318,6 +318,8 @@ func (n *SNetInterface) fetchConfig(expectIp string) {
|
||||
return
|
||||
}
|
||||
|
||||
n.Mtu = inter.MTU
|
||||
|
||||
n.Mac = inter.HardwareAddr.String()
|
||||
addrs, err := inter.Addrs()
|
||||
if err == nil {
|
||||
@@ -336,15 +338,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() {
|
||||
|
||||
+78
@@ -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"`
|
||||
}
|
||||
+25
@@ -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
|
||||
+99
@@ -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 = `<Environment
|
||||
xmlns="http://schemas.dmtf.org/ovf/environment/1"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
|
||||
xmlns:ve="http://www.vmware.com/schema/ovfenv"
|
||||
oe:id=""
|
||||
ve:esxId="%s">`
|
||||
ovfEnvPlatformSection = `<PlatformSection>
|
||||
<Kind>%s</Kind>
|
||||
<Version>%s</Version>
|
||||
<Vendor>%s</Vendor>
|
||||
<Locale>%s</Locale>
|
||||
</PlatformSection>`
|
||||
ovfEnvPropertyHeader = `<PropertySection>`
|
||||
ovfEnvPropertyEntry = `<Property oe:key="%s" oe:value="%s"/>`
|
||||
ovfEnvPropertyFooter = `</PropertySection>`
|
||||
ovfEnvFooter = `</Environment>`
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
||||
+191
@@ -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"`
|
||||
}
|
||||
+103
@@ -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
|
||||
}
|
||||
+35
@@ -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
|
||||
}
|
||||
Vendored
+1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user